[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:
parent
8a2618a986
commit
cd5f48302a
@ -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);
|
||||
|
||||
|
@ -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
|
||||
// -----------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
146
src/objects.cc
146
src/objects.cc
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
405
test/mjsunit/es6/classes-subclass-builtins.js
Normal file
405
test/mjsunit/es6/classes-subclass-builtins.js
Normal 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");
|
||||
})();
|
Loading…
Reference in New Issue
Block a user