f3d9f71df1
BUG=v8:7308 Change-Id: I04c21ed1919f9bc9c68312c15d1e1229aaba32b5 Reviewed-on: https://chromium-review.googlesource.com/1061013 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#53301}
802 lines
30 KiB
C++
802 lines
30 KiB
C++
// 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) {
|
|
Node* target = Parameter(ConstructWithArrayLikeDescriptor::kTarget);
|
|
Node* new_target = Parameter(ConstructWithArrayLikeDescriptor::kNewTarget);
|
|
Node* arguments_list =
|
|
Parameter(ConstructWithArrayLikeDescriptor::kArgumentsList);
|
|
Node* context = Parameter(ConstructWithArrayLikeDescriptor::kContext);
|
|
CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
|
|
}
|
|
|
|
TF_BUILTIN(ConstructWithSpread, CallOrConstructBuiltinsAssembler) {
|
|
Node* target = Parameter(ConstructWithSpreadDescriptor::kTarget);
|
|
Node* new_target = Parameter(ConstructWithSpreadDescriptor::kNewTarget);
|
|
Node* spread = Parameter(ConstructWithSpreadDescriptor::kSpread);
|
|
Node* args_count = Parameter(ConstructWithSpreadDescriptor::kArgumentsCount);
|
|
Node* context = Parameter(ConstructWithSpreadDescriptor::kContext);
|
|
CallOrConstructWithSpread(target, new_target, spread, args_count, context);
|
|
}
|
|
|
|
typedef compiler::Node Node;
|
|
|
|
Node* ConstructorBuiltinsAssembler::NotHasBoilerplate(Node* literal_site) {
|
|
return TaggedIsSmi(literal_site);
|
|
}
|
|
|
|
Node* ConstructorBuiltinsAssembler::LoadAllocationSiteBoilerplate(Node* site) {
|
|
CSA_ASSERT(this, IsAllocationSite(site));
|
|
return LoadObjectField(site,
|
|
AllocationSite::kTransitionInfoOrBoilerplateOffset);
|
|
}
|
|
|
|
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));
|
|
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(FastNewFunctionContextDescriptor::kScopeInfo);
|
|
Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots);
|
|
Node* context = Parameter(FastNewFunctionContextDescriptor::kContext);
|
|
Return(EmitFastNewFunctionContext(scope_info, slots, context,
|
|
ScopeType::EVAL_SCOPE));
|
|
}
|
|
|
|
TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) {
|
|
Node* scope_info = Parameter(FastNewFunctionContextDescriptor::kScopeInfo);
|
|
Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots);
|
|
Node* context = Parameter(FastNewFunctionContextDescriptor::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> allocation_site =
|
|
CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
|
|
GotoIf(NotHasBoilerplate(allocation_site), call_runtime);
|
|
|
|
Node* boilerplate = LoadAllocationSiteBoilerplate(allocation_site);
|
|
|
|
CSA_ASSERT(this, IsJSArrayMap(LoadMap(boilerplate)));
|
|
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.
|
|
TVARIABLE(Object, allocation_site,
|
|
CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0,
|
|
INTPTR_PARAMETERS)));
|
|
|
|
Label create_empty_array(this),
|
|
initialize_allocation_site(this, Label::kDeferred), done(this);
|
|
Branch(TaggedIsSmi(allocation_site.value()), &initialize_allocation_site,
|
|
&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);
|
|
CSA_ASSERT(this, IsAllocationSite(CAST(allocation_site.value())));
|
|
Node* kind = SmiToInt32(CAST(
|
|
LoadObjectField(CAST(allocation_site.value()),
|
|
AllocationSite::kTransitionInfoOrBoilerplateOffset)));
|
|
CSA_ASSERT(this, IsFastElementsKind(kind));
|
|
Node* 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> allocation_site =
|
|
CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
|
|
GotoIf(NotHasBoilerplate(allocation_site), call_runtime);
|
|
|
|
Node* boilerplate = LoadAllocationSiteBoilerplate(allocation_site);
|
|
Node* 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 =
|
|
AllocateHeapNumberWithValue(double_value, MUTABLE);
|
|
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* boilerplate_description =
|
|
Parameter(Descriptor::kBoilerplateDescription);
|
|
Node* flags = Parameter(Descriptor::kFlags);
|
|
Node* context = Parameter(Descriptor::kContext);
|
|
TailCallRuntime(Runtime::kCreateObjectLiteral, context, feedback_vector,
|
|
SmiTag(slot), 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(BuiltinDescriptor::kArgumentsCount));
|
|
CodeStubArguments args(this, argc);
|
|
Node* context = Parameter(BuiltinDescriptor::kContext);
|
|
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
|
|
|
|
VARIABLE(var_result, MachineRepresentation::kTagged);
|
|
Label if_subclass(this, Label::kDeferred), if_notsubclass(this),
|
|
return_result(this);
|
|
GotoIf(IsUndefined(new_target), &if_notsubclass);
|
|
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
|
MachineType::TaggedPointer());
|
|
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(BuiltinDescriptor::kContext);
|
|
Node* argc =
|
|
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
|
|
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(BuiltinDescriptor::kNewTarget);
|
|
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.
|
|
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
|
MachineType::TaggedPointer());
|
|
Node* result =
|
|
CallBuiltin(Builtins::kFastNewObject, context, target, new_target);
|
|
StoreObjectField(result, JSValue::kValueOffset, n_value);
|
|
args.PopAndReturn(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
// https://tc39.github.io/ecma262/#sec-string-constructor
|
|
TF_BUILTIN(StringConstructor, ConstructorBuiltinsAssembler) {
|
|
Node* context = Parameter(BuiltinDescriptor::kContext);
|
|
Node* argc =
|
|
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
|
|
CodeStubArguments args(this, argc);
|
|
|
|
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
|
|
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
|
MachineType::TaggedPointer());
|
|
|
|
// 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);
|
|
{
|
|
Node* result =
|
|
CallBuiltin(Builtins::kFastNewObject, context, target, new_target);
|
|
StoreObjectField(result, JSValue::kValueOffset, s_value);
|
|
args.PopAndReturn(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|