From 4490ce85205d3504dc0bf444f5e8b3deb04cbb06 Mon Sep 17 00:00:00 2001 From: ishell Date: Mon, 2 Nov 2015 00:25:19 -0800 Subject: [PATCH] Reland "[es6] Better support for built-ins subclassing." Original issue's description: > [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 > > Committed: https://crrev.com/cd5f48302a502154a0106d12e3066bd563c6340c > Cr-Commit-Position: refs/heads/master@{#31680} It also fixes typed array map smashing done during typed array initialization. BUG=v8:3101, v8:3330, v8:4419 LOG=Y Review URL: https://codereview.chromium.org/1413033006 Cr-Commit-Position: refs/heads/master@{#31701} --- src/arm/builtins-arm.cc | 46 +- src/arm64/builtins-arm64.cc | 30 +- src/crankshaft/hydrogen.cc | 22 - src/heap/heap.cc | 2 +- src/ia32/builtins-ia32.cc | 32 +- src/mips/builtins-mips.cc | 25 +- src/mips64/builtins-mips64.cc | 25 +- src/objects-inl.h | 27 +- src/objects-printer.cc | 48 +- src/objects.cc | 146 ++++++- src/objects.h | 26 +- src/runtime/runtime-object.cc | 53 +-- src/runtime/runtime-typedarray.cc | 12 +- src/x64/builtins-x64.cc | 31 +- test/mjsunit/es6/classes-subclass-builtins.js | 412 ++++++++++++++++++ 15 files changed, 791 insertions(+), 146 deletions(-) create mode 100644 test/mjsunit/es6/classes-subclass-builtins.js diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index efa8d20e38..138dfc12da 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -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); diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 574b714130..03c2024a23 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -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 // ----------------------------------- diff --git a/src/crankshaft/hydrogen.cc b/src/crankshaft/hydrogen.cc index 555291f588..c7e18fec1a 100644 --- a/src/crankshaft/hydrogen.cc +++ b/src/crankshaft/hydrogen.cc @@ -10132,25 +10132,6 @@ void HOptimizedGraphBuilder::GenerateDataViewInitialize( } -static Handle TypedArrayMap(Isolate* isolate, - ExternalArrayType array_type, - ElementsKind target_kind) { - Handle native_context = isolate->native_context(); - Handle fun; - switch (array_type) { -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ - case kExternal##Type##Array: \ - fun = Handle(native_context->type##_array_fun()); \ - break; - - TYPED_ARRAYS(TYPED_ARRAY_CASE) -#undef TYPED_ARRAY_CASE - } - Handle map(fun->initial_map()); - return Map::AsElementsKind(map, target_kind); -} - - HValue* HOptimizedGraphBuilder::BuildAllocateExternalElements( ExternalArrayType array_type, bool is_zero_byte_offset, @@ -10364,9 +10345,6 @@ void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( if (buffer != NULL) { elements = BuildAllocateExternalElements( array_type, is_zero_byte_offset, buffer, byte_offset, length); - Handle obj_map = - TypedArrayMap(isolate(), array_type, fixed_elements_kind); - AddStoreMapConstant(obj, obj_map); } else { DCHECK(is_zero_byte_offset); elements = BuildAllocateFixedTypedArray(array_type, element_size, diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 49b1e0330b..1d048d904e 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -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(); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 139cb0fbb3..4cef8db1f6 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -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(); } } diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index 2779e7e900..712ba8c2bb 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -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); diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index 4ffb21b5d9..f5295f3a9e 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -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); diff --git a/src/objects-inl.h b/src/objects-inl.h index bfdf7b66d0..6dc3d38365 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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::CopyInitialMap(Handle 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) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index f2fee648f9..501c3db35b 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -33,7 +33,7 @@ void Object::Print(std::ostream& os) { // NOLINT void HeapObject::PrintHeader(std::ostream& os, const char* id) { // NOLINT - os << "" << reinterpret_cast(this) << ": [" << id << "]\n"; + os << reinterpret_cast(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(map()) << " [" - << ElementsKindToString(this->map()->elements_kind()) - << "]\n - prototype = " << reinterpret_cast(iter.GetCurrent()) - << "\n {\n"; - PrintProperties(os); - PrintTransitions(os); - PrintElements(os); + PrototypeIterator iter(obj->GetIsolate(), obj); + os << " - map = " << reinterpret_cast(obj->map()) << " [" + << ElementsKindToString(obj->map()->elements_kind()) + << "]\n - prototype = " << reinterpret_cast(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(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); } diff --git a/src/objects.cc b/src/objects.cc index 35beaf3dad..74bf10b36b 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -8210,6 +8210,24 @@ Handle Map::CopyNormalized(Handle map, } +Handle Map::CopyInitialMap(Handle 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 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::CopyDropDescriptors(Handle map) { Handle result = RawCopy(map, map->instance_size()); @@ -11821,17 +11839,16 @@ void JSFunction::EnsureHasInitialMap(Handle 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 = isolate->factory()->NewMap(instance_type, instance_size); if (function->map()->is_strong()) { map->set_is_strong(); @@ -11858,6 +11875,66 @@ void JSFunction::EnsureHasInitialMap(Handle function) { } +Handle JSFunction::EnsureDerivedHasInitialMap( + Handle original_constructor, Handle constructor) { + DCHECK(constructor->has_initial_map()); + Isolate* isolate = constructor->GetIsolate(); + Handle 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 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::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::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(); + 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); } diff --git a/src/objects.h b/src/objects.h index 127daddb07..1853b46832 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 Update(Handle map); + static inline Handle CopyInitialMap(Handle map); + static Handle CopyInitialMap(Handle map, int instance_size, + int in_object_properties, + int unused_property_fields); static Handle CopyDropDescriptors(Handle map); static Handle CopyInsertDescriptor(Handle 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 prototype); inline bool has_initial_map(); static void EnsureHasInitialMap(Handle 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 EnsureDerivedHasInitialMap( + Handle original_constructor, Handle 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); diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index 9624aa03e3..7696fbc2e5 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -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 result; - if (site.is_null()) { - result = isolate->factory()->NewJSObject(function); - } else { - result = isolate->factory()->NewJSObjectWithMemento(function, site); + JSFunction::EnsureHasInitialMap(function); + Handle 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 prototype = - handle(original_function->instance_prototype(), isolate); - MAYBE_RETURN(JSObject::SetPrototype(result, prototype, false, - Object::THROW_ON_ERROR), - isolate->heap()->exception()); - } - } + Handle result = + isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site); isolate->counters()->constructed_objects()->Increment(); isolate->counters()->constructed_objects_runtime()->Increment(); diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc index b963868e81..f39e37072d 100644 --- a/src/runtime/runtime-typedarray.cc +++ b/src/runtime/runtime-typedarray.cc @@ -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)); } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 0354ffee5b..94ab284d46 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -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(); } } diff --git a/test/mjsunit/es6/classes-subclass-builtins.js b/test/mjsunit/es6/classes-subclass-builtins.js new file mode 100644 index 0000000000..175d0dcbc0 --- /dev/null +++ b/test/mjsunit/es6/classes-subclass-builtins.js @@ -0,0 +1,412 @@ +// 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 array(13); + assertTrue(o instanceof Object); + assertTrue(o instanceof array); + assertEquals("object", typeof o); + checkPrototypeChain(o, [array, Object]); + assertEquals(13, o.length); + + 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"); +})();