[stubs] Implement TF builtin for Object.create fast paths
BUG= Review-Url: https://chromiumcodereview.appspot.com/2385423005 Cr-Commit-Position: refs/heads/master@{#40429}
This commit is contained in:
parent
3c10b97c34
commit
b7ff0d7136
@ -1129,7 +1129,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
SimpleInstallFunction(object_function, factory->assign_string(),
|
||||
Builtins::kObjectAssign, 2, false);
|
||||
SimpleInstallFunction(object_function, factory->create_string(),
|
||||
Builtins::kObjectCreate, 2, false);
|
||||
Builtins::kObjectCreate, 2, true);
|
||||
SimpleInstallFunction(object_function, "getOwnPropertyDescriptor",
|
||||
Builtins::kObjectGetOwnPropertyDescriptor, 2, false);
|
||||
SimpleInstallFunction(object_function, "getOwnPropertyNames",
|
||||
|
@ -459,57 +459,96 @@ void Builtins::Generate_ObjectProtoToString(CodeStubAssembler* assembler) {
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.2 Object.create ( O [ , Properties ] )
|
||||
// TODO(verwaest): Support the common cases with precached map directly in
|
||||
// an Object.create stub.
|
||||
BUILTIN(ObjectCreate) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> prototype = args.atOrUndefined(isolate, 1);
|
||||
if (!prototype->IsNull(isolate) && !prototype->IsJSReceiver()) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kProtoObjectOrNull, prototype));
|
||||
void Builtins::Generate_ObjectCreate(CodeStubAssembler* a) {
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
|
||||
Node* prototype = a->Parameter(1);
|
||||
Node* properties = a->Parameter(2);
|
||||
Node* context = a->Parameter(3 + 2);
|
||||
|
||||
Label call_runtime(a, Label::kDeferred), prototype_valid(a), no_properties(a);
|
||||
{
|
||||
a->Comment("Argument 1 check: prototype");
|
||||
a->GotoIf(a->WordEqual(prototype, a->NullConstant()), &prototype_valid);
|
||||
a->BranchIfJSReceiver(prototype, &prototype_valid, &call_runtime);
|
||||
}
|
||||
|
||||
// Generate the map with the specified {prototype} based on the Object
|
||||
// function's initial map from the current native context.
|
||||
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
|
||||
// slack tracking for Object.create.
|
||||
Handle<Map> map(isolate->native_context()->object_function()->initial_map(),
|
||||
isolate);
|
||||
if (map->prototype() != *prototype) {
|
||||
if (prototype->IsNull(isolate)) {
|
||||
map = isolate->object_with_null_prototype_map();
|
||||
} else if (prototype->IsJSObject()) {
|
||||
Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype);
|
||||
if (!js_prototype->map()->is_prototype_map()) {
|
||||
JSObject::OptimizeAsPrototype(js_prototype, FAST_PROTOTYPE);
|
||||
}
|
||||
Handle<PrototypeInfo> info =
|
||||
Map::GetOrCreatePrototypeInfo(js_prototype, isolate);
|
||||
// TODO(verwaest): Use inobject slack tracking for this map.
|
||||
if (info->HasObjectCreateMap()) {
|
||||
map = handle(info->ObjectCreateMap(), isolate);
|
||||
} else {
|
||||
map = Map::CopyInitialMap(map);
|
||||
Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
|
||||
PrototypeInfo::SetObjectCreateMap(info, map);
|
||||
}
|
||||
} else {
|
||||
map = Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE);
|
||||
a->Bind(&prototype_valid);
|
||||
{
|
||||
a->Comment("Argument 2 check: properties");
|
||||
// Check that we have a simple object
|
||||
a->GotoIf(a->TaggedIsSmi(properties), &call_runtime);
|
||||
// Undefined implies no properties.
|
||||
a->GotoIf(a->WordEqual(properties, a->UndefinedConstant()), &no_properties);
|
||||
Node* properties_map = a->LoadMap(properties);
|
||||
a->GotoIf(a->IsSpecialReceiverMap(properties_map), &call_runtime);
|
||||
// Stay on the fast path only if there are no elements.
|
||||
a->GotoUnless(a->WordEqual(a->LoadElements(properties),
|
||||
a->LoadRoot(Heap::kEmptyFixedArrayRootIndex)),
|
||||
&call_runtime);
|
||||
// Jump to the runtime for slow objects.
|
||||
Node* bit_field3 = a->LoadMapBitField3(properties_map);
|
||||
Node* is_fast_map = a->Word32Equal(
|
||||
a->BitFieldDecode<Map::DictionaryMap>(bit_field3), a->Int32Constant(0));
|
||||
a->GotoUnless(is_fast_map, &call_runtime);
|
||||
|
||||
a->Branch(
|
||||
a->WordEqual(
|
||||
a->BitFieldDecodeWord<Map::NumberOfOwnDescriptorsBits>(bit_field3),
|
||||
a->IntPtrConstant(0)),
|
||||
&no_properties, &call_runtime);
|
||||
}
|
||||
|
||||
// Create a new object with the given prototype.
|
||||
a->Bind(&no_properties);
|
||||
{
|
||||
Variable map(a, MachineRepresentation::kTagged);
|
||||
Label non_null_proto(a), instantiate_map(a), good(a);
|
||||
|
||||
a->Branch(a->WordEqual(prototype, a->NullConstant()), &good,
|
||||
&non_null_proto);
|
||||
|
||||
a->Bind(&good);
|
||||
{
|
||||
map.Bind(a->LoadContextElement(context,
|
||||
Context::OBJECT_WITH_NULL_PROTOTYPE_MAP));
|
||||
a->Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
a->Bind(&non_null_proto);
|
||||
{
|
||||
Node* object_function =
|
||||
a->LoadContextElement(context, Context::OBJECT_FUNCTION_INDEX);
|
||||
Node* object_function_map = a->LoadObjectField(
|
||||
object_function, JSFunction::kPrototypeOrInitialMapOffset);
|
||||
map.Bind(object_function_map);
|
||||
a->GotoIf(a->WordEqual(prototype, a->LoadMapPrototype(map.value())),
|
||||
&instantiate_map);
|
||||
// Try loading the prototype info.
|
||||
Node* prototype_info =
|
||||
a->LoadMapPrototypeInfo(a->LoadMap(prototype), &call_runtime);
|
||||
a->Comment("Load ObjectCreateMap from PrototypeInfo");
|
||||
Node* weak_cell =
|
||||
a->LoadObjectField(prototype_info, PrototypeInfo::kObjectCreateMap);
|
||||
a->GotoIf(a->WordEqual(weak_cell, a->UndefinedConstant()), &call_runtime);
|
||||
map.Bind(a->LoadWeakCellValue(weak_cell, &call_runtime));
|
||||
a->Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
a->Bind(&instantiate_map);
|
||||
{
|
||||
Node* instance = a->AllocateJSObjectFromMap(map.value());
|
||||
a->Return(instance);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually allocate the object.
|
||||
Handle<JSObject> object = isolate->factory()->NewJSObjectFromMap(map);
|
||||
|
||||
// Define the properties if properties was specified and is not undefined.
|
||||
Handle<Object> properties = args.atOrUndefined(isolate, 2);
|
||||
if (!properties->IsUndefined(isolate)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSReceiver::DefineProperties(isolate, object, properties));
|
||||
a->Bind(&call_runtime);
|
||||
{
|
||||
a->Return(
|
||||
a->CallRuntime(Runtime::kObjectCreate, context, prototype, properties));
|
||||
}
|
||||
|
||||
return *object;
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.3 Object.defineProperties
|
||||
|
@ -522,7 +522,7 @@ namespace internal {
|
||||
\
|
||||
/* Object */ \
|
||||
CPP(ObjectAssign) \
|
||||
CPP(ObjectCreate) \
|
||||
TFJ(ObjectCreate, 3) \
|
||||
CPP(ObjectDefineGetter) \
|
||||
CPP(ObjectDefineProperties) \
|
||||
CPP(ObjectDefineProperty) \
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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/code-stub-assembler.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/frames-inl.h"
|
||||
@ -525,6 +524,11 @@ Node* CodeStubAssembler::WordIsPositiveSmi(Node* a) {
|
||||
IntPtrConstant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::WordIsWordAligned(Node* word) {
|
||||
return WordEqual(IntPtrConstant(0),
|
||||
WordAnd(word, IntPtrConstant((1 << kPointerSizeLog2) - 1)));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfSimd128Equal(Node* lhs, Node* lhs_map,
|
||||
Node* rhs, Node* rhs_map,
|
||||
Label* if_equal,
|
||||
@ -625,6 +629,24 @@ void CodeStubAssembler::BranchIfPrototypesHaveNoElements(
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfJSReceiver(Node* object, Label* if_true,
|
||||
Label* if_false) {
|
||||
GotoIf(TaggedIsSmi(object), if_false);
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
Branch(Int32GreaterThanOrEqual(LoadInstanceType(object),
|
||||
Int32Constant(FIRST_JS_RECEIVER_TYPE)),
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfJSObject(Node* object, Label* if_true,
|
||||
Label* if_false) {
|
||||
GotoIf(TaggedIsSmi(object), if_false);
|
||||
STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE);
|
||||
Branch(Int32GreaterThanOrEqual(LoadInstanceType(object),
|
||||
Int32Constant(FIRST_JS_OBJECT_TYPE)),
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context,
|
||||
Label* if_true, Label* if_false) {
|
||||
// Bailout if receiver is a Smi.
|
||||
@ -785,6 +807,11 @@ Node* CodeStubAssembler::InnerAllocate(Node* previous, int offset) {
|
||||
return InnerAllocate(previous, IntPtrConstant(offset));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsRegularHeapObjectSize(Node* size) {
|
||||
return UintPtrLessThanOrEqual(size,
|
||||
IntPtrConstant(kMaxRegularHeapObjectSize));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfToBooleanIsTrue(Node* value, Label* if_true,
|
||||
Label* if_false) {
|
||||
Label if_valueissmi(this), if_valueisnotsmi(this), if_valueisstring(this),
|
||||
@ -950,10 +977,14 @@ Node* CodeStubAssembler::LoadInstanceType(Node* object) {
|
||||
return LoadMapInstanceType(LoadMap(object));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::HasInstanceType(Node* object,
|
||||
InstanceType instance_type) {
|
||||
return Word32Equal(LoadInstanceType(object), Int32Constant(instance_type));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::AssertInstanceType(Node* object,
|
||||
InstanceType instance_type) {
|
||||
CSA_ASSERT(
|
||||
Word32Equal(LoadInstanceType(object), Int32Constant(instance_type)));
|
||||
CSA_ASSERT(HasInstanceType(object, instance_type));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadProperties(Node* object) {
|
||||
@ -1005,6 +1036,17 @@ Node* CodeStubAssembler::LoadMapPrototype(Node* map) {
|
||||
return LoadObjectField(map, Map::kPrototypeOffset);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadMapPrototypeInfo(Node* map,
|
||||
Label* if_no_proto_info) {
|
||||
Node* prototype_info =
|
||||
LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
|
||||
GotoIf(TaggedIsSmi(prototype_info), if_no_proto_info);
|
||||
GotoUnless(WordEqual(LoadMap(prototype_info),
|
||||
LoadRoot(Heap::kPrototypeInfoMapRootIndex)),
|
||||
if_no_proto_info);
|
||||
return prototype_info;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadMapInstanceSize(Node* map) {
|
||||
return ChangeUint32ToWord(
|
||||
LoadObjectField(map, Map::kInstanceSizeOffset, MachineType::Uint8()));
|
||||
@ -1050,6 +1092,20 @@ Node* CodeStubAssembler::LoadMapConstructor(Node* map) {
|
||||
return result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsSpecialReceiverMap(Node* map) {
|
||||
Node* bit_field = LoadMapBitField(map);
|
||||
Node* mask = Int32Constant(1 << Map::kHasNamedInterceptor |
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
Assert(Word32Equal(Word32And(bit_field, mask), Int32Constant(0)));
|
||||
return IsSpecialReceiverInstanceType(LoadMapInstanceType(map));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsSpecialReceiverInstanceType(Node* instance_type) {
|
||||
STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE);
|
||||
return Int32LessThanOrEqual(instance_type,
|
||||
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadNameHashField(Node* name) {
|
||||
return LoadObjectField(name, Name::kHashFieldOffset, MachineType::Uint32());
|
||||
}
|
||||
@ -1604,6 +1660,66 @@ Node* CodeStubAssembler::AllocateRegExpResult(Node* context, Node* length,
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateJSObjectFromMap(Node* map, Node* properties,
|
||||
Node* elements) {
|
||||
Node* size =
|
||||
IntPtrMul(LoadMapInstanceSize(map), IntPtrConstant(kPointerSize));
|
||||
CSA_ASSERT(IsRegularHeapObjectSize(size));
|
||||
Node* object = Allocate(size);
|
||||
StoreMapNoWriteBarrier(object, map);
|
||||
InitializeJSObjectFromMap(object, map, size, properties, elements);
|
||||
return object;
|
||||
}
|
||||
|
||||
void CodeStubAssembler::InitializeJSObjectFromMap(Node* object, Node* map,
|
||||
Node* size, Node* properties,
|
||||
Node* elements) {
|
||||
// This helper assumes that the object is in new-space, as guarded by the
|
||||
// check in AllocatedJSObjectFromMap.
|
||||
if (properties == nullptr) {
|
||||
StoreObjectFieldRoot(object, JSObject::kPropertiesOffset,
|
||||
Heap::kEmptyFixedArrayRootIndex);
|
||||
} else {
|
||||
StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset,
|
||||
properties);
|
||||
}
|
||||
if (elements == nullptr) {
|
||||
StoreObjectFieldRoot(object, JSObject::kElementsOffset,
|
||||
Heap::kEmptyFixedArrayRootIndex);
|
||||
} else {
|
||||
StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset, elements);
|
||||
}
|
||||
InitializeJSObjectBody(object, map, size, JSObject::kHeaderSize);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::InitializeJSObjectBody(Node* object, Node* map,
|
||||
Node* size, int start_offset) {
|
||||
// TODO(cbruni): activate in-object slack tracking machinery.
|
||||
Comment("InitializeJSObjectBody");
|
||||
Node* filler = LoadRoot(Heap::kUndefinedValueRootIndex);
|
||||
// Calculate the untagged field addresses.
|
||||
Node* start_address =
|
||||
IntPtrAdd(object, IntPtrConstant(start_offset - kHeapObjectTag));
|
||||
Node* end_address =
|
||||
IntPtrSub(IntPtrAdd(object, size), IntPtrConstant(kHeapObjectTag));
|
||||
StoreFieldsNoWriteBarrier(start_address, end_address, filler);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreFieldsNoWriteBarrier(Node* start_address,
|
||||
Node* end_address,
|
||||
Node* value) {
|
||||
Comment("StoreFieldsNoWriteBarrier");
|
||||
CSA_ASSERT(WordIsWordAligned(start_address));
|
||||
CSA_ASSERT(WordIsWordAligned(end_address));
|
||||
BuildFastLoop(MachineType::PointerRepresentation(), start_address,
|
||||
end_address,
|
||||
[value](CodeStubAssembler* a, Node* current) {
|
||||
a->StoreNoWriteBarrier(MachineType::PointerRepresentation(),
|
||||
current, value);
|
||||
},
|
||||
kPointerSize, IndexAdvanceMode::kPost);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateUninitializedJSArrayWithoutElements(
|
||||
ElementsKind kind, Node* array_map, Node* length, Node* allocation_site) {
|
||||
Comment("begin allocation of JSArray without elements");
|
||||
|
@ -156,6 +156,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* InnerAllocate(compiler::Node* previous, int offset);
|
||||
compiler::Node* InnerAllocate(compiler::Node* previous,
|
||||
compiler::Node* offset);
|
||||
compiler::Node* IsRegularHeapObjectSize(compiler::Node* size);
|
||||
|
||||
void Assert(compiler::Node* condition, const char* string = nullptr,
|
||||
const char* file = nullptr, int line = 0);
|
||||
@ -164,6 +165,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* TaggedIsSmi(compiler::Node* a);
|
||||
// Check that the value is a non-negative smi.
|
||||
compiler::Node* WordIsPositiveSmi(compiler::Node* a);
|
||||
// Check that a word has a word-aligned address.
|
||||
compiler::Node* WordIsWordAligned(compiler::Node* word);
|
||||
|
||||
void BranchIfSmiEqual(compiler::Node* a, compiler::Node* b, Label* if_true,
|
||||
Label* if_false) {
|
||||
@ -199,6 +202,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
if_notequal);
|
||||
}
|
||||
|
||||
void BranchIfJSReceiver(compiler::Node* object, Label* if_true,
|
||||
Label* if_false);
|
||||
void BranchIfJSObject(compiler::Node* object, Label* if_true,
|
||||
Label* if_false);
|
||||
|
||||
void BranchIfFastJSArray(compiler::Node* object, compiler::Node* context,
|
||||
Label* if_true, Label* if_false);
|
||||
|
||||
@ -234,6 +242,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* LoadMap(compiler::Node* object);
|
||||
// Load the instance type of an HeapObject.
|
||||
compiler::Node* LoadInstanceType(compiler::Node* object);
|
||||
// Compare the instance the type of the object against the provided one.
|
||||
compiler::Node* HasInstanceType(compiler::Node* object, InstanceType type);
|
||||
// Checks that given heap object has given instance type.
|
||||
void AssertInstanceType(compiler::Node* object, InstanceType instance_type);
|
||||
// Load the properties backing store of a JSObject.
|
||||
@ -260,6 +270,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* LoadMapDescriptors(compiler::Node* map);
|
||||
// Load the prototype of a map.
|
||||
compiler::Node* LoadMapPrototype(compiler::Node* map);
|
||||
// Load the prototype info of a map. The result has to be checked if it is a
|
||||
// prototype info object or not.
|
||||
compiler::Node* LoadMapPrototypeInfo(compiler::Node* map,
|
||||
Label* if_has_no_proto_info);
|
||||
// Load the instance size of a Map.
|
||||
compiler::Node* LoadMapInstanceSize(compiler::Node* map);
|
||||
// Load the inobject properties count of a Map (valid only for JSObjects).
|
||||
@ -268,6 +282,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* LoadMapConstructorFunctionIndex(compiler::Node* map);
|
||||
// Load the constructor of a Map (equivalent to Map::GetConstructor()).
|
||||
compiler::Node* LoadMapConstructor(compiler::Node* map);
|
||||
// Check whether the map is for an object with special properties, such as a
|
||||
// JSProxy or an object with interceptors.
|
||||
compiler::Node* IsSpecialReceiverMap(compiler::Node* map);
|
||||
compiler::Node* IsSpecialReceiverInstanceType(compiler::Node* instance_type);
|
||||
|
||||
// Load the hash field of a name as an uint32 value.
|
||||
compiler::Node* LoadNameHashField(compiler::Node* name);
|
||||
@ -350,6 +368,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* object, compiler::Node* index, compiler::Node* value,
|
||||
ParameterMode parameter_mode = INTEGER_PARAMETERS);
|
||||
|
||||
void StoreFieldsNoWriteBarrier(compiler::Node* start_address,
|
||||
compiler::Node* end_address,
|
||||
compiler::Node* value);
|
||||
|
||||
// Allocate a HeapNumber without initializing its value.
|
||||
compiler::Node* AllocateHeapNumber(MutableMode mode = IMMUTABLE);
|
||||
// Allocate a HeapNumber with a specific value.
|
||||
@ -409,6 +431,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* index,
|
||||
compiler::Node* input);
|
||||
|
||||
compiler::Node* AllocateJSObjectFromMap(compiler::Node* map,
|
||||
compiler::Node* properties = nullptr,
|
||||
compiler::Node* elements = nullptr);
|
||||
|
||||
void InitializeJSObjectFromMap(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* size,
|
||||
compiler::Node* properties = nullptr,
|
||||
compiler::Node* elements = nullptr);
|
||||
|
||||
void InitializeJSObjectBody(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* size,
|
||||
int start_offset = JSObject::kHeaderSize);
|
||||
|
||||
// Allocate a JSArray without elements and initialize the header fields.
|
||||
compiler::Node* AllocateUninitializedJSArrayWithoutElements(
|
||||
ElementsKind kind, compiler::Node* array_map, compiler::Node* length,
|
||||
|
@ -58,6 +58,7 @@ class RawMachineLabel;
|
||||
V(Uint32LessThanOrEqual) \
|
||||
V(Uint32GreaterThanOrEqual) \
|
||||
V(UintPtrLessThan) \
|
||||
V(UintPtrLessThanOrEqual) \
|
||||
V(UintPtrGreaterThan) \
|
||||
V(UintPtrGreaterThanOrEqual) \
|
||||
V(WordEqual) \
|
||||
|
@ -1206,6 +1206,7 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT
|
||||
os << "\n - prototype users: " << Brief(prototype_users());
|
||||
os << "\n - registry slot: " << registry_slot();
|
||||
os << "\n - validity cell: " << Brief(validity_cell());
|
||||
os << "\n - object create map: " << Brief(object_create_map());
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
|
@ -207,6 +207,59 @@ RUNTIME_FUNCTION(Runtime_ObjectHasOwnProperty) {
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.2 Object.create ( O [ , Properties ] )
|
||||
// TODO(verwaest): Support the common cases with precached map directly in
|
||||
// an Object.create stub.
|
||||
RUNTIME_FUNCTION(Runtime_ObjectCreate) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> prototype = args.at<Object>(0);
|
||||
if (!prototype->IsNull(isolate) && !prototype->IsJSReceiver()) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kProtoObjectOrNull, prototype));
|
||||
}
|
||||
|
||||
// Generate the map with the specified {prototype} based on the Object
|
||||
// function's initial map from the current native context.
|
||||
// TODO(bmeurer): Use a dedicated cache for Object.create; think about
|
||||
// slack tracking for Object.create.
|
||||
Handle<Map> map(isolate->native_context()->object_function()->initial_map(),
|
||||
isolate);
|
||||
if (map->prototype() != *prototype) {
|
||||
if (prototype->IsNull(isolate)) {
|
||||
map = isolate->object_with_null_prototype_map();
|
||||
} else if (prototype->IsJSObject()) {
|
||||
Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype);
|
||||
if (!js_prototype->map()->is_prototype_map()) {
|
||||
JSObject::OptimizeAsPrototype(js_prototype, FAST_PROTOTYPE);
|
||||
}
|
||||
Handle<PrototypeInfo> info =
|
||||
Map::GetOrCreatePrototypeInfo(js_prototype, isolate);
|
||||
// TODO(verwaest): Use inobject slack tracking for this map.
|
||||
if (info->HasObjectCreateMap()) {
|
||||
map = handle(info->ObjectCreateMap(), isolate);
|
||||
} else {
|
||||
map = Map::CopyInitialMap(map);
|
||||
Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
|
||||
PrototypeInfo::SetObjectCreateMap(info, map);
|
||||
}
|
||||
} else {
|
||||
map = Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually allocate the object.
|
||||
Handle<JSObject> object = isolate->factory()->NewJSObjectFromMap(map);
|
||||
|
||||
// Define the properties if properties was specified and is not undefined.
|
||||
Handle<Object> properties = args.at<Object>(1);
|
||||
if (!properties->IsUndefined(isolate)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSReceiver::DefineProperties(isolate, object, properties));
|
||||
}
|
||||
|
||||
return *object;
|
||||
}
|
||||
|
||||
MaybeHandle<Object> Runtime::SetObjectProperty(Isolate* isolate,
|
||||
Handle<Object> object,
|
||||
Handle<Object> key,
|
||||
|
@ -378,6 +378,7 @@ namespace internal {
|
||||
#define FOR_EACH_INTRINSIC_OBJECT(F) \
|
||||
F(GetPrototype, 1, 1) \
|
||||
F(ObjectHasOwnProperty, 2, 1) \
|
||||
F(ObjectCreate, 2, 1) \
|
||||
F(InternalSetPrototype, 2, 1) \
|
||||
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
|
||||
F(GetProperty, 2, 1) \
|
||||
|
@ -1509,5 +1509,67 @@ TEST(GotoIfExceptionMultiple) {
|
||||
CHECK(constructor->SameValue(*isolate->type_error_function()));
|
||||
}
|
||||
|
||||
TEST(AllocateJSObjectFromMap) {
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
const int kNumParams = 3;
|
||||
CodeStubAssemblerTester m(isolate, kNumParams);
|
||||
|
||||
{
|
||||
Node* map = m.Parameter(0);
|
||||
Node* properties = m.Parameter(1);
|
||||
Node* elements = m.Parameter(2);
|
||||
|
||||
Node* result = m.AllocateJSObjectFromMap(map, properties, elements);
|
||||
|
||||
m.Return(result);
|
||||
}
|
||||
|
||||
Handle<Code> code = m.GenerateCode();
|
||||
FunctionTester ft(code, kNumParams);
|
||||
|
||||
Handle<Map> maps[] = {
|
||||
isolate->object_with_null_prototype_map(),
|
||||
handle(isolate->object_function()->initial_map(), isolate),
|
||||
handle(isolate->array_function()->initial_map(), isolate),
|
||||
};
|
||||
|
||||
#define VERIFY(result, map_value, properties_value, elements_value) \
|
||||
CHECK_EQ(result->map(), map_value); \
|
||||
CHECK_EQ(result->properties(), properties_value); \
|
||||
CHECK_EQ(result->elements(), elements_value);
|
||||
|
||||
{
|
||||
Handle<Object> empty_fixed_array = factory->empty_fixed_array();
|
||||
for (int i = 0; i < arraysize(maps); i++) {
|
||||
Handle<Map> map = maps[i];
|
||||
Handle<JSObject> result = Handle<JSObject>::cast(
|
||||
ft.Call(map, empty_fixed_array, empty_fixed_array).ToHandleChecked());
|
||||
VERIFY(result, *map, *empty_fixed_array, *empty_fixed_array);
|
||||
#ifdef VERIFY_HEAP
|
||||
isolate->heap()->Verify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// TODO(cbruni): handle in-object properties
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(
|
||||
v8::Utils::OpenHandle(*CompileRun("var object = {a:1,b:2, 1:1, 2:2}; "
|
||||
"object")));
|
||||
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
||||
"Normalize");
|
||||
Handle<JSObject> result = Handle<JSObject>::cast(
|
||||
ft.Call(handle(object->map()), handle(object->properties()),
|
||||
handle(object->elements()))
|
||||
.ToHandleChecked());
|
||||
VERIFY(result, object->map(), object->properties(), object->elements());
|
||||
#ifdef VERIFY_HEAP
|
||||
isolate->heap()->Verify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -248,3 +248,14 @@ for (x in sonOfTricky) {
|
||||
sum += sonOfTricky[x];
|
||||
}
|
||||
assertEquals(16, sum);
|
||||
|
||||
|
||||
(function createWithEmptyProtoInfoCreateMap() {
|
||||
var proto = {a:1};
|
||||
var instance = {__proto__: proto };
|
||||
// Try force creation of prototype info on proto by loading a proto property.
|
||||
assertEquals(instance.a, 1);
|
||||
var result = Object.create(proto, {});
|
||||
assertEquals(result.a, 1);
|
||||
assertEquals(result.__proto__, proto);
|
||||
})()
|
||||
|
Loading…
Reference in New Issue
Block a user