// Copyright 2016 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. #include "src/builtins/builtins-constructor-gen.h" #include "src/ast/ast.h" #include "src/builtins/builtins-call-gen.h" #include "src/builtins/builtins-constructor.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/codegen/code-factory.h" #include "src/codegen/code-stub-assembler.h" #include "src/codegen/interface-descriptors.h" #include "src/codegen/macro-assembler.h" #include "src/logging/counters.h" #include "src/objects/objects-inl.h" namespace v8 { namespace internal { void Builtins::Generate_ConstructVarargs(MacroAssembler* masm) { Generate_CallOrConstructVarargs(masm, BUILTIN_CODE(masm->isolate(), Construct)); } void Builtins::Generate_ConstructForwardVarargs(MacroAssembler* masm) { Generate_CallOrConstructForwardVarargs( masm, CallOrConstructMode::kConstruct, BUILTIN_CODE(masm->isolate(), Construct)); } void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) { Generate_CallOrConstructForwardVarargs( masm, CallOrConstructMode::kConstruct, BUILTIN_CODE(masm->isolate(), ConstructFunction)); } TF_BUILTIN(ConstructWithArrayLike, CallOrConstructBuiltinsAssembler) { TNode target = CAST(Parameter(Descriptor::kTarget)); SloppyTNode new_target = CAST(Parameter(Descriptor::kNewTarget)); TNode arguments_list = CAST(Parameter(Descriptor::kArgumentsList)); TNode context = CAST(Parameter(Descriptor::kContext)); CallOrConstructWithArrayLike(target, new_target, arguments_list, context); } TF_BUILTIN(ConstructWithSpread, CallOrConstructBuiltinsAssembler) { TNode target = CAST(Parameter(Descriptor::kTarget)); SloppyTNode new_target = CAST(Parameter(Descriptor::kNewTarget)); TNode spread = CAST(Parameter(Descriptor::kSpread)); TNode args_count = UncheckedCast(Parameter(Descriptor::kActualArgumentsCount)); TNode context = CAST(Parameter(Descriptor::kContext)); CallOrConstructWithSpread(target, new_target, spread, args_count, context); } using Node = compiler::Node; TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) { TNode shared_function_info = CAST(Parameter(Descriptor::kSharedFunctionInfo)); TNode feedback_cell = CAST(Parameter(Descriptor::kFeedbackCell)); TNode context = CAST(Parameter(Descriptor::kContext)); IncrementCounter(isolate()->counters()->fast_new_closure_total(), 1); // Bump the closure counter encoded the {feedback_cell}s map. { TNode const feedback_cell_map = LoadMap(feedback_cell); Label no_closures(this), one_closure(this), cell_done(this); GotoIf(IsNoClosuresCellMap(feedback_cell_map), &no_closures); GotoIf(IsOneClosureCellMap(feedback_cell_map), &one_closure); CSA_ASSERT(this, IsManyClosuresCellMap(feedback_cell_map), feedback_cell_map, feedback_cell); Goto(&cell_done); BIND(&no_closures); StoreMapNoWriteBarrier(feedback_cell, RootIndex::kOneClosureCellMap); Goto(&cell_done); BIND(&one_closure); StoreMapNoWriteBarrier(feedback_cell, RootIndex::kManyClosuresCellMap); Goto(&cell_done); BIND(&cell_done); } // The calculation of |function_map_index| must be in sync with // SharedFunctionInfo::function_map_index(). TNode flags = LoadObjectField( shared_function_info, SharedFunctionInfo::kFlagsOffset); TNode const function_map_index = Signed(IntPtrAdd( DecodeWordFromWord32(flags), IntPtrConstant(Context::FIRST_FUNCTION_MAP_INDEX))); CSA_ASSERT(this, UintPtrLessThanOrEqual( function_map_index, IntPtrConstant(Context::LAST_FUNCTION_MAP_INDEX))); // Get the function map in the current native context and set that // as the map of the allocated object. TNode const native_context = LoadNativeContext(context); TNode const function_map = CAST(LoadContextElement(native_context, function_map_index)); // Create a new closure from the given function info in new space TNode instance_size_in_bytes = TimesTaggedSize(LoadMapInstanceSizeInWords(function_map)); TNode result = Allocate(instance_size_in_bytes); StoreMapNoWriteBarrier(result, function_map); InitializeJSObjectBodyNoSlackTracking(result, function_map, instance_size_in_bytes, JSFunction::kSizeWithoutPrototype); // Initialize the rest of the function. StoreObjectFieldRoot(result, JSObject::kPropertiesOrHashOffset, RootIndex::kEmptyFixedArray); StoreObjectFieldRoot(result, JSObject::kElementsOffset, RootIndex::kEmptyFixedArray); { // Set function prototype if necessary. Label done(this), init_prototype(this); Branch(IsFunctionWithPrototypeSlotMap(function_map), &init_prototype, &done); BIND(&init_prototype); StoreObjectFieldRoot(result, JSFunction::kPrototypeOrInitialMapOffset, RootIndex::kTheHoleValue); Goto(&done); BIND(&done); } STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize); StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackCellOffset, feedback_cell); StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset, shared_function_info); StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context); Handle lazy_builtin_handle = isolate()->builtins()->builtin_handle(Builtins::kCompileLazy); TNode lazy_builtin = HeapConstant(lazy_builtin_handle); StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeOffset, lazy_builtin); Return(result); } TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) { TNode context = CAST(Parameter(Descriptor::kContext)); TNode target = CAST(Parameter(Descriptor::kTarget)); TNode new_target = CAST(Parameter(Descriptor::kNewTarget)); Label call_runtime(this); TNode result = EmitFastNewObject(context, target, new_target, &call_runtime); Return(result); BIND(&call_runtime); TailCallRuntime(Runtime::kNewObject, context, target, new_target); } TNode ConstructorBuiltinsAssembler::EmitFastNewObject( SloppyTNode context, SloppyTNode target, SloppyTNode new_target) { TVARIABLE(JSObject, var_obj); Label call_runtime(this), end(this); var_obj = EmitFastNewObject(context, target, new_target, &call_runtime); Goto(&end); BIND(&call_runtime); var_obj = CAST(CallRuntime(Runtime::kNewObject, context, target, new_target)); Goto(&end); BIND(&end); return var_obj.value(); } TNode ConstructorBuiltinsAssembler::EmitFastNewObject( SloppyTNode context, SloppyTNode target, SloppyTNode new_target, Label* call_runtime) { // Verify that the new target is a JSFunction. Label fast(this), end(this); GotoIf(HasInstanceType(new_target, JS_FUNCTION_TYPE), &fast); Goto(call_runtime); BIND(&fast); // Load the initial map and verify that it's in fact a map. TNode initial_map_or_proto = LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset); GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime); GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE), call_runtime); TNode initial_map = CAST(initial_map_or_proto); // Fall back to runtime if the target differs from the new target's // initial map constructor. TNode new_target_constructor = LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset); GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime); TVARIABLE(HeapObject, properties); Label instantiate_map(this), allocate_properties(this); GotoIf(IsDictionaryMap(initial_map), &allocate_properties); { properties = EmptyFixedArrayConstant(); Goto(&instantiate_map); } BIND(&allocate_properties); { properties = AllocateNameDictionary(NameDictionary::kInitialCapacity); Goto(&instantiate_map); } BIND(&instantiate_map); return AllocateJSObjectFromMap(initial_map, properties.value(), nullptr, kNone, kWithSlackTracking); } TNode ConstructorBuiltinsAssembler::EmitFastNewFunctionContext( TNode scope_info, TNode slots, TNode context, ScopeType scope_type) { TNode slots_intptr = Signed(ChangeUint32ToWord(slots)); TNode size = ElementOffsetFromIndex(slots_intptr, PACKED_ELEMENTS, Context::kTodoHeaderSize); // Create a new closure from the given function info in new space TNode function_context = UncheckedCast(AllocateInNewSpace(size)); RootIndex context_type; switch (scope_type) { case EVAL_SCOPE: context_type = RootIndex::kEvalContextMap; break; case FUNCTION_SCOPE: context_type = RootIndex::kFunctionContextMap; break; default: UNREACHABLE(); } // Set up the header. StoreMapNoWriteBarrier(function_context, context_type); TNode min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS); // TODO(ishell): for now, length also includes MIN_CONTEXT_SLOTS. TNode length = IntPtrAdd(slots_intptr, min_context_slots); StoreObjectFieldNoWriteBarrier(function_context, Context::kLengthOffset, SmiTag(length)); StoreObjectFieldNoWriteBarrier(function_context, Context::kScopeInfoOffset, scope_info); StoreObjectFieldNoWriteBarrier(function_context, Context::kPreviousOffset, context); StoreObjectFieldNoWriteBarrier(function_context, Context::kExtensionOffset, TheHoleConstant()); TNode native_context = LoadNativeContext(context); StoreObjectFieldNoWriteBarrier(function_context, Context::kNativeContextOffset, native_context); // Initialize the varrest of the slots to undefined. TNode undefined = UndefinedConstant(); TNode start_offset = IntPtrConstant(Context::kTodoHeaderSize); CodeStubAssembler::VariableList vars(0, zone()); BuildFastLoop( vars, start_offset, size, [=](TNode offset) { StoreObjectFieldNoWriteBarrier(function_context, offset, undefined); }, kTaggedSize, IndexAdvanceMode::kPost); return function_context; } TF_BUILTIN(FastNewFunctionContextEval, ConstructorBuiltinsAssembler) { TNode scope_info = CAST(Parameter(Descriptor::kScopeInfo)); TNode slots = UncheckedCast(Parameter(Descriptor::kSlots)); TNode context = CAST(Parameter(Descriptor::kContext)); Return(EmitFastNewFunctionContext(scope_info, slots, context, ScopeType::EVAL_SCOPE)); } TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) { TNode scope_info = CAST(Parameter(Descriptor::kScopeInfo)); TNode slots = UncheckedCast(Parameter(Descriptor::kSlots)); TNode context = CAST(Parameter(Descriptor::kContext)); Return(EmitFastNewFunctionContext(scope_info, slots, context, ScopeType::FUNCTION_SCOPE)); } TNode ConstructorBuiltinsAssembler::EmitCreateRegExpLiteral( TNode maybe_feedback_vector, TNode slot, TNode pattern, TNode flags, TNode context) { Label call_runtime(this, Label::kDeferred), end(this); GotoIf(IsUndefined(maybe_feedback_vector), &call_runtime); TVARIABLE(JSRegExp, result); TNode feedback_vector = CAST(maybe_feedback_vector); TNode literal_site = CAST(LoadFeedbackVectorSlot(feedback_vector, slot)); GotoIf(NotHasBoilerplate(literal_site), &call_runtime); { TNode boilerplate = CAST(literal_site); int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kTaggedSize; TNode copy = Allocate(size); for (int offset = 0; offset < size; offset += kTaggedSize) { TNode value = LoadObjectField(boilerplate, offset); StoreObjectFieldNoWriteBarrier(copy, offset, value); } result = CAST(copy); Goto(&end); } BIND(&call_runtime); { result = CAST(CallRuntime(Runtime::kCreateRegExpLiteral, context, maybe_feedback_vector, SmiTag(Signed(slot)), pattern, flags)); Goto(&end); } BIND(&end); return result.value(); } TF_BUILTIN(CreateRegExpLiteral, ConstructorBuiltinsAssembler) { TNode maybe_feedback_vector = CAST(Parameter(Descriptor::kFeedbackVector)); TNode slot = Unsigned(SmiUntag(Parameter(Descriptor::kSlot))); TNode pattern = CAST(Parameter(Descriptor::kPattern)); TNode flags = CAST(Parameter(Descriptor::kFlags)); TNode context = CAST(Parameter(Descriptor::kContext)); TNode result = EmitCreateRegExpLiteral(maybe_feedback_vector, slot, pattern, flags, context); Return(result); } TNode ConstructorBuiltinsAssembler::EmitCreateShallowArrayLiteral( TNode feedback_vector, TNode slot, TNode context, Label* call_runtime, AllocationSiteMode allocation_site_mode) { Label zero_capacity(this), cow_elements(this), fast_elements(this), return_result(this); TNode maybe_allocation_site = CAST(LoadFeedbackVectorSlot(feedback_vector, slot)); GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime); TNode allocation_site = CAST(maybe_allocation_site); TNode boilerplate = CAST(LoadBoilerplate(allocation_site)); ParameterMode mode = OptimalParameterMode(); if (allocation_site_mode == TRACK_ALLOCATION_SITE) { return CloneFastJSArray(context, boilerplate, mode, allocation_site); } else { return CloneFastJSArray(context, boilerplate, mode); } } TF_BUILTIN(CreateShallowArrayLiteral, ConstructorBuiltinsAssembler) { TNode feedback_vector = CAST(Parameter(Descriptor::kFeedbackVector)); TNode slot = Unsigned(SmiUntag(Parameter(Descriptor::kSlot))); TNode constant_elements = CAST(Parameter(Descriptor::kConstantElements)); TNode context = CAST(Parameter(Descriptor::kContext)); Label call_runtime(this, Label::kDeferred); Return(EmitCreateShallowArrayLiteral(feedback_vector, slot, context, &call_runtime, DONT_TRACK_ALLOCATION_SITE)); BIND(&call_runtime); { Comment("call runtime"); int const flags = AggregateLiteral::kDisableMementos | AggregateLiteral::kIsShallow; Return(CallRuntime(Runtime::kCreateArrayLiteral, context, feedback_vector, SmiTag(Signed(slot)), constant_elements, SmiConstant(flags))); } } TNode ConstructorBuiltinsAssembler::EmitCreateEmptyArrayLiteral( TNode feedback_vector, TNode slot, TNode context) { // Array literals always have a valid AllocationSite to properly track // elements transitions. TNode maybe_allocation_site = CAST(LoadFeedbackVectorSlot(feedback_vector, slot)); TVARIABLE(AllocationSite, allocation_site); Label create_empty_array(this), initialize_allocation_site(this, Label::kDeferred), done(this); GotoIf(TaggedIsSmi(maybe_allocation_site), &initialize_allocation_site); { allocation_site = CAST(maybe_allocation_site); Goto(&create_empty_array); } // TODO(cbruni): create the AllocationSite in CSA. BIND(&initialize_allocation_site); { allocation_site = CreateAllocationSiteInFeedbackVector(feedback_vector, slot); Goto(&create_empty_array); } BIND(&create_empty_array); TNode kind = LoadElementsKind(allocation_site.value()); TNode native_context = LoadNativeContext(context); Comment("LoadJSArrayElementsMap"); TNode array_map = LoadJSArrayElementsMap(kind, native_context); TNode zero = SmiConstant(0); Comment("Allocate JSArray"); TNode result = AllocateJSArray(GetInitialFastElementsKind(), array_map, zero, zero, allocation_site.value(), ParameterMode::SMI_PARAMETERS); Goto(&done); BIND(&done); return result; } TF_BUILTIN(CreateEmptyArrayLiteral, ConstructorBuiltinsAssembler) { TNode feedback_vector = CAST(Parameter(Descriptor::kFeedbackVector)); TNode slot = Unsigned(SmiUntag(Parameter(Descriptor::kSlot))); TNode context = CAST(Parameter(Descriptor::kContext)); TNode result = EmitCreateEmptyArrayLiteral(feedback_vector, slot, context); Return(result); } TNode ConstructorBuiltinsAssembler::EmitCreateShallowObjectLiteral( TNode feedback_vector, TNode slot, Label* call_runtime) { TNode maybe_allocation_site = CAST(LoadFeedbackVectorSlot(feedback_vector, slot)); GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime); TNode allocation_site = CAST(maybe_allocation_site); TNode boilerplate = LoadBoilerplate(allocation_site); TNode boilerplate_map = LoadMap(boilerplate); CSA_ASSERT(this, IsJSObjectMap(boilerplate_map)); TVARIABLE(FixedArray, var_properties); { TNode bit_field_3 = LoadMapBitField3(boilerplate_map); GotoIf(IsSetWord32(bit_field_3), call_runtime); // Directly copy over the property store for dict-mode boilerplates. Label if_dictionary(this), if_fast(this), done(this); Branch(IsSetWord32(bit_field_3), &if_dictionary, &if_fast); BIND(&if_dictionary); { Comment("Copy dictionary properties"); var_properties = CopyNameDictionary(CAST(LoadSlowProperties(boilerplate)), call_runtime); // Slow objects have no in-object properties. Goto(&done); } BIND(&if_fast); { // TODO(cbruni): support copying out-of-object properties. TNode boilerplate_properties = LoadFastProperties(boilerplate); GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime); var_properties = EmptyFixedArrayConstant(); Goto(&done); } BIND(&done); } TVARIABLE(FixedArrayBase, var_elements); { // Copy the elements backing store, assuming that it's flat. Label if_empty_fixed_array(this), if_copy_elements(this), done(this); TNode boilerplate_elements = LoadElements(boilerplate); Branch(IsEmptyFixedArray(boilerplate_elements), &if_empty_fixed_array, &if_copy_elements); BIND(&if_empty_fixed_array); var_elements = boilerplate_elements; Goto(&done); BIND(&if_copy_elements); CSA_ASSERT(this, Word32BinaryNot( IsFixedCOWArrayMap(LoadMap(boilerplate_elements)))); ExtractFixedArrayFlags flags; flags |= ExtractFixedArrayFlag::kAllFixedArrays; flags |= ExtractFixedArrayFlag::kNewSpaceAllocationOnly; flags |= ExtractFixedArrayFlag::kDontCopyCOW; var_elements = CloneFixedArray(boilerplate_elements, flags); Goto(&done); BIND(&done); } // Ensure new-space allocation for a fresh JSObject so we can skip write // barriers when copying all object fields. STATIC_ASSERT(JSObject::kMaxInstanceSize < kMaxRegularHeapObjectSize); TNode instance_size = TimesTaggedSize(LoadMapInstanceSizeInWords(boilerplate_map)); TNode allocation_size = instance_size; bool needs_allocation_memento = FLAG_allocation_site_pretenuring; if (needs_allocation_memento) { // Prepare for inner-allocating the AllocationMemento. allocation_size = IntPtrAdd(instance_size, IntPtrConstant(AllocationMemento::kSize)); } TNode copy = UncheckedCast(AllocateInNewSpace(allocation_size)); { Comment("Initialize Literal Copy"); // Initialize Object fields. StoreMapNoWriteBarrier(copy, boilerplate_map); StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOrHashOffset, var_properties.value()); StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset, var_elements.value()); } // Initialize the AllocationMemento before potential GCs due to heap number // allocation when copying the in-object properties. if (needs_allocation_memento) { InitializeAllocationMemento(copy, instance_size, allocation_site); } { // Copy over in-object properties. Label continue_with_write_barrier(this), done_init(this); TVARIABLE(IntPtrT, offset, IntPtrConstant(JSObject::kHeaderSize)); // Heap numbers are only mutable on 32-bit platforms. bool may_use_mutable_heap_numbers = !FLAG_unbox_double_fields; { Comment("Copy in-object properties fast"); Label continue_fast(this, &offset); Branch(IntPtrEqual(offset.value(), instance_size), &done_init, &continue_fast); BIND(&continue_fast); if (may_use_mutable_heap_numbers) { TNode field = LoadObjectField(boilerplate, offset.value()); Label store_field(this); GotoIf(TaggedIsSmi(field), &store_field); // TODO(leszeks): Read the field descriptor to decide if this heap // number is mutable or not. GotoIf(IsHeapNumber(CAST(field)), &continue_with_write_barrier); Goto(&store_field); BIND(&store_field); StoreObjectFieldNoWriteBarrier(copy, offset.value(), field); } else { // Copy fields as raw data. TNode field = LoadObjectField(boilerplate, offset.value()); StoreObjectFieldNoWriteBarrier(copy, offset.value(), field); } offset = IntPtrAdd(offset.value(), IntPtrConstant(kTaggedSize)); Branch(WordNotEqual(offset.value(), instance_size), &continue_fast, &done_init); } if (!may_use_mutable_heap_numbers) { BIND(&done_init); return copy; } // Continue initializing the literal after seeing the first sub-object // potentially causing allocation. In this case we prepare the new literal // by copying all pending fields over from the boilerplate and emit full // write barriers from here on. BIND(&continue_with_write_barrier); { Comment("Copy in-object properties slow"); BuildFastLoop( offset.value(), instance_size, [=](TNode offset) { // TODO(ishell): value decompression is not necessary here. TNode field = LoadObjectField(boilerplate, offset); StoreObjectFieldNoWriteBarrier(copy, offset, field); }, kTaggedSize, IndexAdvanceMode::kPost); Comment("Copy mutable HeapNumber values"); BuildFastLoop( offset.value(), instance_size, [=](TNode offset) { TNode field = LoadObjectField(copy, offset); Label copy_heap_number(this, Label::kDeferred), continue_loop(this); // We only have to clone complex field values. GotoIf(TaggedIsSmi(field), &continue_loop); // TODO(leszeks): Read the field descriptor to decide if this heap // number is mutable or not. Branch(IsHeapNumber(CAST(field)), ©_heap_number, &continue_loop); BIND(©_heap_number); { TNode double_value = LoadHeapNumberValue(CAST(field)); TNode heap_number = AllocateHeapNumberWithValue(double_value); StoreObjectField(copy, offset, heap_number); Goto(&continue_loop); } BIND(&continue_loop); }, kTaggedSize, IndexAdvanceMode::kPost); Goto(&done_init); } BIND(&done_init); } return copy; } TF_BUILTIN(CreateShallowObjectLiteral, ConstructorBuiltinsAssembler) { Label call_runtime(this); TNode feedback_vector = CAST(Parameter(Descriptor::kFeedbackVector)); TNode slot = Unsigned(SmiUntag(Parameter(Descriptor::kSlot))); TNode copy = EmitCreateShallowObjectLiteral(feedback_vector, slot, &call_runtime); Return(copy); BIND(&call_runtime); TNode object_boilerplate_description = CAST(Parameter(Descriptor::kObjectBoilerplateDescription)); TNode flags = CAST(Parameter(Descriptor::kFlags)); TNode context = CAST(Parameter(Descriptor::kContext)); TailCallRuntime(Runtime::kCreateObjectLiteral, context, feedback_vector, SmiTag(Signed(slot)), object_boilerplate_description, flags); } // Used by the CreateEmptyObjectLiteral bytecode and the Object constructor. TNode ConstructorBuiltinsAssembler::EmitCreateEmptyObjectLiteral( TNode context) { TNode native_context = LoadNativeContext(context); TNode object_function = CAST(LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX)); TNode map = LoadObjectField( object_function, JSFunction::kPrototypeOrInitialMapOffset); // Ensure that slack tracking is disabled for the map. STATIC_ASSERT(Map::kNoSlackTracking == 0); CSA_ASSERT( this, IsClearWord32(LoadMapBitField3(map))); TNode empty_fixed_array = EmptyFixedArrayConstant(); TNode result = AllocateJSObjectFromMap(map, empty_fixed_array, empty_fixed_array); return result; } // ES #sec-object-constructor TF_BUILTIN(ObjectConstructor, ConstructorBuiltinsAssembler) { int const kValueArg = 0; TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); CodeStubArguments args(this, argc); TNode context = CAST(Parameter(Descriptor::kContext)); TNode new_target = CAST(Parameter(Descriptor::kJSNewTarget)); TVARIABLE(Object, var_result); Label if_subclass(this, Label::kDeferred), if_notsubclass(this), return_result(this); GotoIf(IsUndefined(new_target), &if_notsubclass); TNode target = CAST(Parameter(Descriptor::kJSTarget)); Branch(TaggedEqual(new_target, target), &if_notsubclass, &if_subclass); BIND(&if_subclass); { var_result = CallBuiltin(Builtins::kFastNewObject, context, target, new_target); Goto(&return_result); } BIND(&if_notsubclass); { Label if_newobject(this, Label::kDeferred), if_toobject(this); TNode value_index = IntPtrConstant(kValueArg); GotoIf(UintPtrGreaterThanOrEqual(value_index, argc), &if_newobject); TNode value = args.AtIndex(value_index); GotoIf(IsNull(value), &if_newobject); Branch(IsUndefined(value), &if_newobject, &if_toobject); BIND(&if_newobject); { var_result = EmitCreateEmptyObjectLiteral(context); Goto(&return_result); } BIND(&if_toobject); { var_result = CallBuiltin(Builtins::kToObject, context, value); Goto(&return_result); } } BIND(&return_result); args.PopAndReturn(var_result.value()); } // ES #sec-number-constructor TF_BUILTIN(NumberConstructor, ConstructorBuiltinsAssembler) { TNode context = CAST(Parameter(Descriptor::kContext)); TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); CodeStubArguments args(this, argc); // 1. If no arguments were passed to this function invocation, let n be +0. TVARIABLE(Number, var_n, SmiConstant(0)); Label if_nloaded(this, &var_n); GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &if_nloaded); // 2. Else, // a. Let prim be ? ToNumeric(value). // b. If Type(prim) is BigInt, let n be the Number value for prim. // c. Otherwise, let n be prim. TNode value = args.AtIndex(0); var_n = ToNumber(context, value, BigIntHandling::kConvertToNumber); Goto(&if_nloaded); BIND(&if_nloaded); { // 3. If NewTarget is undefined, return n. TNode n_value = var_n.value(); TNode new_target = CAST(Parameter(Descriptor::kJSNewTarget)); Label return_n(this), constructnumber(this, Label::kDeferred); Branch(IsUndefined(new_target), &return_n, &constructnumber); BIND(&return_n); { args.PopAndReturn(n_value); } BIND(&constructnumber); { // 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, // "%NumberPrototype%", « [[NumberData]] »). // 5. Set O.[[NumberData]] to n. // 6. Return O. // We are not using Parameter(Descriptor::kJSTarget) and loading the value // from the current frame here in order to reduce register pressure on the // fast path. TNode target = LoadTargetFromFrame(); TNode result = CallBuiltin(Builtins::kFastNewObject, context, target, new_target); StoreObjectField(result, JSPrimitiveWrapper::kValueOffset, n_value); args.PopAndReturn(result); } } } TF_BUILTIN(GenericLazyDeoptContinuation, ConstructorBuiltinsAssembler) { TNode result = CAST(Parameter(Descriptor::kResult)); Return(result); } } // namespace internal } // namespace v8