[torque] Port builtins-conversion-gen to Torque

- Ports all conversions that Torque can handle (without weird linkage.)
- Moves NumberToString to number:: namespace.
- Moves ToStringImpl to string:: namespace.

Bug: v8:9891
Change-Id: I5190c545952e1d9810ca71ae7ff4a807d2d98781
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2205192
Commit-Queue: Bill Budge <bbudge@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67903}
This commit is contained in:
Bill Budge 2020-05-19 07:36:15 -07:00 committed by Commit Bot
parent 986254d25f
commit f84d519ac1
13 changed files with 310 additions and 439 deletions

View File

@ -1030,8 +1030,9 @@ torque_files = [
"src/builtins/bigint.tq",
"src/builtins/boolean.tq",
"src/builtins/builtins-string.tq",
"src/builtins/collections.tq",
"src/builtins/cast.tq",
"src/builtins/collections.tq",
"src/builtins/conversion.tq",
"src/builtins/convert.tq",
"src/builtins/console.tq",
"src/builtins/data-view.tq",

View File

@ -292,11 +292,11 @@ transitioning macro ArrayJoinImpl<T: type>(implicit context: Context)(
next = str;
}
case (num: Number): {
next = NumberToString(num);
next = number::NumberToString(num);
}
case (obj: JSAny): {
if (IsNullOrUndefined(obj)) continue;
next = ToString(context, obj);
next = string::ToString(context, obj);
}
}
}

View File

@ -264,6 +264,7 @@ extern enum MessageTemplate {
kNotGeneric,
kCalledNonCallable,
kCalledOnNullOrUndefined,
kCannotConvertToPrimitive,
kProtoObjectOrNull,
kInvalidOffset,
kInvalidTypedArrayLength,
@ -307,6 +308,7 @@ extern enum MessageTemplate {
kProxyGetPrototypeOfNonExtensible,
kProxySetPrototypeOfNonExtensible,
kProxyDeletePropertyNonExtensible,
kUndefinedOrNullToObject,
kWeakRefsCleanupMustBeCallable,
kWasmTrapUnreachable,
kWasmTrapMemOutOfBounds,
@ -385,6 +387,7 @@ type Boolean = True|False;
type NumberOrUndefined = Number|Undefined;
extern macro DefaultStringConstant(): String;
extern macro EmptyStringConstant(): EmptyString;
extern macro FalseConstant(): False;
extern macro Int32FalseConstant(): bool;
@ -396,10 +399,15 @@ extern macro MessageStringConstant(): String;
extern macro NanConstant(): NaN;
extern macro NameStringConstant(): String;
extern macro NullConstant(): Null;
extern macro NumberStringConstant(): String;
extern macro ReturnStringConstant(): String;
extern macro StringStringConstant(): String;
extern macro TheHoleConstant(): TheHole;
extern macro ToPrimitiveSymbolConstant(): PublicSymbol;
extern macro ToStringStringConstant(): String;
extern macro TrueConstant(): True;
extern macro UndefinedConstant(): Undefined;
extern macro ValueOfStringConstant(): String;
const TheHole: TheHole = TheHoleConstant();
const Null: Null = NullConstant();
@ -580,10 +588,6 @@ extern builtin ToObject(Context, JSAny): JSReceiver;
extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsNullOrUndefined(Object): bool;
extern macro IsString(HeapObject): bool;
transitioning builtin ToString(context: Context, o: JSAny): String {
return ToStringImpl(context, o);
}
extern transitioning runtime ToStringRT(Context, JSAny): String;
extern transitioning builtin NonPrimitiveToPrimitive_String(
Context, JSAny): JSPrimitive;
extern transitioning builtin NonPrimitiveToPrimitive_Default(
@ -1371,7 +1375,6 @@ transitioning macro GetMethod(implicit context: Context)(
MessageTemplate::kPropertyNotFunction, value, symbol, o);
}
extern macro NumberToString(Number): String;
extern macro IsOneByteStringInstanceType(InstanceType): bool;
// After converting an index to an integer, calculate a relative index:
@ -1573,35 +1576,6 @@ transitioning builtin FastCreateDataProperty(implicit context: Context)(
return Undefined;
}
@export
transitioning macro ToStringImpl(context: Context, o: JSAny): String {
let result: JSAny = o;
while (true) {
typeswitch (result) {
case (num: Number): {
return NumberToString(num);
}
case (str: String): {
return str;
}
case (oddball: Oddball): {
return oddball.to_string;
}
case (JSReceiver): {
result = NonPrimitiveToPrimitive_String(context, result);
continue;
}
case (Symbol): {
ThrowTypeError(MessageTemplate::kSymbolToString);
}
case (JSAny): {
return ToStringRT(context, o);
}
}
}
unreachable;
}
macro VerifiedUnreachable(): never {
StaticAssert(false);
unreachable;

View File

@ -12,181 +12,6 @@
namespace v8 {
namespace internal {
class ConversionBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
protected:
void Generate_NonPrimitiveToPrimitive(TNode<Context> context,
TNode<Object> input,
ToPrimitiveHint hint);
void Generate_OrdinaryToPrimitive(TNode<Context> context, TNode<Object> input,
OrdinaryToPrimitiveHint hint);
};
// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
TNode<Context> context, TNode<Object> input, ToPrimitiveHint hint) {
// Lookup the @@toPrimitive property on the {input}.
TNode<Object> exotic_to_prim =
GetProperty(context, input, factory()->to_primitive_symbol());
// Check if {exotic_to_prim} is neither null nor undefined.
Label ordinary_to_primitive(this);
GotoIf(IsNullOrUndefined(exotic_to_prim), &ordinary_to_primitive);
{
// Invoke the {exotic_to_prim} method on the {input} with a string
// representation of the {hint}.
TNode<String> hint_string =
HeapConstant(factory()->ToPrimitiveHintString(hint));
TNode<Object> result = Call(context, exotic_to_prim, input, hint_string);
// Verify that the {result} is actually a primitive.
Label if_resultisprimitive(this),
if_resultisnotprimitive(this, Label::kDeferred);
GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
TNode<Uint16T> result_instance_type = LoadInstanceType(CAST(result));
Branch(IsPrimitiveInstanceType(result_instance_type), &if_resultisprimitive,
&if_resultisnotprimitive);
BIND(&if_resultisprimitive);
{
// Just return the {result}.
Return(result);
}
BIND(&if_resultisnotprimitive);
{
// Somehow the @@toPrimitive method on {input} didn't yield a primitive.
ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
}
}
// Convert using the OrdinaryToPrimitive algorithm instead.
BIND(&ordinary_to_primitive);
{
Callable callable = CodeFactory::OrdinaryToPrimitive(
isolate(), (hint == ToPrimitiveHint::kString)
? OrdinaryToPrimitiveHint::kString
: OrdinaryToPrimitiveHint::kNumber);
TailCallStub(callable, context, input);
}
}
TF_BUILTIN(NonPrimitiveToPrimitive_Default, ConversionBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kDefault);
}
TF_BUILTIN(NonPrimitiveToPrimitive_Number, ConversionBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kNumber);
}
TF_BUILTIN(NonPrimitiveToPrimitive_String, ConversionBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Generate_NonPrimitiveToPrimitive(context, input, ToPrimitiveHint::kString);
}
TF_BUILTIN(StringToNumber, CodeStubAssembler) {
TNode<String> input = CAST(Parameter(Descriptor::kArgument));
Return(StringToNumber(input));
}
TF_BUILTIN(ToName, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
TVARIABLE(Object, var_input, input);
Label loop(this, &var_input);
Goto(&loop);
BIND(&loop);
{
// Load the current {input} value.
TNode<Object> input = var_input.value();
// Dispatch based on the type of the {input.}
Label if_inputisbigint(this), if_inputisname(this), if_inputisnumber(this),
if_inputisoddball(this), if_inputisreceiver(this, Label::kDeferred);
GotoIf(TaggedIsSmi(input), &if_inputisnumber);
TNode<Uint16T> input_instance_type = LoadInstanceType(CAST(input));
STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
GotoIf(IsNameInstanceType(input_instance_type), &if_inputisname);
GotoIf(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver);
GotoIf(IsHeapNumberInstanceType(input_instance_type), &if_inputisnumber);
Branch(IsBigIntInstanceType(input_instance_type), &if_inputisbigint,
&if_inputisoddball);
BIND(&if_inputisbigint);
{
// We don't have a fast-path for BigInt currently, so just
// tail call to the %ToString runtime function here for now.
TailCallRuntime(Runtime::kToStringRT, context, input);
}
BIND(&if_inputisname);
{
// The {input} is already a Name.
Return(input);
}
BIND(&if_inputisnumber);
{
// Convert the String {input} to a Number.
TailCallBuiltin(Builtins::kNumberToString, context, input);
}
BIND(&if_inputisoddball);
{
// Just return the {input}'s string representation.
CSA_ASSERT(this, IsOddballInstanceType(input_instance_type));
Return(LoadObjectField(CAST(input), Oddball::kToStringOffset));
}
BIND(&if_inputisreceiver);
{
// Convert the JSReceiver {input} to a primitive first,
// and then run the loop again with the new {input},
// which is then a primitive value.
var_input = CallBuiltin(Builtins::kNonPrimitiveToPrimitive_String,
context, input);
Goto(&loop);
}
}
}
TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<HeapObject> input = CAST(Parameter(Descriptor::kArgument));
Return(NonNumberToNumber(context, input));
}
TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<HeapObject> input = CAST(Parameter(Descriptor::kArgument));
Return(NonNumberToNumeric(context, input));
}
TF_BUILTIN(ToNumeric, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Return(Select<Numeric>(
IsNumber(input), [=] { return CAST(input); },
[=] { return NonNumberToNumeric(context, CAST(input)); }));
}
// ES6 section 7.1.3 ToNumber ( argument )
TF_BUILTIN(ToNumber, CodeStubAssembler) {
// TODO(solanes, v8:6949): Changing this to a TNode<Context> crashes with the
@ -206,93 +31,6 @@ TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
}
// ES section #sec-tostring-applied-to-the-number-type
TF_BUILTIN(NumberToString, CodeStubAssembler) {
TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
Return(NumberToString(input));
}
// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
TNode<Context> context, TNode<Object> input, OrdinaryToPrimitiveHint hint) {
TVARIABLE(Object, var_result);
Label return_result(this, &var_result);
Handle<String> method_names[2];
switch (hint) {
case OrdinaryToPrimitiveHint::kNumber:
method_names[0] = factory()->valueOf_string();
method_names[1] = factory()->toString_string();
break;
case OrdinaryToPrimitiveHint::kString:
method_names[0] = factory()->toString_string();
method_names[1] = factory()->valueOf_string();
break;
}
for (Handle<String> name : method_names) {
// Lookup the {name} on the {input}.
TNode<Object> method = GetProperty(context, input, name);
// Check if the {method} is callable.
Label if_methodiscallable(this),
if_methodisnotcallable(this, Label::kDeferred);
GotoIf(TaggedIsSmi(method), &if_methodisnotcallable);
TNode<Map> method_map = LoadMap(CAST(method));
Branch(IsCallableMap(method_map), &if_methodiscallable,
&if_methodisnotcallable);
BIND(&if_methodiscallable);
{
// Call the {method} on the {input}.
TNode<Object> result = Call(context, method, input);
var_result = result;
// Return the {result} if it is a primitive.
GotoIf(TaggedIsSmi(result), &return_result);
TNode<Uint16T> result_instance_type = LoadInstanceType(CAST(result));
GotoIf(IsPrimitiveInstanceType(result_instance_type), &return_result);
}
// Just continue with the next {name} if the {method} is not callable.
Goto(&if_methodisnotcallable);
BIND(&if_methodisnotcallable);
}
ThrowTypeError(context, MessageTemplate::kCannotConvertToPrimitive);
BIND(&return_result);
Return(var_result.value());
}
TF_BUILTIN(OrdinaryToPrimitive_Number, ConversionBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Generate_OrdinaryToPrimitive(context, input,
OrdinaryToPrimitiveHint::kNumber);
}
TF_BUILTIN(OrdinaryToPrimitive_String, ConversionBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
Generate_OrdinaryToPrimitive(context, input,
OrdinaryToPrimitiveHint::kString);
}
// ES6 section 7.1.2 ToBoolean ( argument )
TF_BUILTIN(ToBoolean, CodeStubAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kArgument));
Label return_true(this), return_false(this);
BranchIfToBooleanIsTrue(value, &return_true, &return_false);
BIND(&return_true);
Return(TrueConstant());
BIND(&return_false);
Return(FalseConstant());
}
// ES6 section 7.1.2 ToBoolean ( argument )
// Requires parameter on stack so that it can be used as a continuation from a
// LAZY deopt.
@ -309,130 +47,6 @@ TF_BUILTIN(ToBooleanLazyDeoptContinuation, CodeStubAssembler) {
Return(FalseConstant());
}
TF_BUILTIN(ToLength, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// We might need to loop once for ToNumber conversion.
TVARIABLE(Object, var_len, CAST(Parameter(Descriptor::kArgument)));
Label loop(this, &var_len);
Goto(&loop);
BIND(&loop);
{
// Shared entry points.
Label return_len(this), return_two53minus1(this, Label::kDeferred),
return_zero(this, Label::kDeferred);
// Load the current {len} value.
TNode<Object> len = var_len.value();
// Check if {len} is a positive Smi.
GotoIf(TaggedIsPositiveSmi(len), &return_len);
// Check if {len} is a (negative) Smi.
GotoIf(TaggedIsSmi(len), &return_zero);
// Check if {len} is a HeapNumber.
TNode<HeapObject> len_heap_object = CAST(len);
Label if_lenisheapnumber(this),
if_lenisnotheapnumber(this, Label::kDeferred);
Branch(IsHeapNumber(len_heap_object), &if_lenisheapnumber,
&if_lenisnotheapnumber);
BIND(&if_lenisheapnumber);
{
// Load the floating-point value of {len}.
TNode<Float64T> len_value = LoadHeapNumberValue(len_heap_object);
// Check if {len} is not greater than zero.
GotoIfNot(Float64GreaterThan(len_value, Float64Constant(0.0)),
&return_zero);
// Check if {len} is greater than or equal to 2^53-1.
GotoIf(Float64GreaterThanOrEqual(len_value,
Float64Constant(kMaxSafeInteger)),
&return_two53minus1);
// Round the {len} towards -Infinity.
TNode<Float64T> value = Float64Floor(len_value);
TNode<Number> result = ChangeFloat64ToTagged(value);
Return(result);
}
BIND(&if_lenisnotheapnumber);
{
// Need to convert {len} to a Number first.
var_len = CallBuiltin(Builtins::kNonNumberToNumber, context, len);
Goto(&loop);
}
BIND(&return_len);
Return(var_len.value());
BIND(&return_two53minus1);
Return(NumberConstant(kMaxSafeInteger));
BIND(&return_zero);
Return(SmiConstant(0));
}
}
// ES6 section 7.1.13 ToObject (argument)
TF_BUILTIN(ToObject, CodeStubAssembler) {
Label if_smi(this, Label::kDeferred), if_jsreceiver(this),
if_noconstructor(this, Label::kDeferred),
if_wrapjs_primitive_wrapper(this);
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> object = CAST(Parameter(Descriptor::kArgument));
TVARIABLE(IntPtrT, constructor_function_index_var);
GotoIf(TaggedIsSmi(object), &if_smi);
TNode<Map> map = LoadMap(CAST(object));
TNode<Uint16T> instance_type = LoadMapInstanceType(map);
GotoIf(IsJSReceiverInstanceType(instance_type), &if_jsreceiver);
TNode<IntPtrT> constructor_function_index =
LoadMapConstructorFunctionIndex(map);
GotoIf(WordEqual(constructor_function_index,
IntPtrConstant(Map::kNoConstructorFunctionIndex)),
&if_noconstructor);
constructor_function_index_var = constructor_function_index;
Goto(&if_wrapjs_primitive_wrapper);
BIND(&if_smi);
constructor_function_index_var =
IntPtrConstant(Context::NUMBER_FUNCTION_INDEX);
Goto(&if_wrapjs_primitive_wrapper);
BIND(&if_wrapjs_primitive_wrapper);
TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<JSFunction> constructor = CAST(LoadContextElement(
native_context, constructor_function_index_var.value()));
TNode<Map> initial_map = LoadObjectField<Map>(
constructor, JSFunction::kPrototypeOrInitialMapOffset);
TNode<HeapObject> js_primitive_wrapper =
Allocate(JSPrimitiveWrapper::kHeaderSize);
StoreMapNoWriteBarrier(js_primitive_wrapper, initial_map);
StoreObjectFieldRoot(js_primitive_wrapper,
JSPrimitiveWrapper::kPropertiesOrHashOffset,
RootIndex::kEmptyFixedArray);
StoreObjectFieldRoot(js_primitive_wrapper,
JSPrimitiveWrapper::kElementsOffset,
RootIndex::kEmptyFixedArray);
StoreObjectField(js_primitive_wrapper, JSPrimitiveWrapper::kValueOffset,
object);
Return(js_primitive_wrapper);
BIND(&if_noconstructor);
ThrowTypeError(context, MessageTemplate::kUndefinedOrNullToObject,
"ToObject");
BIND(&if_jsreceiver);
Return(object);
}
// ES6 section 12.5.5 typeof operator
TF_BUILTIN(Typeof, CodeStubAssembler) {
TNode<Object> object = CAST(Parameter(Descriptor::kObject));

View File

@ -197,7 +197,7 @@ TF_BUILTIN(DatePrototypeToPrimitive, CodeStubAssembler) {
hint_is_invalid(this, Label::kDeferred);
// Fast cases for internalized strings.
TNode<String> number_string = numberStringConstant();
TNode<String> number_string = NumberStringConstant();
GotoIf(TaggedEqual(hint, number_string), &hint_is_number);
TNode<String> default_string = DefaultStringConstant();
GotoIf(TaggedEqual(hint, default_string), &hint_is_string);

View File

@ -196,22 +196,8 @@ namespace internal {
ASM(HandleDebuggerStatement, ContextOnly) \
\
/* Type conversions */ \
TFC(ToObject, TypeConversion) \
TFC(ToBoolean, TypeConversion) \
TFC(OrdinaryToPrimitive_Number, TypeConversion) \
TFC(OrdinaryToPrimitive_String, TypeConversion) \
TFC(NonPrimitiveToPrimitive_Default, TypeConversion) \
TFC(NonPrimitiveToPrimitive_Number, TypeConversion) \
TFC(NonPrimitiveToPrimitive_String, TypeConversion) \
TFC(StringToNumber, TypeConversion) \
TFC(ToName, TypeConversion) \
TFC(NonNumberToNumber, TypeConversion) \
TFC(NonNumberToNumeric, TypeConversion) \
TFC(ToNumber, TypeConversion) \
TFC(ToNumberConvertBigInt, TypeConversion) \
TFC(ToNumeric, TypeConversion) \
TFC(NumberToString, TypeConversion) \
TFC(ToLength, TypeConversion) \
TFC(Typeof, Typeof) \
TFC(GetSuperConstructor, Typeof) \
TFC(BigIntToI64, BigIntToI64) \

View File

@ -5,6 +5,43 @@
#include 'src/builtins/builtins-string-gen.h'
namespace string {
// TODO(bbudge) Remove the 'RT' suffix on this runtime function.
extern transitioning runtime ToStringRT(Context, JSAny): String;
@export
transitioning macro ToStringImpl(context: Context, o: JSAny): String {
let result: JSAny = o;
while (true) {
typeswitch (result) {
case (num: Number): {
return number::NumberToString(num);
}
case (str: String): {
return str;
}
case (oddball: Oddball): {
return oddball.to_string;
}
case (JSReceiver): {
result = NonPrimitiveToPrimitive_String(context, result);
continue;
}
case (Symbol): {
ThrowTypeError(MessageTemplate::kSymbolToString);
}
case (JSAny): {
return ToStringRT(context, o);
}
}
}
unreachable;
}
transitioning builtin ToString(context: Context, o: JSAny): String {
return ToStringImpl(context, o);
}
extern macro StringBuiltinsAssembler::SubString(
String, uintptr, uintptr): String;

View File

@ -425,6 +425,11 @@ Cast<NumberDictionary>(o: HeapObject): NumberDictionary
goto CastError;
}
Cast<Name>(o: HeapObject): Name
labels CastError {
if (IsName(o)) return %RawDownCast<Name>(o);
goto CastError;
}
Cast<String>(o: HeapObject): String
labels CastError {
return HeapObjectToString(o) otherwise CastError;

239
src/builtins/conversion.tq Normal file
View File

@ -0,0 +1,239 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace runtime {
extern transitioning runtime ToStringRT(Context, BigInt): String;
}
// A namespace to disambiguate calls with the same name as a JS Builtin.
namespace csa {
extern macro StringToNumber(String): Number;
extern macro NonNumberToNumber(implicit context: Context)(JSAnyNotNumber):
Number;
extern macro NonNumberToNumeric(implicit context: Context)(JSAnyNotNumber):
Numeric;
}
extern enum OrdinaryToPrimitiveHint { kString, kNumber }
extern macro OrdinaryToPrimitive(implicit context: Context)(
JSAny, constexpr OrdinaryToPrimitiveHint): JSPrimitive;
namespace conversion {
builtin StringToNumber(implicit context: Context)(input: String): Number {
return csa::StringToNumber(input);
}
builtin NonNumberToNumber(implicit context: Context)(input: JSAnyNotNumber):
Number {
return csa::NonNumberToNumber(input);
}
builtin NonNumberToNumeric(implicit context: Context)(input: JSAnyNotNumber):
Numeric {
return csa::NonNumberToNumeric(input);
}
builtin ToNumeric(implicit context: Context)(input: JSAny): Numeric {
typeswitch (input) {
case (n: Number): {
return n;
}
case (h: JSAnyNotNumber): {
return NonNumberToNumeric(h);
}
}
}
// ES section #sec-tostring-applied-to-the-number-type
builtin NumberToString(implicit context: Context)(input: Number): String {
return number::NumberToString(input);
}
// ES6 section 7.1.2 ToBoolean ( argument )
builtin ToBoolean(implicit context: Context)(input: JSAny): Boolean {
BranchIfToBooleanIsTrue(input) otherwise return TrueConstant(),
return FalseConstant();
}
builtin ToLength(implicit context: Context)(input: JSAny): Number {
// We might need to loop once for ToNumber conversion.
let x: JSAny = input;
while (true) {
typeswitch (x) {
case (s: Smi): {
if (s < 0) return 0;
return s;
}
case (h: HeapNumber): {
let value: float64 = Convert<float64>(h);
// The sense of this test is important for the NaN and -0 cases.
if (!(value > 0)) return 0;
if (value > kMaxSafeInteger) return kMaxSafeInteger;
value = math::Float64Floor(value);
return ChangeFloat64ToTagged(value);
}
case (h: JSAnyNotNumber): {
x = csa::NonNumberToNumber(h);
}
}
}
VerifiedUnreachable();
}
transitioning builtin ToName(implicit context: Context)(input: JSAny): Name {
// We might need to loop once for ToNumber conversion.
let x: JSAny = input;
while (true) {
typeswitch (x) {
case (n: Name): {
return n;
}
case (n: Number): {
return number::NumberToString(n);
}
case (b: BigInt): {
// We don't have a fast-path for BigInt currently, so just
// tail call to the %ToString runtime function here for now.
tail runtime::ToStringRT(context, b);
}
case (o: Oddball): {
return o.to_string;
}
case (o: JSReceiver): {
x = NonPrimitiveToPrimitive_String(o);
}
}
}
VerifiedUnreachable();
}
const kNoConstructorFunctionIndex:
constexpr int31 generates 'Map::kNoConstructorFunctionIndex';
// ES6 section 7.1.13 ToObject (argument)
transitioning builtin ToObject(implicit context: Context)(input: JSAny):
JSReceiver {
try {
typeswitch (input) {
case (Smi): {
goto WrapPrimitive(NativeContextSlot::NUMBER_FUNCTION_INDEX);
}
case (o: JSReceiver): {
return o;
}
case (o: JSAnyNotSmi): {
const index: intptr = Convert<intptr>(
o.map.in_object_properties_start_or_constructor_function_index);
if (index != kNoConstructorFunctionIndex) goto WrapPrimitive(index);
ThrowTypeError(MessageTemplate::kUndefinedOrNullToObject, 'ToObject');
}
}
} label WrapPrimitive(constructorIndex: intptr) {
const nativeContext = LoadNativeContext(context);
const constructor = UnsafeCast<JSFunction>(nativeContext[constructorIndex]);
const map: Map = UnsafeCast<Map>(constructor.prototype_or_initial_map);
const wrapper =
UnsafeCast<JSPrimitiveWrapper>(AllocateFastOrSlowJSObjectFromMap(map));
wrapper.value = input;
return wrapper;
}
}
// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
transitioning macro TryGetExoticToPrimitive(implicit context: Context)(
input: JSAny): JSAny labels OrdinaryToPrimitive {
// Look up the @@toPrimitive property.
const exoticToPrimitive: JSAny =
GetProperty(input, ToPrimitiveSymbolConstant());
if (IsNullOrUndefined(exoticToPrimitive)) goto OrdinaryToPrimitive;
return exoticToPrimitive;
}
transitioning macro CallExoticToPrimitive(implicit context: Context)(
input: JSAny, exoticToPrimitive: JSAny, hint: String): JSPrimitive {
// Invoke the exoticToPrimitive method on the input with a string
// representation of the hint.
const result: JSAny = Call(context, exoticToPrimitive, input, hint);
// Verify that the result is primitive.
typeswitch (result) {
case (o: JSPrimitive): {
return o;
}
case (JSReceiver): {
// Somehow the @@toPrimitive method on input didn't yield a primitive.
ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
}
}
}
transitioning builtin NonPrimitiveToPrimitive_Default(
implicit context: Context)(input: JSReceiver): JSPrimitive {
const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
otherwise return OrdinaryToPrimitive_Number(input);
return CallExoticToPrimitive(
input, exoticToPrimitive, DefaultStringConstant());
}
transitioning builtin NonPrimitiveToPrimitive_Number(implicit context: Context)(
input: JSReceiver): JSPrimitive {
const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
otherwise return OrdinaryToPrimitive_Number(input);
return CallExoticToPrimitive(
input, exoticToPrimitive, NumberStringConstant());
}
transitioning builtin NonPrimitiveToPrimitive_String(implicit context: Context)(
input: JSReceiver): JSPrimitive {
const exoticToPrimitive: JSAny = TryGetExoticToPrimitive(input)
otherwise return OrdinaryToPrimitive_String(input);
return CallExoticToPrimitive(
input, exoticToPrimitive, StringStringConstant());
}
// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
transitioning macro TryToPrimitiveMethod(implicit context: Context)(
input: JSAny, name: String): JSPrimitive labels Continue {
const method: JSAny = GetProperty(input, name);
typeswitch (method) {
case (Callable): {
const value: JSAny = Call(context, method, input);
return Cast<JSPrimitive>(value) otherwise Continue;
}
case (JSAny): {
goto Continue;
}
}
}
transitioning builtin OrdinaryToPrimitive_Number(implicit context: Context)(
input: JSAny): JSPrimitive {
try {
return TryToPrimitiveMethod(input, ValueOfStringConstant())
otherwise String;
} label String {
return TryToPrimitiveMethod(input, ToStringStringConstant())
otherwise Throw;
} label Throw {
ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
}
}
transitioning builtin OrdinaryToPrimitive_String(implicit context: Context)(
input: JSAny): JSPrimitive {
try {
return TryToPrimitiveMethod(input, ToStringStringConstant())
otherwise String;
} label String {
return TryToPrimitiveMethod(input, ValueOfStringConstant()) otherwise Throw;
} label Throw {
ThrowTypeError(MessageTemplate::kCannotConvertToPrimitive);
}
}
} // namespace conversion

View File

@ -16,6 +16,8 @@ extern macro MinusInfinityStringConstant(): String;
const kAsciiZero: constexpr int32 = 48; // '0' (ascii)
const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii)
extern macro NumberToString(Number): String;
transitioning macro ThisNumberValue(implicit context: Context)(
receiver: JSAny, method: constexpr string): Number {
return UnsafeCast<Number>(

View File

@ -7299,6 +7299,12 @@ TNode<Number> CodeStubAssembler::ToLength_Inline(SloppyTNode<Context> context,
[=] { return CAST(CallBuiltin(Builtins::kToLength, context, input)); });
}
TNode<Object> CodeStubAssembler::OrdinaryToPrimitive(
TNode<Context> context, TNode<Object> input, OrdinaryToPrimitiveHint hint) {
Callable callable = CodeFactory::OrdinaryToPrimitive(isolate(), hint);
return CallStub(callable, context, input);
}
TNode<Uint32T> CodeStubAssembler::DecodeWord32(SloppyTNode<Word32T> word32,
uint32_t shift, uint32_t mask) {
DCHECK_EQ((mask >> shift) << shift, mask);

View File

@ -168,7 +168,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(NoClosuresCellMap, no_closures_cell_map, NoClosuresCellMap) \
V(null_to_string, null_to_string, NullToString) \
V(NullValue, null_value, Null) \
V(number_string, number_string, numberString) \
V(number_string, number_string, NumberString) \
V(number_to_string, number_to_string, NumberToString) \
V(Object_string, Object_string, ObjectString) \
V(object_to_string, object_to_string, ObjectToString) \
@ -212,6 +212,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(SymbolMap, symbol_map, SymbolMap) \
V(TheHoleValue, the_hole_value, TheHole) \
V(then_string, then_string, ThenString) \
V(toString_string, toString_string, ToStringString) \
V(to_primitive_symbol, to_primitive_symbol, ToPrimitiveSymbol) \
V(to_string_tag_symbol, to_string_tag_symbol, ToStringTagSymbol) \
V(TransitionArrayMap, transition_array_map, TransitionArrayMap) \
V(TrueValue, true_value, True) \
@ -228,6 +230,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(undefined_to_string, undefined_to_string, UndefinedToString) \
V(UndefinedValue, undefined_value, Undefined) \
V(uninitialized_symbol, uninitialized_symbol, UninitializedSymbol) \
V(valueOf_string, valueOf_string, ValueOfString) \
V(WeakFixedArrayMap, weak_fixed_array_map, WeakFixedArrayMap) \
V(zero_string, zero_string, ZeroString) \
TORQUE_INTERNAL_MAP_CSA_LIST(V)
@ -2847,6 +2850,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> ToLength_Inline(SloppyTNode<Context> context,
SloppyTNode<Object> input);
TNode<Object> OrdinaryToPrimitive(TNode<Context> context, TNode<Object> input,
OrdinaryToPrimitiveHint hint);
// Returns a node that contains a decoded (unsigned!) value of a bit
// field |BitField| in |word32|. Returns result as an uint32 node.
template <typename BitField>

View File

@ -36,6 +36,7 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX,
MATH_RANDOM_CACHE_INDEX,
MATH_RANDOM_INDEX_INDEX,
NUMBER_FUNCTION_INDEX,
PROXY_REVOCABLE_RESULT_MAP_INDEX,
REFLECT_APPLY_INDEX,
REGEXP_FUNCTION_INDEX,