[es6] Better support for built-ins subclassing.

Create proper initial map for original constructor (new.target) instead of doing prototype transition on the base constructor's initial map. This approach fixes in-object slack tracking for subclass instances.
This CL also fixes subclassing from String.

BUG=v8:3101, v8:3330
LOG=Y

Review URL: https://codereview.chromium.org/1427483002

Cr-Commit-Position: refs/heads/master@{#31680}
This commit is contained in:
ishell 2015-10-30 03:58:04 -07:00 committed by Commit bot
parent 8a2618a986
commit cd5f48302a
14 changed files with 784 additions and 124 deletions

View File

@ -213,39 +213,42 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : number of arguments
// -- r1 : constructor function
// -- r3 : original constructor
// -- lr : return address
// -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
// -- sp[argc * 4] : receiver
// -----------------------------------
// 1. Load the first argument into r0 and get rid of the rest (including the
// 1. Load the first argument into r2 and get rid of the rest (including the
// receiver).
{
Label no_arguments, done;
__ sub(r0, r0, Operand(1), SetCC);
__ b(lo, &no_arguments);
__ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex));
__ ldr(r2, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex));
__ Drop(2);
__ b(&done);
__ bind(&no_arguments);
__ LoadRoot(r0, Heap::kempty_stringRootIndex);
__ LoadRoot(r2, Heap::kempty_stringRootIndex);
__ Drop(1);
__ bind(&done);
}
// 2. Make sure r0 is a string.
// 2. Make sure r2 is a string.
{
Label convert, done_convert;
__ JumpIfSmi(r0, &convert);
__ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
__ JumpIfSmi(r2, &convert);
__ CompareObjectType(r2, r4, r4, FIRST_NONSTRING_TYPE);
__ b(lo, &done_convert);
__ bind(&convert);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(r1);
__ Push(r1, r3);
__ Move(r0, r2);
__ CallStub(&stub);
__ Pop(r1);
__ Move(r2, r0);
__ Pop(r1, r3);
}
__ bind(&done_convert);
}
@ -253,13 +256,18 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// 3. Allocate a JSValue wrapper for the string.
{
// ----------- S t a t e -------------
// -- r0 : the first argument
// -- r2 : the first argument
// -- r1 : constructor function
// -- r3 : original constructor
// -- lr : return address
// -----------------------------------
Label allocate, done_allocate;
__ Move(r2, r0);
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and function differ.
__ cmp(r1, r3);
__ b(ne, &rt_call);
__ Allocate(JSValue::kSize, r0, r3, r4, &allocate, TAG_OBJECT);
__ bind(&done_allocate);
@ -283,6 +291,18 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(r1, r2);
}
__ b(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(r1, r2);
__ Push(r1, r3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(r1, r2);
}
__ str(r2, FieldMemOperand(r0, JSValue::kValueOffset));
__ Ret();
}
}
@ -500,8 +520,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// r3: original constructor
__ bind(&rt_call);
__ push(r1); // argument 2/1: constructor function
__ push(r3); // argument 3/2: original constructor
__ push(r1); // constructor function
__ push(r3); // original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ mov(r4, r0);

View File

@ -209,6 +209,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x0 : number of arguments
// -- x1 : constructor function
// -- x3 : original constructor
// -- lr : return address
// -- sp[(argc - n - 1) * 8] : arg[n] (zero based)
// -- sp[argc * 8] : receiver
@ -234,16 +235,16 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
Label convert, done_convert;
__ JumpIfSmi(x2, &convert);
__ JumpIfObjectType(x2, x3, x3, FIRST_NONSTRING_TYPE, &done_convert, lo);
__ JumpIfObjectType(x2, x4, x4, FIRST_NONSTRING_TYPE, &done_convert, lo);
__ Bind(&convert);
{
FrameScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(x1);
__ Push(x1, x3);
__ Move(x0, x2);
__ CallStub(&stub);
__ Move(x2, x0);
__ Pop(x1);
__ Pop(x1, x3);
}
__ Bind(&done_convert);
}
@ -251,12 +252,18 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// 3. Allocate a JSValue wrapper for the string.
{
// ----------- S t a t e -------------
// -- x1 : constructor function
// -- x2 : the first argument
// -- x1 : constructor function
// -- x3 : original constructor
// -- lr : return address
// -----------------------------------
Label allocate, done_allocate;
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and function differ.
__ cmp(x1, x3);
__ B(ne, &rt_call);
__ Allocate(JSValue::kSize, x0, x3, x4, &allocate, TAG_OBJECT);
__ Bind(&done_allocate);
@ -280,6 +287,17 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(x2, x1);
}
__ B(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(x1, x2, x1, x3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(x2, x1);
}
__ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset));
__ Ret();
}
}
@ -336,7 +354,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// -- x0 : number of arguments
// -- x1 : constructor function
// -- x2 : allocation site or undefined
// -- x3 : original constructor
// -- x3 : original constructor
// -- lr : return address
// -- sp[...]: constructor arguments
// -----------------------------------

View File

@ -3438,7 +3438,7 @@ void Heap::InitializeJSObjectFromMap(JSObject* obj, FixedArray* properties,
if (constructor->IsJSFunction() &&
JSFunction::cast(constructor)->IsInobjectSlackTrackingInProgress()) {
// We might want to shrink the object later.
DCHECK(obj->GetInternalFieldCount() == 0);
DCHECK_EQ(0, obj->GetInternalFieldCount());
filler = Heap::one_pointer_filler_map();
} else {
filler = Heap::undefined_value();

View File

@ -280,8 +280,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// runtime.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
__ mov(edi, Operand(esp, offset));
__ push(edi); // argument 2/1: constructor function
__ push(edx); // argument 3/2: original constructor
__ push(edi); // constructor function
__ push(edx); // original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ mov(ebx, eax); // store result in ebx
@ -1363,6 +1363,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : number of arguments
// -- edi : constructor function
// -- edx : original constructor
// -- esp[0] : return address
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
// -- esp[(argc + 1) * 4] : receiver
@ -1388,16 +1389,18 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
Label convert, done_convert;
__ JumpIfSmi(ebx, &convert, Label::kNear);
__ CmpObjectType(ebx, FIRST_NONSTRING_TYPE, edx);
__ CmpObjectType(ebx, FIRST_NONSTRING_TYPE, ecx);
__ j(below, &done_convert);
__ bind(&convert);
{
FrameScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(edi);
__ Push(edx);
__ Move(eax, ebx);
__ CallStub(&stub);
__ Move(ebx, eax);
__ Pop(edx);
__ Pop(edi);
}
__ bind(&done_convert);
@ -1408,9 +1411,15 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- ebx : the first argument
// -- edi : constructor function
// -- edx : original constructor
// -----------------------------------
Label allocate, done_allocate;
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and constructor differ.
__ cmp(edx, edi);
__ j(not_equal, &rt_call);
__ Allocate(JSValue::kSize, eax, ecx, no_reg, &allocate, TAG_OBJECT);
__ bind(&done_allocate);
@ -1437,6 +1446,21 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(ebx);
}
__ jmp(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(ebx);
__ Push(edi);
__ Push(edi); // constructor function
__ Push(edx); // original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(edi);
__ Pop(ebx);
}
__ mov(FieldOperand(eax, JSValue::kValueOffset), ebx);
__ Ret();
}
}

View File

@ -227,6 +227,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : number of arguments
// -- a1 : constructor function
// -- a3 : original constructor
// -- ra : return address
// -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
// -- sp[argc * 4] : receiver
@ -260,10 +261,10 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(a1);
__ Push(a1, a3);
__ CallStub(&stub);
__ Move(a0, v0);
__ Pop(a1);
__ Pop(a1, a3);
}
__ bind(&done_convert);
}
@ -273,10 +274,15 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : the first argument
// -- a1 : constructor function
// -- a3 : original constructor
// -- ra : return address
// -----------------------------------
Label allocate, done_allocate;
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and function differ.
__ Branch(&rt_call, ne, a1, Operand(a3));
__ Allocate(JSValue::kSize, v0, a2, a3, &allocate, TAG_OBJECT);
__ bind(&done_allocate);
@ -300,6 +306,17 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(a0, a1);
}
__ jmp(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a0, a1, a1, a3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(a0, a1);
}
__ Ret(USE_DELAY_SLOT);
__ sw(a0, FieldMemOperand(v0, JSValue::kValueOffset));
}
}
@ -510,7 +527,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// a3: original constructor
__ bind(&rt_call);
__ Push(a1, a3); // arguments 2-3 / 1-2
__ Push(a1, a3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ mov(t4, v0);

View File

@ -224,6 +224,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : number of arguments
// -- a1 : constructor function
// -- a3 : original constructor
// -- ra : return address
// -- sp[(argc - n - 1) * 8] : arg[n] (zero based)
// -- sp[argc * 8] : receiver
@ -257,10 +258,10 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(a1);
__ Push(a1, a3);
__ CallStub(&stub);
__ Move(a0, v0);
__ Pop(a1);
__ Pop(a1, a3);
}
__ bind(&done_convert);
}
@ -270,10 +271,15 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : the first argument
// -- a1 : constructor function
// -- a3 : original constructor
// -- ra : return address
// -----------------------------------
Label allocate, done_allocate;
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and function differ.
__ Branch(&rt_call, ne, a1, Operand(a3));
__ Allocate(JSValue::kSize, v0, a2, a3, &allocate, TAG_OBJECT);
__ bind(&done_allocate);
@ -297,6 +303,17 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(a0, a1);
}
__ jmp(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a0, a1, a1, a3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(a0, a1);
}
__ sd(a0, FieldMemOperand(v0, JSValue::kValueOffset));
__ Ret();
}
}
@ -508,7 +525,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// a3: original constructor
__ bind(&rt_call);
__ Push(a1, a3); // arguments 2-3 / 1-2
__ Push(a1, a3); // constructor function, original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ mov(t0, v0);

View File

@ -2134,8 +2134,10 @@ void WeakCell::clear_next(Heap* heap) {
bool WeakCell::next_cleared() { return next()->IsTheHole(); }
int JSObject::GetHeaderSize() {
InstanceType type = map()->instance_type();
int JSObject::GetHeaderSize() { return GetHeaderSize(map()->instance_type()); }
int JSObject::GetHeaderSize(InstanceType type) {
// Check for the most common kind of JavaScript object before
// falling into the generic switch. This speeds up the internal
// field operations considerably on average.
@ -2192,15 +2194,18 @@ int JSObject::GetHeaderSize() {
}
int JSObject::GetInternalFieldCount() {
DCHECK(1 << kPointerSizeLog2 == kPointerSize);
// Make sure to adjust for the number of in-object properties. These
// properties do contribute to the size, but are not internal fields.
return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) -
map()->GetInObjectProperties();
int JSObject::GetInternalFieldCount(Map* map) {
int instance_size = map->instance_size();
if (instance_size == kVariableSizeSentinel) return 0;
InstanceType instance_type = map->instance_type();
return ((instance_size - GetHeaderSize(instance_type)) >> kPointerSizeLog2) -
map->GetInObjectProperties();
}
int JSObject::GetInternalFieldCount() { return GetInternalFieldCount(map()); }
int JSObject::GetInternalFieldOffset(int index) {
DCHECK(index < GetInternalFieldCount() && index >= 0);
return GetHeaderSize() + (kPointerSize * index);
@ -5624,6 +5629,12 @@ void Map::SetConstructor(Object* constructor, WriteBarrierMode mode) {
}
Handle<Map> Map::CopyInitialMap(Handle<Map> map) {
return CopyInitialMap(map, map->instance_size(), map->GetInObjectProperties(),
map->unused_property_fields());
}
ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)
ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset)
ACCESSORS(JSFunction, next_function_link, Object, kNextFunctionLinkOffset)

View File

@ -33,7 +33,7 @@ void Object::Print(std::ostream& os) { // NOLINT
void HeapObject::PrintHeader(std::ostream& os, const char* id) { // NOLINT
os << "" << reinterpret_cast<void*>(this) << ": [" << id << "]\n";
os << reinterpret_cast<void*>(this) << ": [" << id << "]\n";
}
@ -117,8 +117,7 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
JSBuiltinsObject::cast(this)->JSBuiltinsObjectPrint(os);
break;
case JS_VALUE_TYPE:
os << "Value wrapper around:";
JSValue::cast(this)->value()->Print(os);
JSValue::cast(this)->JSValuePrint(os);
break;
case JS_DATE_TYPE:
JSDate::cast(this)->JSDatePrint(os);
@ -388,22 +387,33 @@ void JSObject::PrintElements(std::ostream& os) { // NOLINT
}
void JSObject::JSObjectPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "JSObject");
static void JSObjectPrintHeader(std::ostream& os, JSObject* obj,
const char* id) { // NOLINT
obj->PrintHeader(os, id);
// Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging.
PrototypeIterator iter(GetIsolate(), this);
os << " - map = " << reinterpret_cast<void*>(map()) << " ["
<< ElementsKindToString(this->map()->elements_kind())
<< "]\n - prototype = " << reinterpret_cast<void*>(iter.GetCurrent())
<< "\n {\n";
PrintProperties(os);
PrintTransitions(os);
PrintElements(os);
PrototypeIterator iter(obj->GetIsolate(), obj);
os << " - map = " << reinterpret_cast<void*>(obj->map()) << " ["
<< ElementsKindToString(obj->map()->elements_kind())
<< "]\n - prototype = " << reinterpret_cast<void*>(iter.GetCurrent());
}
static void JSObjectPrintBody(std::ostream& os, JSObject* obj) { // NOLINT
os << "\n {\n";
obj->PrintProperties(os);
obj->PrintTransitions(os);
obj->PrintElements(os);
os << " }\n";
}
void JSObject::JSObjectPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSObject");
JSObjectPrintBody(os, this);
}
void JSModule::JSModulePrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "JSModule");
os << " - map = " << reinterpret_cast<void*>(map()) << "\n"
@ -461,6 +471,7 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
if (is_access_check_needed()) os << " - access_check_needed\n";
if (!is_extensible()) os << " - non-extensible\n";
if (is_observed()) os << " - observed\n";
if (is_strong()) os << " - strong_map\n";
if (is_prototype_map()) {
os << " - prototype_map\n";
os << " - prototype info: " << Brief(prototype_info());
@ -634,20 +645,21 @@ void TypeFeedbackVector::TypeFeedbackVectorPrint(std::ostream& os) { // NOLINT
void JSValue::JSValuePrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "ValueObject");
value()->Print(os);
JSObjectPrintHeader(os, this, "JSValue");
os << "\n - value = " << Brief(value());
JSObjectPrintBody(os, this);
}
void JSMessageObject::JSMessageObjectPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "JSMessageObject");
os << " - type: " << type();
JSObjectPrintHeader(os, this, "JSMessageObject");
os << "\n - type: " << type();
os << "\n - arguments: " << Brief(argument());
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << "\n - script: " << Brief(script());
os << "\n - stack_frames: " << Brief(stack_frames());
os << "\n";
JSObjectPrintBody(os, this);
}

View File

@ -8210,6 +8210,24 @@ Handle<Map> Map::CopyNormalized(Handle<Map> map,
}
Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size,
int in_object_properties,
int unused_property_fields) {
#ifdef DEBUG
Object* constructor = map->GetConstructor();
DCHECK(constructor->IsJSFunction());
DCHECK_EQ(*map, JSFunction::cast(constructor)->initial_map());
#endif
Handle<Map> result = RawCopy(map, instance_size);
// Please note instance_type and instance_size are set when allocated.
result->SetInObjectProperties(in_object_properties);
result->set_unused_property_fields(unused_property_fields);
return result;
}
Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) {
Handle<Map> result = RawCopy(map, map->instance_size());
@ -11821,17 +11839,16 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
// First create a new map with the size and number of in-object properties
// suggested by the function.
InstanceType instance_type;
int instance_size;
int in_object_properties;
if (function->shared()->is_generator()) {
instance_type = JS_GENERATOR_OBJECT_TYPE;
instance_size = JSGeneratorObject::kSize;
in_object_properties = 0;
} else {
instance_type = JS_OBJECT_TYPE;
instance_size = function->shared()->CalculateInstanceSize();
in_object_properties = function->shared()->CalculateInObjectProperties();
}
int instance_size;
int in_object_properties;
function->CalculateInstanceSize(instance_type, 0, &instance_size,
&in_object_properties);
Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size);
if (function->map()->is_strong()) {
map->set_is_strong();
@ -11858,6 +11875,66 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
}
Handle<Map> JSFunction::EnsureDerivedHasInitialMap(
Handle<JSFunction> original_constructor, Handle<JSFunction> constructor) {
DCHECK(constructor->has_initial_map());
Isolate* isolate = constructor->GetIsolate();
Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
if (*original_constructor == *constructor) return constructor_initial_map;
if (original_constructor->has_initial_map()) {
// Check that |original_constructor|'s initial map still in sync with
// the |constructor|, otherwise we must create a new initial map for
// |original_constructor|.
if (original_constructor->initial_map()->GetConstructor() == *constructor) {
return handle(original_constructor->initial_map(), isolate);
}
}
// First create a new map with the size and number of in-object properties
// suggested by the function.
DCHECK(!original_constructor->shared()->is_generator());
DCHECK(!constructor->shared()->is_generator());
// Fetch or allocate prototype.
Handle<Object> prototype;
if (original_constructor->has_instance_prototype()) {
prototype = handle(original_constructor->instance_prototype(), isolate);
} else {
prototype = isolate->factory()->NewFunctionPrototype(original_constructor);
}
// Finally link initial map and constructor function if the original
// constructor is actually a subclass constructor.
if (IsSubclassConstructor(original_constructor->shared()->kind())) {
InstanceType instance_type = constructor_initial_map->instance_type();
int internal_fields =
JSObject::GetInternalFieldCount(*constructor_initial_map);
int instance_size;
int in_object_properties;
original_constructor->CalculateInstanceSizeForDerivedClass(
instance_type, internal_fields, &instance_size, &in_object_properties);
Handle<Map> map =
Map::CopyInitialMap(constructor_initial_map, instance_size,
in_object_properties, in_object_properties);
JSFunction::SetInitialMap(original_constructor, map, prototype);
map->SetConstructor(*constructor);
original_constructor->StartInobjectSlackTracking();
return map;
} else {
Handle<Map> map = Map::CopyInitialMap(constructor_initial_map);
DCHECK(prototype->IsJSReceiver());
if (map->prototype() != *prototype) {
Map::SetPrototype(map, prototype, FAST_PROTOTYPE);
}
map->SetConstructor(*constructor);
return map;
}
}
void JSFunction::SetInstanceClassName(String* name) {
shared()->set_instance_class_name(name);
}
@ -12193,19 +12270,56 @@ int SharedFunctionInfo::SourceSize() {
}
int SharedFunctionInfo::CalculateInstanceSize() {
int instance_size =
JSObject::kHeaderSize +
expected_nof_properties() * kPointerSize;
if (instance_size > JSObject::kMaxInstanceSize) {
instance_size = JSObject::kMaxInstanceSize;
}
return instance_size;
namespace {
void CalculateInstanceSizeHelper(InstanceType instance_type,
int requested_internal_fields,
int requested_in_object_properties,
int* instance_size,
int* in_object_properties) {
int header_size = JSObject::GetHeaderSize(instance_type);
DCHECK_LE(requested_internal_fields,
(JSObject::kMaxInstanceSize - header_size) >> kPointerSizeLog2);
*instance_size =
Min(header_size +
((requested_internal_fields + requested_in_object_properties)
<< kPointerSizeLog2),
JSObject::kMaxInstanceSize);
*in_object_properties = ((*instance_size - header_size) >> kPointerSizeLog2) -
requested_internal_fields;
}
} // namespace
void JSFunction::CalculateInstanceSize(InstanceType instance_type,
int requested_internal_fields,
int* instance_size,
int* in_object_properties) {
CalculateInstanceSizeHelper(instance_type, requested_internal_fields,
shared()->expected_nof_properties(),
instance_size, in_object_properties);
}
int SharedFunctionInfo::CalculateInObjectProperties() {
return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
void JSFunction::CalculateInstanceSizeForDerivedClass(
InstanceType instance_type, int requested_internal_fields,
int* instance_size, int* in_object_properties) {
Isolate* isolate = GetIsolate();
int expected_nof_properties = 0;
for (PrototypeIterator iter(isolate, this,
PrototypeIterator::START_AT_RECEIVER);
!iter.IsAtEnd(); iter.Advance()) {
JSFunction* func = iter.GetCurrent<JSFunction>();
SharedFunctionInfo* shared = func->shared();
expected_nof_properties += shared->expected_nof_properties();
if (!IsSubclassConstructor(shared->kind())) {
break;
}
}
CalculateInstanceSizeHelper(instance_type, requested_internal_fields,
expected_nof_properties, instance_size,
in_object_properties);
}

View File

@ -2257,8 +2257,10 @@ class JSObject: public JSReceiver {
// Get the header size for a JSObject. Used to compute the index of
// internal fields as well as the number of internal fields.
static inline int GetHeaderSize(InstanceType instance_type);
inline int GetHeaderSize();
static inline int GetInternalFieldCount(Map* map);
inline int GetInternalFieldCount();
inline int GetInternalFieldOffset(int index);
inline Object* GetInternalField(int index);
@ -5799,6 +5801,10 @@ class Map: public HeapObject {
// gathering type feedback. Use TryUpdate in those cases instead.
static Handle<Map> Update(Handle<Map> map);
static inline Handle<Map> CopyInitialMap(Handle<Map> map);
static Handle<Map> CopyInitialMap(Handle<Map> map, int instance_size,
int in_object_properties,
int unused_property_fields);
static Handle<Map> CopyDropDescriptors(Handle<Map> map);
static Handle<Map> CopyInsertDescriptor(Handle<Map> map,
Descriptor* descriptor,
@ -6830,12 +6836,6 @@ class SharedFunctionInfo: public HeapObject {
// Source size of this function.
int SourceSize();
// Calculate the instance size.
int CalculateInstanceSize();
// Calculate the number of in-object properties.
int CalculateInObjectProperties();
inline bool has_simple_parameters();
// Initialize a SharedFunctionInfo from a parsed function literal.
@ -7329,6 +7329,11 @@ class JSFunction: public JSObject {
Handle<Object> prototype);
inline bool has_initial_map();
static void EnsureHasInitialMap(Handle<JSFunction> function);
// Ensures that the |original_constructor| has correct initial map and
// returns it. If the |original_constructor| is not a subclass constructor
// its initial map is left unmodified.
static Handle<Map> EnsureDerivedHasInitialMap(
Handle<JSFunction> original_constructor, Handle<JSFunction> constructor);
// Get and set the prototype property on a JSFunction. If the
// function has an initial map the prototype is set on the initial
@ -7377,6 +7382,15 @@ class JSFunction: public JSObject {
DECLARE_CAST(JSFunction)
// Calculate the instance size and in-object properties count.
void CalculateInstanceSize(InstanceType instance_type,
int requested_internal_fields, int* instance_size,
int* in_object_properties);
void CalculateInstanceSizeForDerivedClass(InstanceType instance_type,
int requested_internal_fields,
int* instance_size,
int* in_object_properties);
// Iterates the objects, including code objects indirectly referenced
// through pointers to the first instruction in the code object.
void JSFunctionIterateBody(int object_size, ObjectVisitor* v);

View File

@ -991,46 +991,31 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
// Handle stepping into constructors if step into is active.
if (debug->StepInActive()) debug->HandleStepIn(function, true);
if (function->has_initial_map()) {
if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
// The 'Function' function ignores the receiver object when
// called using 'new' and creates a new JSFunction object that
// is returned. The receiver object is only used for error
// reporting if an error occurs when constructing the new
// JSFunction. Factory::NewJSObject() should not be used to
// allocate JSFunctions since it does not properly initialize
// the shared part of the function. Since the receiver is
// ignored anyway, we use the global object as the receiver
// instead of a new JSFunction object. This way, errors are
// reported the same way whether or not 'Function' is called
// using 'new'.
return isolate->global_proxy();
}
}
// The function should be compiled for the optimization hints to be
// available.
Compiler::Compile(function, CLEAR_EXCEPTION);
Handle<JSObject> result;
if (site.is_null()) {
result = isolate->factory()->NewJSObject(function);
} else {
result = isolate->factory()->NewJSObjectWithMemento(function, site);
JSFunction::EnsureHasInitialMap(function);
Handle<Map> initial_map =
JSFunction::EnsureDerivedHasInitialMap(original_function, function);
if (initial_map->instance_type() == JS_FUNCTION_TYPE) {
// The 'Function' function ignores the receiver object when
// called using 'new' and creates a new JSFunction object that
// is returned. The receiver object is only used for error
// reporting if an error occurs when constructing the new
// JSFunction. Factory::NewJSObject() should not be used to
// allocate JSFunctions since it does not properly initialize
// the shared part of the function. Since the receiver is
// ignored anyway, we use the global object as the receiver
// instead of a new JSFunction object. This way, errors are
// reported the same way whether or not 'Function' is called
// using 'new'.
return isolate->global_proxy();
}
// Set up the prototoype using original function.
// TODO(dslomov): instead of setting the __proto__,
// use and cache the correct map.
if (*original_function != *function) {
if (original_function->has_instance_prototype()) {
Handle<Object> prototype =
handle(original_function->instance_prototype(), isolate);
MAYBE_RETURN(JSObject::SetPrototype(result, prototype, false,
Object::THROW_ON_ERROR),
isolate->heap()->exception());
}
}
Handle<JSObject> result =
isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site);
isolate->counters()->constructed_objects()->Increment();
isolate->counters()->constructed_objects_runtime()->Increment();

View File

@ -164,8 +164,8 @@ RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
// All checks are done, now we can modify objects.
DCHECK(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
DCHECK_EQ(v8::ArrayBufferView::kInternalFieldCount,
holder->GetInternalFieldCount());
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}
@ -238,8 +238,8 @@ RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
}
size_t byte_length = length * element_size;
DCHECK(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
DCHECK_EQ(v8::ArrayBufferView::kInternalFieldCount,
holder->GetInternalFieldCount());
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}
@ -441,8 +441,8 @@ RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
DCHECK(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
DCHECK_EQ(v8::ArrayBufferView::kInternalFieldCount,
holder->GetInternalFieldCount());
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}

View File

@ -277,8 +277,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Must restore rsi (context) and rdi (constructor) before calling runtime.
__ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ movp(rdi, Operand(rsp, offset));
__ Push(rdi); // argument 2/1: constructor function
__ Push(rdx); // argument 3/2: original constructor
__ Push(rdi); // constructor function
__ Push(rdx); // original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ movp(rbx, rax); // store result in rbx
@ -1423,6 +1423,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : number of arguments
// -- rdi : constructor function
// -- rdx : original constructor
// -- rsp[0] : return address
// -- rsp[(argc - n) * 8] : arg[n] (zero-based)
// -- rsp[(argc + 1) * 8] : receiver
@ -1449,17 +1450,19 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
Label convert, done_convert;
__ JumpIfSmi(rbx, &convert, Label::kNear);
__ CmpObjectType(rbx, FIRST_NONSTRING_TYPE, rdx);
__ CmpObjectType(rbx, FIRST_NONSTRING_TYPE, rcx);
__ j(below, &done_convert);
__ bind(&convert);
{
FrameScope scope(masm, StackFrame::INTERNAL);
ToStringStub stub(masm->isolate());
__ Push(rdx);
__ Push(rdi);
__ Move(rax, rbx);
__ CallStub(&stub);
__ Move(rbx, rax);
__ Pop(rdi);
__ Pop(rdx);
}
__ bind(&done_convert);
}
@ -1469,9 +1472,14 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rbx : the first argument
// -- rdi : constructor function
// -- rdx : original constructor
// -----------------------------------
Label allocate, done_allocate, rt_call;
// Fall back to runtime if the original constructor and constructor differ.
__ cmpp(rdx, rdi);
__ j(not_equal, &rt_call);
Label allocate, done_allocate;
__ Allocate(JSValue::kSize, rax, rcx, no_reg, &allocate, TAG_OBJECT);
__ bind(&done_allocate);
@ -1497,6 +1505,21 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
__ Pop(rbx);
}
__ jmp(&done_allocate);
// Fallback to the runtime to create new object.
__ bind(&rt_call);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rbx);
__ Push(rdi);
__ Push(rdi); // constructor function
__ Push(rdx); // original constructor
__ CallRuntime(Runtime::kNewObject, 2);
__ Pop(rdi);
__ Pop(rbx);
}
__ movp(FieldOperand(rax, JSValue::kValueOffset), rbx);
__ Ret();
}
}

View File

@ -0,0 +1,405 @@
// Copyright 2015 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.
// Flags: --allow-natives-syntax
"use strict";
function checkPrototypeChain(object, constructors) {
var proto = object.__proto__;
for (var i = 0; i < constructors.length; i++) {
assertEquals(constructors[i].prototype, proto);
assertEquals(constructors[i], proto.constructor);
proto = proto.__proto__;
}
}
(function() {
class A extends Boolean {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A(true);
assertTrue(o instanceof Object);
assertTrue(o instanceof Boolean);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, Boolean]);
assertTrue(o.valueOf());
assertEquals(42, o.a);
var o1 = new A(false);
assertTrue(%HaveSameMap(o, o1));
})();
function TestErrorSubclassing(error) {
class A extends error {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A("message");
assertTrue(o instanceof Object);
assertTrue(o instanceof error);
assertTrue(o instanceof Error);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
if (error == Error) {
checkPrototypeChain(o, [A, Error, Object]);
} else {
checkPrototypeChain(o, [A, error, Error, Object]);
}
assertEquals("message", o.message);
assertEquals(error.name + ": message", o.toString());
assertEquals(42, o.a);
var o1 = new A("achtung!");
assertTrue(%HaveSameMap(o, o1));
}
(function() {
TestErrorSubclassing(Error);
TestErrorSubclassing(EvalError);
TestErrorSubclassing(RangeError);
TestErrorSubclassing(ReferenceError);
TestErrorSubclassing(SyntaxError);
TestErrorSubclassing(TypeError);
TestErrorSubclassing(URIError);
})();
(function() {
class A extends Number {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A(153);
assertTrue(o instanceof Object);
assertTrue(o instanceof Number);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, Number, Object]);
assertEquals(153, o.valueOf());
assertEquals(42, o.a);
var o1 = new A(312);
assertTrue(%HaveSameMap(o, o1));
})();
(function() {
class A extends Date {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A(1234567890);
assertTrue(o instanceof Object);
assertTrue(o instanceof Date);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, Date, Object]);
assertEquals(1234567890, o.getTime());
assertEquals(42, o.a);
var o1 = new A(2015, 10, 29);
assertEquals(2015, o1.getFullYear());
assertEquals(10, o1.getMonth());
assertEquals(29, o1.getDate());
assertTrue(%HaveSameMap(o, o1));
})();
(function() {
class A extends String {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A("foo");
assertTrue(o instanceof Object);
assertTrue(o instanceof String);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, String, Object]);
assertEquals("foo", o.valueOf());
assertEquals(42, o.a);
var o1 = new A("bar");
assertTrue(%HaveSameMap(o, o1));
})();
(function() {
class A extends RegExp {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A("o..h");
assertTrue(o instanceof Object);
assertTrue(o instanceof RegExp);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, RegExp, Object]);
assertTrue(o.test("ouch"));
assertEquals(42, o.a);
var o1 = new A(7);
assertTrue(%HaveSameMap(o, o1));
})();
function TestArraySubclassing(array) {
class A extends array {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A(10);
assertTrue(o instanceof Object);
assertTrue(o instanceof array);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, array, Object]);
assertEquals(10, o.length);
assertEquals(42, o.a);
var o1 = new A(7);
assertTrue(%HaveSameMap(o, o1));
}
(function() {
TestArraySubclassing(Array);
TestArraySubclassing(Int8Array);
TestArraySubclassing(Uint8Array);
TestArraySubclassing(Uint8ClampedArray);
TestArraySubclassing(Int16Array);
TestArraySubclassing(Uint16Array);
TestArraySubclassing(Int32Array);
TestArraySubclassing(Uint32Array);
TestArraySubclassing(Float32Array);
TestArraySubclassing(Float64Array);
})();
(function() {
class A extends ArrayBuffer {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var o = new A(16);
assertTrue(o instanceof Object);
assertTrue(o instanceof ArrayBuffer);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, ArrayBuffer, Object]);
assertEquals(16, o.byteLength);
assertEquals(42, o.a);
var o1 = new A("bar");
assertTrue(%HaveSameMap(o, o1));
class MyInt32Array extends Int32Array {
constructor(v, name) {
super(v);
this.name = name;
}
}
class MyUint32Array extends Uint32Array {
constructor(v, name) {
super(v);
this.name = name;
}
}
var int32view = new MyInt32Array(o, "cats");
var uint32view = new MyUint32Array(o, "dogs");
int32view[0] = -2;
uint32view[1] = 0xffffffff;
assertEquals("cats", int32view.name);
assertEquals("dogs", uint32view.name);
assertEquals(-2, int32view[0]);
assertEquals(-1, int32view[1]);
assertEquals(0xfffffffe, uint32view[0]);
assertEquals(0xffffffff, uint32view[1]);
})();
(function() {
class A extends DataView {
constructor(...args) {
assertTrue(%IsConstructCall());
super(...args);
this.a = 42;
}
}
var buffer = new ArrayBuffer(16);
var o = new A(buffer);
assertTrue(o instanceof Object);
assertTrue(o instanceof DataView);
assertTrue(o instanceof A);
assertEquals("object", typeof o);
checkPrototypeChain(o, [A, DataView, Object]);
o.setUint32(0, 0xcafebabe, false);
assertEquals(0xcafebabe, o.getUint32(0, false));
assertEquals(0xbebafeca, o.getUint32(0, true));
assertEquals(42, o.a);
var o1 = new A(buffer);
assertTrue(%HaveSameMap(o, o1));
})();
(function() {
class A extends Boolean {
constructor() {
assertTrue(%IsConstructCall());
super(true);
this.a00 = 0
this.a01 = 0
this.a02 = 0
this.a03 = 0
this.a04 = 0
this.a05 = 0
this.a06 = 0
this.a07 = 0
this.a08 = 0
this.a09 = 0
this.a10 = 0
this.a11 = 0
this.a12 = 0
this.a13 = 0
this.a14 = 0
this.a15 = 0
this.a16 = 0
this.a17 = 0
this.a18 = 0
this.a19 = 0
}
}
class B extends A {
constructor() {
assertTrue(%IsConstructCall());
super();
this.b00 = 0
this.b01 = 0
this.b02 = 0
this.b03 = 0
this.b04 = 0
this.b05 = 0
this.b06 = 0
this.b07 = 0
this.b08 = 0
this.b09 = 0
this.b10 = 0
this.b11 = 0
this.b12 = 0
this.b13 = 0
this.b14 = 0
this.b15 = 0
this.b16 = 0
this.b17 = 0
this.b18 = 0
this.b19 = 0
}
}
class C extends B {
constructor() {
assertTrue(%IsConstructCall());
super();
this.c00 = 0
this.c01 = 0
this.c02 = 0
this.c03 = 0
this.c04 = 0
this.c05 = 0
this.c06 = 0
this.c07 = 0
this.c08 = 0
this.c09 = 0
this.c10 = 0
this.c11 = 0
this.c12 = 0
this.c13 = 0
this.c14 = 0
this.c15 = 0
this.c16 = 0
this.c17 = 0
this.c18 = 0
this.c19 = 0
}
}
var o = new C();
assertTrue(o instanceof Object);
assertTrue(o instanceof Boolean);
assertTrue(o instanceof A);
assertTrue(o instanceof B);
assertTrue(o instanceof C);
assertEquals("object", typeof o);
checkPrototypeChain(o, [C, B, A, Boolean, Object]);
})();
(function() {
assertThrows("class A extends undefined {}");
assertThrows("class B extends NaN {}");
assertThrows("class C extends Infinity {}");
})();
(function() {
class A extends null {}
assertThrows("new A");
})();
(function() {
class A extends Symbol {}
assertThrows("new A");
})();