|  | // 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/code-factory.h" | 
|  | #include "src/code-stub-assembler.h" | 
|  | #include "src/counters.h" | 
|  | #include "src/interface-descriptors.h" | 
|  | #include "src/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<Object> target = CAST(Parameter(Descriptor::kTarget)); | 
|  | SloppyTNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget)); | 
|  | TNode<Object> arguments_list = CAST(Parameter(Descriptor::kArgumentsList)); | 
|  | TNode<Context> context = CAST(Parameter(Descriptor::kContext)); | 
|  | CallOrConstructWithArrayLike(target, new_target, arguments_list, context); | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(ConstructWithSpread, CallOrConstructBuiltinsAssembler) { | 
|  | TNode<Object> target = CAST(Parameter(Descriptor::kTarget)); | 
|  | SloppyTNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget)); | 
|  | TNode<Object> spread = CAST(Parameter(Descriptor::kSpread)); | 
|  | TNode<Int32T> args_count = | 
|  | UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount)); | 
|  | TNode<Context> context = CAST(Parameter(Descriptor::kContext)); | 
|  | CallOrConstructWithSpread(target, new_target, spread, args_count, context); | 
|  | } | 
|  |  | 
|  | typedef compiler::Node Node; | 
|  |  | 
|  | TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) { | 
|  | Node* shared_function_info = Parameter(Descriptor::kSharedFunctionInfo); | 
|  | Node* feedback_cell = Parameter(Descriptor::kFeedbackCell); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  |  | 
|  | CSA_ASSERT(this, IsFeedbackCell(feedback_cell)); | 
|  | CSA_ASSERT(this, IsSharedFunctionInfo(shared_function_info)); | 
|  |  | 
|  | IncrementCounter(isolate()->counters()->fast_new_closure_total(), 1); | 
|  |  | 
|  | // Bump the closure counter encoded the {feedback_cell}s map. | 
|  | { | 
|  | Node* 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, Heap::kOneClosureCellMapRootIndex); | 
|  | Goto(&cell_done); | 
|  |  | 
|  | BIND(&one_closure); | 
|  | StoreMapNoWriteBarrier(feedback_cell, Heap::kManyClosuresCellMapRootIndex); | 
|  | Goto(&cell_done); | 
|  |  | 
|  | BIND(&cell_done); | 
|  | } | 
|  |  | 
|  | // The calculation of |function_map_index| must be in sync with | 
|  | // SharedFunctionInfo::function_map_index(). | 
|  | Node* const flags = | 
|  | LoadObjectField(shared_function_info, SharedFunctionInfo::kFlagsOffset, | 
|  | MachineType::Uint32()); | 
|  | Node* const function_map_index = IntPtrAdd( | 
|  | DecodeWordFromWord32<SharedFunctionInfo::FunctionMapIndexBits>(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. | 
|  | Node* const native_context = LoadNativeContext(context); | 
|  | Node* const function_map = | 
|  | LoadContextElement(native_context, function_map_index); | 
|  |  | 
|  | // Create a new closure from the given function info in new space | 
|  | Node* instance_size_in_bytes = | 
|  | TimesPointerSize(LoadMapInstanceSizeInWords(function_map)); | 
|  | Node* const 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, | 
|  | Heap::kEmptyFixedArrayRootIndex); | 
|  | StoreObjectFieldRoot(result, JSObject::kElementsOffset, | 
|  | Heap::kEmptyFixedArrayRootIndex); | 
|  | { | 
|  | // 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, | 
|  | Heap::kTheHoleValueRootIndex); | 
|  | Goto(&done); | 
|  | BIND(&done); | 
|  | } | 
|  |  | 
|  | STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize); | 
|  | StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackCellOffset, | 
|  | feedback_cell); | 
|  | StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset, | 
|  | shared_function_info); | 
|  | StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context); | 
|  | Handle<Code> lazy_builtin_handle( | 
|  | isolate()->builtins()->builtin(Builtins::kCompileLazy), isolate()); | 
|  | Node* lazy_builtin = HeapConstant(lazy_builtin_handle); | 
|  | StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeOffset, lazy_builtin); | 
|  | Return(result); | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) { | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* target = Parameter(Descriptor::kTarget); | 
|  | Node* new_target = Parameter(Descriptor::kNewTarget); | 
|  |  | 
|  | Label call_runtime(this); | 
|  |  | 
|  | Node* result = EmitFastNewObject(context, target, new_target, &call_runtime); | 
|  | Return(result); | 
|  |  | 
|  | BIND(&call_runtime); | 
|  | TailCallRuntime(Runtime::kNewObject, context, target, new_target); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context, | 
|  | Node* target, | 
|  | Node* new_target) { | 
|  | VARIABLE(var_obj, MachineRepresentation::kTagged); | 
|  | Label call_runtime(this), end(this); | 
|  |  | 
|  | Node* result = EmitFastNewObject(context, target, new_target, &call_runtime); | 
|  | var_obj.Bind(result); | 
|  | Goto(&end); | 
|  |  | 
|  | BIND(&call_runtime); | 
|  | var_obj.Bind(CallRuntime(Runtime::kNewObject, context, target, new_target)); | 
|  | Goto(&end); | 
|  |  | 
|  | BIND(&end); | 
|  | return var_obj.value(); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context, | 
|  | Node* target, | 
|  | Node* new_target, | 
|  | Label* call_runtime) { | 
|  | CSA_ASSERT(this, HasInstanceType(target, JS_FUNCTION_TYPE)); | 
|  | CSA_ASSERT(this, IsJSReceiver(new_target)); | 
|  |  | 
|  | // 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. | 
|  | Node* initial_map = | 
|  | LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset); | 
|  | GotoIf(TaggedIsSmi(initial_map), call_runtime); | 
|  | GotoIf(DoesntHaveInstanceType(initial_map, MAP_TYPE), call_runtime); | 
|  |  | 
|  | // Fall back to runtime if the target differs from the new target's | 
|  | // initial map constructor. | 
|  | Node* new_target_constructor = | 
|  | LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset); | 
|  | GotoIf(WordNotEqual(target, new_target_constructor), call_runtime); | 
|  |  | 
|  | VARIABLE(properties, MachineRepresentation::kTagged); | 
|  |  | 
|  | Label instantiate_map(this), allocate_properties(this); | 
|  | GotoIf(IsDictionaryMap(initial_map), &allocate_properties); | 
|  | { | 
|  | properties.Bind(EmptyFixedArrayConstant()); | 
|  | Goto(&instantiate_map); | 
|  | } | 
|  | BIND(&allocate_properties); | 
|  | { | 
|  | properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity)); | 
|  | Goto(&instantiate_map); | 
|  | } | 
|  |  | 
|  | BIND(&instantiate_map); | 
|  | return AllocateJSObjectFromMap(initial_map, properties.value(), nullptr, | 
|  | kNone, kWithSlackTracking); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitFastNewFunctionContext( | 
|  | Node* scope_info, Node* slots, Node* context, ScopeType scope_type) { | 
|  | slots = ChangeUint32ToWord(slots); | 
|  |  | 
|  | // TODO(ishell): Use CSA::OptimalParameterMode() here. | 
|  | ParameterMode mode = INTPTR_PARAMETERS; | 
|  | Node* min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS); | 
|  | Node* length = IntPtrAdd(slots, min_context_slots); | 
|  | Node* size = GetFixedArrayAllocationSize(length, PACKED_ELEMENTS, mode); | 
|  |  | 
|  | // Create a new closure from the given function info in new space | 
|  | Node* function_context = AllocateInNewSpace(size); | 
|  |  | 
|  | Heap::RootListIndex context_type; | 
|  | switch (scope_type) { | 
|  | case EVAL_SCOPE: | 
|  | context_type = Heap::kEvalContextMapRootIndex; | 
|  | break; | 
|  | case FUNCTION_SCOPE: | 
|  | context_type = Heap::kFunctionContextMapRootIndex; | 
|  | break; | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | StoreMapNoWriteBarrier(function_context, context_type); | 
|  | StoreObjectFieldNoWriteBarrier(function_context, Context::kLengthOffset, | 
|  | SmiTag(length)); | 
|  |  | 
|  | // Set up the fixed slots. | 
|  | StoreFixedArrayElement(function_context, Context::SCOPE_INFO_INDEX, | 
|  | scope_info, SKIP_WRITE_BARRIER); | 
|  | StoreFixedArrayElement(function_context, Context::PREVIOUS_INDEX, context, | 
|  | SKIP_WRITE_BARRIER); | 
|  | StoreFixedArrayElement(function_context, Context::EXTENSION_INDEX, | 
|  | TheHoleConstant(), SKIP_WRITE_BARRIER); | 
|  |  | 
|  | // Copy the native context from the previous context. | 
|  | Node* native_context = LoadNativeContext(context); | 
|  | StoreFixedArrayElement(function_context, Context::NATIVE_CONTEXT_INDEX, | 
|  | native_context, SKIP_WRITE_BARRIER); | 
|  |  | 
|  | // Initialize the rest of the slots to undefined. | 
|  | Node* undefined = UndefinedConstant(); | 
|  | BuildFastFixedArrayForEach( | 
|  | function_context, PACKED_ELEMENTS, min_context_slots, length, | 
|  | [this, undefined](Node* context, Node* offset) { | 
|  | StoreNoWriteBarrier(MachineRepresentation::kTagged, context, offset, | 
|  | undefined); | 
|  | }, | 
|  | mode); | 
|  |  | 
|  | return function_context; | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(FastNewFunctionContextEval, ConstructorBuiltinsAssembler) { | 
|  | Node* scope_info = Parameter(Descriptor::kScopeInfo); | 
|  | Node* slots = Parameter(Descriptor::kSlots); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Return(EmitFastNewFunctionContext(scope_info, slots, context, | 
|  | ScopeType::EVAL_SCOPE)); | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) { | 
|  | Node* scope_info = Parameter(Descriptor::kScopeInfo); | 
|  | Node* slots = Parameter(Descriptor::kSlots); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Return(EmitFastNewFunctionContext(scope_info, slots, context, | 
|  | ScopeType::FUNCTION_SCOPE)); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitCreateRegExpLiteral( | 
|  | Node* feedback_vector, Node* slot, Node* pattern, Node* flags, | 
|  | Node* context) { | 
|  | Label call_runtime(this, Label::kDeferred), end(this); | 
|  |  | 
|  | VARIABLE(result, MachineRepresentation::kTagged); | 
|  | TNode<Object> literal_site = | 
|  | CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS)); | 
|  | GotoIf(NotHasBoilerplate(literal_site), &call_runtime); | 
|  | { | 
|  | Node* boilerplate = literal_site; | 
|  | CSA_ASSERT(this, IsJSRegExp(boilerplate)); | 
|  | int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; | 
|  | Node* copy = Allocate(size); | 
|  | for (int offset = 0; offset < size; offset += kPointerSize) { | 
|  | Node* value = LoadObjectField(boilerplate, offset); | 
|  | StoreObjectFieldNoWriteBarrier(copy, offset, value); | 
|  | } | 
|  | result.Bind(copy); | 
|  | Goto(&end); | 
|  | } | 
|  |  | 
|  | BIND(&call_runtime); | 
|  | { | 
|  | result.Bind(CallRuntime(Runtime::kCreateRegExpLiteral, context, | 
|  | feedback_vector, SmiTag(slot), pattern, flags)); | 
|  | Goto(&end); | 
|  | } | 
|  |  | 
|  | BIND(&end); | 
|  | return result.value(); | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(CreateRegExpLiteral, ConstructorBuiltinsAssembler) { | 
|  | Node* feedback_vector = Parameter(Descriptor::kFeedbackVector); | 
|  | Node* slot = SmiUntag(Parameter(Descriptor::kSlot)); | 
|  | Node* pattern = Parameter(Descriptor::kPattern); | 
|  | Node* flags = Parameter(Descriptor::kFlags); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* result = | 
|  | EmitCreateRegExpLiteral(feedback_vector, slot, pattern, flags, context); | 
|  | Return(result); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitCreateShallowArrayLiteral( | 
|  | Node* feedback_vector, Node* slot, Node* context, Label* call_runtime, | 
|  | AllocationSiteMode allocation_site_mode) { | 
|  | Label zero_capacity(this), cow_elements(this), fast_elements(this), | 
|  | return_result(this); | 
|  | VARIABLE(result, MachineRepresentation::kTagged); | 
|  |  | 
|  | TNode<Object> maybe_allocation_site = | 
|  | CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS)); | 
|  | GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime); | 
|  |  | 
|  | TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site); | 
|  | TNode<JSArray> 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) { | 
|  | Node* feedback_vector = Parameter(Descriptor::kFeedbackVector); | 
|  | Node* slot = SmiUntag(Parameter(Descriptor::kSlot)); | 
|  | Node* constant_elements = Parameter(Descriptor::kConstantElements); | 
|  | Node* context = 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(slot), constant_elements, SmiConstant(flags))); | 
|  | } | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitCreateEmptyArrayLiteral( | 
|  | Node* feedback_vector, Node* slot, Node* context) { | 
|  | // Array literals always have a valid AllocationSite to properly track | 
|  | // elements transitions. | 
|  | TNode<Object> maybe_allocation_site = | 
|  | CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS)); | 
|  | 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, SmiTag(slot)); | 
|  | Goto(&create_empty_array); | 
|  | } | 
|  |  | 
|  | BIND(&create_empty_array); | 
|  | TNode<Int32T> kind = LoadElementsKind(allocation_site.value()); | 
|  | TNode<Context> native_context = LoadNativeContext(context); | 
|  | Comment("LoadJSArrayElementsMap"); | 
|  | Node* array_map = LoadJSArrayElementsMap(kind, native_context); | 
|  | Node* zero = SmiConstant(0); | 
|  | Comment("Allocate JSArray"); | 
|  | Node* result = | 
|  | AllocateJSArray(GetInitialFastElementsKind(), array_map, zero, zero, | 
|  | allocation_site.value(), ParameterMode::SMI_PARAMETERS); | 
|  |  | 
|  | Goto(&done); | 
|  | BIND(&done); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(CreateEmptyArrayLiteral, ConstructorBuiltinsAssembler) { | 
|  | Node* feedback_vector = Parameter(Descriptor::kFeedbackVector); | 
|  | Node* slot = SmiUntag(Parameter(Descriptor::kSlot)); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* result = EmitCreateEmptyArrayLiteral(feedback_vector, slot, context); | 
|  | Return(result); | 
|  | } | 
|  |  | 
|  | Node* ConstructorBuiltinsAssembler::EmitCreateShallowObjectLiteral( | 
|  | Node* feedback_vector, Node* slot, Label* call_runtime) { | 
|  | TNode<Object> maybe_allocation_site = | 
|  | CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS)); | 
|  | GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime); | 
|  |  | 
|  | TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site); | 
|  | TNode<JSObject> boilerplate = LoadBoilerplate(allocation_site); | 
|  | TNode<Map> boilerplate_map = LoadMap(boilerplate); | 
|  | CSA_ASSERT(this, IsJSObjectMap(boilerplate_map)); | 
|  |  | 
|  | VARIABLE(var_properties, MachineRepresentation::kTagged); | 
|  | { | 
|  | Node* bit_field_3 = LoadMapBitField3(boilerplate_map); | 
|  | GotoIf(IsSetWord32<Map::IsDeprecatedBit>(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<Map::IsDictionaryMapBit>(bit_field_3), &if_dictionary, | 
|  | &if_fast); | 
|  | BIND(&if_dictionary); | 
|  | { | 
|  | Comment("Copy dictionary properties"); | 
|  | var_properties.Bind(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. | 
|  | Node* boilerplate_properties = LoadFastProperties(boilerplate); | 
|  | GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime); | 
|  | var_properties.Bind(EmptyFixedArrayConstant()); | 
|  | Goto(&done); | 
|  | } | 
|  | BIND(&done); | 
|  | } | 
|  |  | 
|  | VARIABLE(var_elements, MachineRepresentation::kTagged); | 
|  | { | 
|  | // Copy the elements backing store, assuming that it's flat. | 
|  | Label if_empty_fixed_array(this), if_copy_elements(this), done(this); | 
|  | Node* boilerplate_elements = LoadElements(boilerplate); | 
|  | Branch(IsEmptyFixedArray(boilerplate_elements), &if_empty_fixed_array, | 
|  | &if_copy_elements); | 
|  |  | 
|  | BIND(&if_empty_fixed_array); | 
|  | var_elements.Bind(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.Bind(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); | 
|  | Node* instance_size = | 
|  | TimesPointerSize(LoadMapInstanceSizeInWords(boilerplate_map)); | 
|  | Node* 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)); | 
|  | } | 
|  |  | 
|  | Node* copy = 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); | 
|  | VARIABLE(offset, MachineType::PointerRepresentation(), | 
|  | IntPtrConstant(JSObject::kHeaderSize)); | 
|  | // Mutable heap numbers only occur on 32-bit platforms. | 
|  | bool may_use_mutable_heap_numbers = | 
|  | FLAG_track_double_fields && !FLAG_unbox_double_fields; | 
|  | { | 
|  | Comment("Copy in-object properties fast"); | 
|  | Label continue_fast(this, &offset); | 
|  | Branch(WordEqual(offset.value(), instance_size), &done_init, | 
|  | &continue_fast); | 
|  | BIND(&continue_fast); | 
|  | Node* field = LoadObjectField(boilerplate, offset.value()); | 
|  | if (may_use_mutable_heap_numbers) { | 
|  | Label store_field(this); | 
|  | GotoIf(TaggedIsSmi(field), &store_field); | 
|  | GotoIf(IsMutableHeapNumber(field), &continue_with_write_barrier); | 
|  | Goto(&store_field); | 
|  | BIND(&store_field); | 
|  | } | 
|  | StoreObjectFieldNoWriteBarrier(copy, offset.value(), field); | 
|  | offset.Bind(IntPtrAdd(offset.value(), IntPtrConstant(kPointerSize))); | 
|  | 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, | 
|  | [=](Node* offset) { | 
|  | Node* field = LoadObjectField(boilerplate, offset); | 
|  | StoreObjectFieldNoWriteBarrier(copy, offset, field); | 
|  | }, | 
|  | kPointerSize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); | 
|  | Comment("Copy mutable HeapNumber values"); | 
|  | BuildFastLoop(offset.value(), instance_size, | 
|  | [=](Node* offset) { | 
|  | Node* field = LoadObjectField(copy, offset); | 
|  | Label copy_mutable_heap_number(this, Label::kDeferred), | 
|  | continue_loop(this); | 
|  | // We only have to clone complex field values. | 
|  | GotoIf(TaggedIsSmi(field), &continue_loop); | 
|  | Branch(IsMutableHeapNumber(field), | 
|  | ©_mutable_heap_number, &continue_loop); | 
|  | BIND(©_mutable_heap_number); | 
|  | { | 
|  | Node* double_value = LoadHeapNumberValue(field); | 
|  | Node* mutable_heap_number = | 
|  | AllocateMutableHeapNumberWithValue(double_value); | 
|  | StoreObjectField(copy, offset, mutable_heap_number); | 
|  | Goto(&continue_loop); | 
|  | } | 
|  | BIND(&continue_loop); | 
|  | }, | 
|  | kPointerSize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); | 
|  | Goto(&done_init); | 
|  | } | 
|  | BIND(&done_init); | 
|  | } | 
|  | return copy; | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(CreateShallowObjectLiteral, ConstructorBuiltinsAssembler) { | 
|  | Label call_runtime(this); | 
|  | Node* feedback_vector = Parameter(Descriptor::kFeedbackVector); | 
|  | Node* slot = SmiUntag(Parameter(Descriptor::kSlot)); | 
|  | Node* copy = | 
|  | EmitCreateShallowObjectLiteral(feedback_vector, slot, &call_runtime); | 
|  | Return(copy); | 
|  |  | 
|  | BIND(&call_runtime); | 
|  | Node* object_boilerplate_description = | 
|  | Parameter(Descriptor::kObjectBoilerplateDescription); | 
|  | Node* flags = Parameter(Descriptor::kFlags); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | TailCallRuntime(Runtime::kCreateObjectLiteral, context, feedback_vector, | 
|  | SmiTag(slot), object_boilerplate_description, flags); | 
|  | } | 
|  |  | 
|  | // Used by the CreateEmptyObjectLiteral bytecode and the Object constructor. | 
|  | Node* ConstructorBuiltinsAssembler::EmitCreateEmptyObjectLiteral( | 
|  | Node* context) { | 
|  | Node* native_context = LoadNativeContext(context); | 
|  | Node* object_function = | 
|  | LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX); | 
|  | Node* map = LoadObjectField(object_function, | 
|  | JSFunction::kPrototypeOrInitialMapOffset); | 
|  | CSA_ASSERT(this, IsMap(map)); | 
|  | // Ensure that slack tracking is disabled for the map. | 
|  | STATIC_ASSERT(Map::kNoSlackTracking == 0); | 
|  | CSA_ASSERT( | 
|  | this, IsClearWord32<Map::ConstructionCounterBits>(LoadMapBitField3(map))); | 
|  | Node* empty_fixed_array = EmptyFixedArrayConstant(); | 
|  | Node* result = | 
|  | AllocateJSObjectFromMap(map, empty_fixed_array, empty_fixed_array); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // ES #sec-object-constructor | 
|  | TF_BUILTIN(ObjectConstructor, ConstructorBuiltinsAssembler) { | 
|  | int const kValueArg = 0; | 
|  | Node* argc = | 
|  | ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); | 
|  | CodeStubArguments args(this, argc); | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* new_target = Parameter(Descriptor::kJSNewTarget); | 
|  |  | 
|  | VARIABLE(var_result, MachineRepresentation::kTagged); | 
|  | Label if_subclass(this, Label::kDeferred), if_notsubclass(this), | 
|  | return_result(this); | 
|  | GotoIf(IsUndefined(new_target), &if_notsubclass); | 
|  | TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget)); | 
|  | Branch(WordEqual(new_target, target), &if_notsubclass, &if_subclass); | 
|  |  | 
|  | BIND(&if_subclass); | 
|  | { | 
|  | Node* result = | 
|  | CallBuiltin(Builtins::kFastNewObject, context, target, new_target); | 
|  | var_result.Bind(result); | 
|  | Goto(&return_result); | 
|  | } | 
|  |  | 
|  | BIND(&if_notsubclass); | 
|  | { | 
|  | Label if_newobject(this, Label::kDeferred), if_toobject(this); | 
|  |  | 
|  | Node* value_index = IntPtrConstant(kValueArg); | 
|  | GotoIf(UintPtrGreaterThanOrEqual(value_index, argc), &if_newobject); | 
|  | Node* value = args.AtIndex(value_index); | 
|  | GotoIf(IsNull(value), &if_newobject); | 
|  | Branch(IsUndefined(value), &if_newobject, &if_toobject); | 
|  |  | 
|  | BIND(&if_newobject); | 
|  | { | 
|  | Node* result = EmitCreateEmptyObjectLiteral(context); | 
|  | var_result.Bind(result); | 
|  | Goto(&return_result); | 
|  | } | 
|  |  | 
|  | BIND(&if_toobject); | 
|  | { | 
|  | Node* result = CallBuiltin(Builtins::kToObject, context, value); | 
|  | var_result.Bind(result); | 
|  | Goto(&return_result); | 
|  | } | 
|  | } | 
|  |  | 
|  | BIND(&return_result); | 
|  | args.PopAndReturn(var_result.value()); | 
|  | } | 
|  |  | 
|  | // ES #sec-number-constructor | 
|  | TF_BUILTIN(NumberConstructor, ConstructorBuiltinsAssembler) { | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* argc = | 
|  | ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); | 
|  | CodeStubArguments args(this, argc); | 
|  |  | 
|  | // 1. If no arguments were passed to this function invocation, let n be +0. | 
|  | VARIABLE(var_n, MachineRepresentation::kTagged, SmiConstant(0)); | 
|  | Label if_nloaded(this, &var_n); | 
|  | GotoIf(WordEqual(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. | 
|  | Node* value = args.AtIndex(0); | 
|  | var_n.Bind(ToNumber(context, value, BigIntHandling::kConvertToNumber)); | 
|  | Goto(&if_nloaded); | 
|  |  | 
|  | BIND(&if_nloaded); | 
|  | { | 
|  | // 3. If NewTarget is undefined, return n. | 
|  | Node* n_value = var_n.value(); | 
|  | Node* new_target = 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<JSFunction> target = LoadTargetFromFrame(); | 
|  | Node* result = | 
|  | CallBuiltin(Builtins::kFastNewObject, context, target, new_target); | 
|  | StoreObjectField(result, JSValue::kValueOffset, n_value); | 
|  | args.PopAndReturn(result); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TF_BUILTIN(GenericConstructorLazyDeoptContinuation, | 
|  | ConstructorBuiltinsAssembler) { | 
|  | Node* result = Parameter(Descriptor::kResult); | 
|  | Return(result); | 
|  | } | 
|  |  | 
|  | // https://tc39.github.io/ecma262/#sec-string-constructor | 
|  | TF_BUILTIN(StringConstructor, ConstructorBuiltinsAssembler) { | 
|  | Node* context = Parameter(Descriptor::kContext); | 
|  | Node* argc = | 
|  | ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); | 
|  | CodeStubArguments args(this, argc); | 
|  |  | 
|  | TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget)); | 
|  |  | 
|  | // 1. If no arguments were passed to this function invocation, let s be "". | 
|  | VARIABLE(var_s, MachineRepresentation::kTagged, EmptyStringConstant()); | 
|  | Label if_sloaded(this, &var_s); | 
|  | GotoIf(WordEqual(argc, IntPtrConstant(0)), &if_sloaded); | 
|  |  | 
|  | // 2. Else, | 
|  | //    a. If NewTarget is undefined [...] | 
|  | Node* value = args.AtIndex(0); | 
|  | Label if_tostring(this, &var_s); | 
|  | GotoIfNot(IsUndefined(new_target), &if_tostring); | 
|  |  | 
|  | // 2a. [...] and Type(value) is Symbol, return SymbolDescriptiveString(value). | 
|  | GotoIf(TaggedIsSmi(value), &if_tostring); | 
|  | GotoIfNot(IsSymbol(value), &if_tostring); | 
|  | { | 
|  | Node* result = | 
|  | CallRuntime(Runtime::kSymbolDescriptiveString, context, value); | 
|  | args.PopAndReturn(result); | 
|  | } | 
|  |  | 
|  | // 2b. Let s be ? ToString(value). | 
|  | BIND(&if_tostring); | 
|  | { | 
|  | var_s.Bind(CallBuiltin(Builtins::kToString, context, value)); | 
|  | Goto(&if_sloaded); | 
|  | } | 
|  |  | 
|  | // 3. If NewTarget is undefined, return s. | 
|  | BIND(&if_sloaded); | 
|  | { | 
|  | Node* s_value = var_s.value(); | 
|  | Label return_s(this), constructstring(this, Label::kDeferred); | 
|  | Branch(IsUndefined(new_target), &return_s, &constructstring); | 
|  |  | 
|  | BIND(&return_s); | 
|  | { args.PopAndReturn(s_value); } | 
|  |  | 
|  | BIND(&constructstring); | 
|  | { | 
|  | // 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<JSFunction> target = LoadTargetFromFrame(); | 
|  |  | 
|  | Node* result = | 
|  | CallBuiltin(Builtins::kFastNewObject, context, target, new_target); | 
|  | StoreObjectField(result, JSValue::kValueOffset, s_value); | 
|  | args.PopAndReturn(result); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |