[builtins] Change semantics of class constructors returning primitives

This change mirrors the semantics for derived class constructors. This
change doesn't affect non class constructors.

This change could potentially break web compat. More details:
https://github.com/tc39/ecma262/pull/469

Bug=v8:5536

Change-Id: I519599949523733332d0b35e4f8d9ecb01cac495
Reviewed-on: https://chromium-review.googlesource.com/461225
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44594}
This commit is contained in:
Sathya Gunasekaran 2017-04-11 15:56:50 -07:00 committed by Commit Bot
parent d98dfd8b9b
commit a7c4e77846
16 changed files with 602 additions and 121 deletions

View File

@ -3796,6 +3796,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_rest_spread)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_template_escapes)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrict_constructor_return)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {

View File

@ -448,7 +448,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -534,7 +534,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -549,18 +550,32 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is a smi, it is *not* an object in the ECMA sense.
// r0: result
// sp[0]: receiver
// sp[1]: number of arguments (smi-tagged)
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
__ b(eq, &use_receiver);
__ JumpIfSmi(r0, &do_throw);
} else {
__ JumpIfSmi(r0, &use_receiver);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
__ CompareObjectType(r0, r1, r3, FIRST_JS_RECEIVER_TYPE);
__ b(ge, &exit);
__ b(ge, &return_value);
if (disallow_non_object_return) {
__ bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -569,7 +584,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Remove receiver from the stack, remove caller arguments, and
// return.
__ bind(&exit);
__ bind(&return_value);
// r0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
@ -582,9 +597,10 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
}
// ES6 9.2.2. Step 13+
// Check that the result is not a Smi, indicating that the constructor result
// from a derived class is neither undefined nor an Object.
if (check_derived_construct) {
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(r0, &do_throw);
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
@ -593,7 +609,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
@ -608,7 +624,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -649,6 +666,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -448,7 +448,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -548,7 +548,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -563,17 +564,32 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is a smi, it is *not* an object in the ECMA sense.
// x0: result
// jssp[0]: receiver (newly allocated object)
// jssp[1]: number of arguments (smi-tagged)
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ CompareRoot(x0, Heap::kUndefinedValueRootIndex);
__ B(eq, &use_receiver);
__ JumpIfSmi(x0, &do_throw);
} else {
__ JumpIfSmi(x0, &use_receiver);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
__ JumpIfObjectType(x0, x1, x3, FIRST_JS_RECEIVER_TYPE, &exit, ge);
__ JumpIfObjectType(x0, x1, x3, FIRST_JS_RECEIVER_TYPE, &return_value,
ge);
if (disallow_non_object_return) {
__ Bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -582,7 +598,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Remove the receiver from the stack, remove caller arguments, and
// return.
__ Bind(&exit);
__ Bind(&return_value);
// x0: result
// jssp[0]: receiver (newly allocated object)
// jssp[1]: number of arguments (smi-tagged)
@ -595,9 +611,10 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
}
// ES6 9.2.2. Step 13+
// Check that the result is not a Smi, indicating that the constructor result
// from a derived class is neither undefined nor an Object.
if (check_derived_construct) {
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(x0, &do_throw);
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
@ -605,7 +622,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ Bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ Bind(&dont_throw);
}
@ -620,7 +637,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -660,6 +678,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -93,6 +93,7 @@ namespace internal {
ASM(JSConstructStubApi) \
ASM(JSConstructStubGeneric) \
ASM(JSBuiltinsConstructStub) \
ASM(JSBuiltinsConstructStubForBase) \
ASM(JSBuiltinsConstructStubForDerived) \
TFC(FastNewClosure, FastNewClosure, 1) \
TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \

View File

@ -114,7 +114,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -187,7 +187,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -198,15 +199,28 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
if (create_implicit_receiver) {
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is a smi, it is *not* an object in the ECMA sense.
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ JumpIfRoot(eax, Heap::kUndefinedValueRootIndex, &use_receiver);
__ JumpIfSmi(eax, &do_throw, Label::kNear);
} else {
__ JumpIfSmi(eax, &use_receiver, Label::kNear);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
__ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx);
__ j(above_equal, &exit, Label::kNear);
__ j(above_equal, &return_value, Label::kNear);
if (disallow_non_object_return) {
__ bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -215,7 +229,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Restore the arguments count and leave the construct frame. The
// arguments count is stored below the receiver.
__ bind(&exit);
__ bind(&return_value);
__ mov(ebx, Operand(esp, 1 * kPointerSize));
} else {
__ mov(ebx, Operand(esp, 0));
@ -225,9 +239,10 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
}
// ES6 9.2.2. Step 13+
// Check that the result is not a Smi, indicating that the constructor result
// from a derived class is neither undefined nor an Object.
if (check_derived_construct) {
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(eax, &do_throw, Label::kNear);
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
@ -236,7 +251,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
@ -254,7 +269,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -295,6 +311,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -445,7 +445,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -531,7 +531,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -543,18 +544,32 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is a smi, it is *not* an object in the ECMA sense.
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ JumpIfRoot(v0, Heap::kUndefinedValueRootIndex, &use_receiver);
__ JumpIfSmi(v0, &do_throw);
} else {
__ JumpIfSmi(v0, &use_receiver);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
__ GetObjectType(v0, a1, a3);
__ Branch(&exit, greater_equal, a3, Operand(FIRST_JS_RECEIVER_TYPE));
__ Branch(&return_value, greater_equal, a3,
Operand(FIRST_JS_RECEIVER_TYPE));
if (disallow_non_object_return) {
__ bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -563,7 +578,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Remove receiver from the stack, remove caller arguments, and
// return.
__ bind(&exit);
__ bind(&return_value);
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
@ -576,9 +591,10 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
}
// ES6 9.2.2. Step 13+
// Check that the result is not a Smi, indicating that the constructor result
// from a derived class is neither undefined nor an Object.
if (check_derived_construct) {
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(v0, &do_throw);
__ GetObjectType(v0, a3, a3);
@ -587,7 +603,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
@ -602,7 +618,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -643,6 +660,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -446,7 +446,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -528,7 +528,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -540,18 +541,32 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is a smi, it is *not* an object in the ECMA sense.
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ JumpIfRoot(v0, Heap::kUndefinedValueRootIndex, &use_receiver);
__ JumpIfSmi(v0, &do_throw);
} else {
__ JumpIfSmi(v0, &use_receiver);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
__ GetObjectType(v0, a1, a3);
__ Branch(&exit, greater_equal, a3, Operand(FIRST_JS_RECEIVER_TYPE));
__ Branch(&return_value, greater_equal, a3,
Operand(FIRST_JS_RECEIVER_TYPE));
if (disallow_non_object_return) {
__ bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -560,7 +575,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Remove receiver from the stack, remove caller arguments, and
// return.
__ bind(&exit);
__ bind(&return_value);
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
@ -573,9 +588,10 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
}
// ES6 9.2.2. Step 13+
// Check that the result is not a Smi, indicating that the constructor result
// from a derived class is neither undefined nor an Object.
if (check_derived_construct) {
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(v0, &do_throw);
__ GetObjectType(v0, a3, a3);
@ -584,7 +600,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
@ -600,7 +616,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -641,6 +658,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -118,7 +118,7 @@ namespace {
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
bool create_implicit_receiver,
bool check_derived_construct) {
bool disallow_non_object_return) {
Label post_instantiation_deopt_entry;
// ----------- S t a t e -------------
@ -187,7 +187,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
CheckDebugStepCallWrapper());
// Store offset of return address for deoptimizer.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
masm->pc_offset());
}
@ -199,15 +200,30 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
Label use_receiver, return_value, do_throw;
// If the result is undefined, we jump out to using the implicit
// receiver, otherwise we do a smi check and fall through to
// check if the return value is a valid receiver.
if (disallow_non_object_return) {
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(equal, &use_receiver);
__ JumpIfSmi(rax, &do_throw, Label::kNear);
} else {
// If the result is a smi, it is *not* an object in the ECMA sense.
__ JumpIfSmi(rax, &use_receiver, Label::kNear);
}
// If the type of the result (stored in its map) is less than
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
__ CmpObjectType(rax, FIRST_JS_RECEIVER_TYPE, rcx);
__ j(above_equal, &exit, Label::kNear);
__ j(above_equal, &return_value, Label::kNear);
if (disallow_non_object_return) {
__ bind(&do_throw);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@ -216,7 +232,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Restore the arguments count and leave the construct frame. The
// arguments count is stored below the receiver.
__ bind(&exit);
__ bind(&return_value);
__ movp(rbx, Operand(rsp, 1 * kPointerSize));
} else {
__ movp(rbx, Operand(rsp, 0));
@ -227,8 +243,9 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// ES6 9.2.2. Step 13+
// For derived class constructors, throw a TypeError here if the result
// is not a JSReceiver.
if (check_derived_construct) {
// is not a JSReceiver. For the base constructor, we've already checked
// the result, so we omit the check.
if (disallow_non_object_return && !create_implicit_receiver) {
Label do_throw, dont_throw;
__ JumpIfSmi(rax, &do_throw, Label::kNear);
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
@ -237,7 +254,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
__ bind(&do_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
@ -256,7 +273,8 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
// Store offset of trampoline address for deoptimizer. This is the bailout
// point after the receiver instantiation but before the function invocation.
// We need to restore some registers in order to continue the above code.
if (create_implicit_receiver && !is_api_function) {
if (create_implicit_receiver && !disallow_non_object_return &&
!is_api_function) {
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
masm->pc_offset());
@ -297,6 +315,10 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, true);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);

View File

@ -481,6 +481,20 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
return NoChange();
}
// TODO(6180): Don't inline class constructors for now, as the
// inlining logic doesn't deal properly with class constructors
// that return a primitive.
if (FLAG_harmony_restrict_constructor_return &&
node->opcode() == IrOpcode::kJSConstruct &&
IsClassConstructor(shared_info->kind())) {
TRACE(
"Not inlining %s into %s because class constructor inlining is"
"not supported.\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
return NoChange();
}
// TODO(706642): Don't inline derived class constructors for now, as the
// inlining logic doesn't deal properly with derived class constructors
// that return a primitive, i.e. it's not in sync with what the Parser

View File

@ -9780,7 +9780,10 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
constructor->shared()->construct_stub() ==
isolate()->builtins()->builtin(Builtins::kJSConstructStubGeneric) ||
constructor->shared()->construct_stub() ==
isolate()->builtins()->builtin(Builtins::kJSConstructStubApi));
isolate()->builtins()->builtin(Builtins::kJSConstructStubApi) ||
constructor->shared()->construct_stub() ==
isolate()->builtins()->builtin(
Builtins::kJSBuiltinsConstructStubForBase));
HValue* check = Add<HCheckValue>(function, constructor);
// Force completion of inobject slack tracking before generating

View File

@ -202,7 +202,10 @@ DEFINE_IMPLICATION(es_staging, harmony)
V(harmony_class_fields, "harmony public fields in class literals") \
V(harmony_async_iteration, "harmony async iteration") \
V(harmony_dynamic_import, "harmony dynamic import") \
V(harmony_promise_finally, "harmony Promise.prototype.finally")
V(harmony_promise_finally, "harmony Promise.prototype.finally") \
V(harmony_restrict_constructor_return, \
"harmony disallow non undefined primitive return value from class " \
"constructor")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \

View File

@ -546,7 +546,9 @@ class ErrorUtils : public AllStatic {
T(ConstructorIsAccessor, "Class constructor may not be an accessor") \
T(ConstructorIsGenerator, "Class constructor may not be a generator") \
T(ConstructorIsAsync, "Class constructor may not be an async method") \
T(DerivedConstructorReturn, \
T(ClassConstructorReturnedNonObject, \
"Class constructors may only return object or undefined") \
T(DerivedConstructorReturnedNonObject, \
"Derived constructors may only return object or undefined") \
T(DuplicateConstructor, "A class may only have one constructor") \
T(DuplicateExport, "Duplicate export of '%'") \

View File

@ -154,7 +154,12 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate,
// [[construct]]. Instead they just set up new.target and call into the
// constructor. Hence we can reuse the builtins construct stub for derived
// classes.
Handle<Code> stub(isolate->builtins()->JSBuiltinsConstructStubForDerived());
Handle<Code> stub =
isolate->builtins()->JSBuiltinsConstructStubForDerived();
constructor->shared()->SetConstructStub(*stub);
} else if (FLAG_harmony_restrict_constructor_return) {
DCHECK(super_class->IsTheHole(isolate));
Handle<Code> stub = isolate->builtins()->JSBuiltinsConstructStubForBase();
constructor->shared()->SetConstructStub(*stub);
}

View File

@ -455,11 +455,18 @@ RUNTIME_FUNCTION(Runtime_ThrowConstructedNonConstructable) {
isolate, NewTypeError(MessageTemplate::kNotConstructor, callsite));
}
RUNTIME_FUNCTION(Runtime_ThrowDerivedConstructorReturnedNonObject) {
RUNTIME_FUNCTION(Runtime_ThrowConstructorReturnedNonObject) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
if (FLAG_harmony_restrict_constructor_return) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDerivedConstructorReturn));
isolate,
NewTypeError(MessageTemplate::kClassConstructorReturnedNonObject));
}
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kDerivedConstructorReturnedNonObject));
}
RUNTIME_FUNCTION(Runtime_ThrowUndefinedOrNullToObject) {

View File

@ -310,7 +310,7 @@ namespace internal {
F(ThrowCalledNonCallable, 1, 1) \
F(ThrowCalledOnNullOrUndefined, 1, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowDerivedConstructorReturnedNonObject, 0, 1) \
F(ThrowConstructorReturnedNonObject, 0, 1) \
F(ThrowGeneratorRunning, 0, 1) \
F(ThrowIllegalInvocation, 0, 1) \
F(ThrowIncompatibleMethodReceiver, 2, 1) \

View File

@ -0,0 +1,318 @@
// Copyright 2017 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: --harmony-restrict-constructor-return
assertThrows(
() => {
new class {
constructor() {
return 1;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return 2147483649;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return true;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return null;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return "wat";
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return Symbol();
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class {
constructor() {
return 2.2;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return 1;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return 2147483649;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return true;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return null;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return "wat";
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return Symbol();
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {
return 2.2;
}
}();
},
TypeError,
"Class constructors may only return object or undefined"
);
assertThrows(
() => {
new class extends Object {
constructor() {}
}();
},
ReferenceError,
"Must call super constructor in derived class before accessing " +
"'this' or returning from derived constructor"
);
(function() {
let ret_val = { x: 1 };
let x = new class {
constructor() {
return ret_val;
}
}();
assertSame(ret_val, x);
})();
(function() {
class Foo {
constructor() {}
}
let x = new Foo();
assertTrue(x instanceof Foo);
})();
(function() {
class Foo {
constructor() {
return undefined;
}
}
let x = new Foo();
assertTrue(x instanceof Foo);
})();
(function() {
let ret_val = { x: 1 };
let x = new class extends Object {
constructor() {
return ret_val;
}
}();
assertSame(ret_val, x);
})();
(function() {
class Foo extends Object {
constructor() {
super();
return undefined;
}
}
let x = new Foo();
assertTrue(x instanceof Foo);
})();
(function() {
class Foo extends Object {
constructor() {
super();
}
}
let x = new Foo();
assertTrue(x instanceof Foo);
})();
(function() {
function foo() {
return 1;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return 2147483649;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return true;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return undefined;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return null;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return "wat";
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return Symbol();
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
function foo() {
return 2.2;
}
let x = new foo();
assertTrue(x instanceof foo);
})();
(function() {
var ret_val = { x: 1 };
function foo() {
return ret_val;
}
let x = new foo();
assertSame(x, ret_val);
})();