[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:
parent
986254d25f
commit
f84d519ac1
3
BUILD.gn
3
BUILD.gn
@ -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",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
239
src/builtins/conversion.tq
Normal 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
|
@ -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>(
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user