Commit a7c4e778 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[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/461225Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44594}
parent d98dfd8b
......@@ -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) {
......
......@@ -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)
__ JumpIfSmi(r0, &use_receiver);
// 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);
......
......@@ -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)
__ JumpIfSmi(x0, &use_receiver);
// 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);
......
......@@ -93,6 +93,7 @@ namespace internal {
ASM(JSConstructStubApi) \
ASM(JSConstructStubGeneric) \
ASM(JSBuiltinsConstructStub) \
ASM(JSBuiltinsConstructStubForBase) \
ASM(JSBuiltinsConstructStubForDerived) \
TFC(FastNewClosure, FastNewClosure, 1) \
TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \
......
......@@ -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.
__ JumpIfSmi(eax, &use_receiver, Label::kNear);
// 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);
......
......@@ -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)
__ JumpIfSmi(v0, &use_receiver);
// 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);
......
......@@ -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)
__ JumpIfSmi(v0, &use_receiver);
// 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);
......
......@@ -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;
// If the result is a smi, it is *not* an object in the ECMA sense.
__ JumpIfSmi(rax, &use_receiver, Label::kNear);
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);
......
......@@ -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
......
......@@ -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
......
......@@ -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) \
......
......@@ -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 '%'") \
......
......@@ -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);
}
......
......@@ -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::kClassConstructorReturnedNonObject));
}
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDerivedConstructorReturn));
isolate,
NewTypeError(MessageTemplate::kDerivedConstructorReturnedNonObject));
}
RUNTIME_FUNCTION(Runtime_ThrowUndefinedOrNullToObject) {
......
......@@ -282,55 +282,55 @@ namespace internal {
#define FOR_EACH_INTRINSIC_I18N(F)
#endif
#define FOR_EACH_INTRINSIC_INTERNAL(F) \
F(AllocateInNewSpace, 1, 1) \
F(AllocateInTargetSpace, 2, 1) \
F(AllocateSeqOneByteString, 1, 1) \
F(AllocateSeqTwoByteString, 1, 1) \
F(CheckIsBootstrapping, 0, 1) \
F(CreateAsyncFromSyncIterator, 1, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportFromRuntime, 1, 1) \
F(IncrementUseCounter, 1, 1) \
F(InstallToContext, 1, 1) \
F(Interrupt, 0, 1) \
F(IS_VAR, 1, 1) \
F(NewReferenceError, 2, 1) \
F(NewSyntaxError, 2, 1) \
F(NewTypeError, 2, 1) \
F(OrdinaryHasInstance, 2, 1) \
F(PromoteScheduledException, 0, 1) \
F(ReThrow, 1, 1) \
F(RunMicrotasks, 0, 1) \
F(StackGuard, 0, 1) \
F(Throw, 1, 1) \
F(ThrowApplyNonFunction, 1, 1) \
F(ThrowCannotConvertToPrimitive, 0, 1) \
F(ThrowCalledNonCallable, 1, 1) \
F(ThrowCalledOnNullOrUndefined, 1, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowDerivedConstructorReturnedNonObject, 0, 1) \
F(ThrowGeneratorRunning, 0, 1) \
F(ThrowIllegalInvocation, 0, 1) \
F(ThrowIncompatibleMethodReceiver, 2, 1) \
F(ThrowInvalidHint, 1, 1) \
F(ThrowInvalidStringLength, 0, 1) \
F(ThrowInvalidTypedArrayAlignment, 2, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
F(ThrowSymbolIteratorInvalid, 0, 1) \
F(ThrowNonCallableInInstanceOfCheck, 0, 1) \
F(ThrowNonObjectInInstanceOfCheck, 0, 1) \
F(ThrowNotConstructor, 1, 1) \
F(ThrowNotGeneric, 1, 1) \
F(ThrowRangeError, -1 /* >= 1 */, 1) \
F(ThrowReferenceError, 1, 1) \
F(ThrowStackOverflow, 0, 1) \
F(ThrowSymbolAsyncIteratorInvalid, 0, 1) \
F(ThrowTypeError, -1 /* >= 1 */, 1) \
F(ThrowUndefinedOrNullToObject, 1, 1) \
F(Typeof, 1, 1) \
F(UnwindAndFindExceptionHandler, 0, 1) \
#define FOR_EACH_INTRINSIC_INTERNAL(F) \
F(AllocateInNewSpace, 1, 1) \
F(AllocateInTargetSpace, 2, 1) \
F(AllocateSeqOneByteString, 1, 1) \
F(AllocateSeqTwoByteString, 1, 1) \
F(CheckIsBootstrapping, 0, 1) \
F(CreateAsyncFromSyncIterator, 1, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportFromRuntime, 1, 1) \
F(IncrementUseCounter, 1, 1) \
F(InstallToContext, 1, 1) \
F(Interrupt, 0, 1) \
F(IS_VAR, 1, 1) \
F(NewReferenceError, 2, 1) \
F(NewSyntaxError, 2, 1) \
F(NewTypeError, 2, 1) \
F(OrdinaryHasInstance, 2, 1) \
F(PromoteScheduledException, 0, 1) \
F(ReThrow, 1, 1) \
F(RunMicrotasks, 0, 1) \
F(StackGuard, 0, 1) \
F(Throw, 1, 1) \
F(ThrowApplyNonFunction, 1, 1) \
F(ThrowCannotConvertToPrimitive, 0, 1) \
F(ThrowCalledNonCallable, 1, 1) \
F(ThrowCalledOnNullOrUndefined, 1, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowConstructorReturnedNonObject, 0, 1) \
F(ThrowGeneratorRunning, 0, 1) \
F(ThrowIllegalInvocation, 0, 1) \
F(ThrowIncompatibleMethodReceiver, 2, 1) \
F(ThrowInvalidHint, 1, 1) \
F(ThrowInvalidStringLength, 0, 1) \
F(ThrowInvalidTypedArrayAlignment, 2, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
F(ThrowSymbolIteratorInvalid, 0, 1) \
F(ThrowNonCallableInInstanceOfCheck, 0, 1) \
F(ThrowNonObjectInInstanceOfCheck, 0, 1) \
F(ThrowNotConstructor, 1, 1) \
F(ThrowNotGeneric, 1, 1) \
F(ThrowRangeError, -1 /* >= 1 */, 1) \
F(ThrowReferenceError, 1, 1) \
F(ThrowStackOverflow, 0, 1) \
F(ThrowSymbolAsyncIteratorInvalid, 0, 1) \
F(ThrowTypeError, -1 /* >= 1 */, 1) \
F(ThrowUndefinedOrNullToObject, 1, 1) \
F(Typeof, 1, 1) \
F(UnwindAndFindExceptionHandler, 0, 1) \
F(AllowDynamicFunction, 1, 1)
#define FOR_EACH_INTRINSIC_LITERALS(F) \
......
// 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);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment