Commit c86f1897 authored by cbruni's avatar cbruni Committed by Commit bot

[runtime] Throw exception for derived constructors in correct context.

When derived constructors return a non-object (or not undefined) we
currently throw an exception directly in the callee context. This was
achieved by desugaring the return statement for derived classes. To
be spec compliamnt a separate ConstructStubForDerived is introduced.
Instead of trowing directly, the desugared return statement inside
a derived constructor only returns an integer to indicate an incompatible
result.

BUG=v8:4509
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33336}
parent c5556164
......@@ -417,7 +417,8 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool create_implicit_receiver) {
bool create_implicit_receiver,
bool check_derived_construct) {
// ----------- S t a t e -------------
// -- r0 : number of arguments
// -- r1 : constructor function
......@@ -685,6 +686,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Leave construct frame.
}
// 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) {
Label dont_throw;
__ JumpIfNotSmi(r0, &dont_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
__ add(sp, sp, Operand(kPointerSize));
if (create_implicit_receiver) {
......@@ -695,17 +709,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true);
Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, true);
Generate_JSConstructStubHelper(masm, true, true, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);
}
......
......@@ -418,7 +418,8 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool create_implicit_receiver) {
bool create_implicit_receiver,
bool check_derived_construct) {
// ----------- S t a t e -------------
// -- x0 : number of arguments
// -- x1 : constructor function
......@@ -697,6 +698,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Leave construct frame.
}
// 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) {
Label dont_throw;
__ JumpIfNotSmi(x0, &dont_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
}
__ Bind(&dont_throw);
}
__ DropBySMI(x1);
__ Drop(1);
if (create_implicit_receiver) {
......@@ -707,17 +721,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true);
Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, true);
Generate_JSConstructStubHelper(masm, true, true, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);
}
......
......@@ -174,6 +174,8 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSBuiltinsConstructStub, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSBuiltinsConstructStubForDerived, BUILTIN, UNINITIALIZED, \
kNoExtraICState) \
V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
......@@ -384,6 +386,7 @@ class Builtins {
static void Generate_CompileOptimizedConcurrent(MacroAssembler* masm);
static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
static void Generate_JSBuiltinsConstructStub(MacroAssembler* masm);
static void Generate_JSBuiltinsConstructStubForDerived(MacroAssembler* masm);
static void Generate_JSConstructStubApi(MacroAssembler* masm);
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
......
......@@ -118,7 +118,8 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool create_implicit_receiver) {
bool create_implicit_receiver,
bool check_derived_construct) {
// ----------- S t a t e -------------
// -- eax: number of arguments
// -- edi: constructor function
......@@ -331,8 +332,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
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; see ECMA-262 section 13.2.2-7
// on page 74.
// of the receiver and use the result.
Label use_receiver, exit;
// If the result is a smi, it is *not* an object in the ECMA sense.
......@@ -359,6 +359,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Leave construct frame.
}
// 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) {
Label dont_throw;
__ JumpIfNotSmi(eax, &dont_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
// Remove caller arguments from the stack and return.
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ pop(ecx);
......@@ -372,17 +385,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true);
Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, true);
Generate_JSConstructStubHelper(masm, true, true, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);
}
......
......@@ -709,6 +709,12 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
}
void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a1);
......
......@@ -702,6 +702,12 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
}
void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a1);
......
......@@ -2725,23 +2725,22 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
if (IsSubclassConstructor(function_state_->kind())) {
// For subclass constructors we need to return this in case of undefined
// and throw an exception in case of a non object.
// return a Smi (transformed into an exception in the ConstructStub)
// for a non object.
//
// return expr;
//
// Is rewritten as:
//
// return (temp = expr) === undefined ? this :
// %_IsJSReceiver(temp) ? temp : throw new TypeError(...);
// %_IsJSReceiver(temp) ? temp : 1;
// temp = expr
Variable* temp = scope_->NewTemporary(
ast_value_factory()->empty_string());
Assignment* assign = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos);
Expression* throw_expression =
NewThrowTypeError(MessageTemplate::kDerivedConstructorReturn,
ast_value_factory()->empty_string(), pos);
// %_IsJSReceiver(temp)
ZoneList<Expression*>* is_spec_object_args =
new (zone()) ZoneList<Expression*>(1, zone());
......@@ -2752,7 +2751,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
// %_IsJSReceiver(temp) ? temp : throw_expression
Expression* is_object_conditional = factory()->NewConditional(
is_spec_object_call, factory()->NewVariableProxy(temp),
throw_expression, pos);
factory()->NewSmiLiteral(1, pos), pos);
// temp === undefined
Expression* is_undefined = factory()->NewCompareOperation(
......
......@@ -705,6 +705,12 @@ void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
}
void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ push(r4);
......
......@@ -148,7 +148,7 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate, Handle<Object> name,
// [[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()->JSBuiltinsConstructStub());
Handle<Code> stub(isolate->builtins()->JSBuiltinsConstructStubForDerived());
constructor->shared()->set_construct_stub(*stub);
}
......
......@@ -448,6 +448,14 @@ RUNTIME_FUNCTION(Runtime_ThrowConstructedNonConstructable) {
}
RUNTIME_FUNCTION(Runtime_ThrowDerivedConstructorReturnedNonObject) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDerivedConstructorReturn));
}
// ES6 section 7.3.17 CreateListFromArrayLike (obj)
RUNTIME_FUNCTION(Runtime_CreateListFromArrayLike) {
HandleScope scope(isolate);
......
......@@ -333,6 +333,7 @@ namespace internal {
F(IS_VAR, 1, 1) \
F(IncrementStatsCounter, 1, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowDerivedConstructorReturnedNonObject, 0, 1) \
F(ThrowCalledNonCallable, 1, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(IncrementUseCounter, 1, 1)
......
......@@ -117,7 +117,8 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool create_implicit_receiver) {
bool create_implicit_receiver,
bool check_derived_construct) {
// ----------- S t a t e -------------
// -- rax: number of arguments
// -- rdi: constructor function
......@@ -357,6 +358,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Leave construct frame.
}
// 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) {
Label dont_throw;
__ JumpIfNotSmi(rax, &dont_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
// Remove caller arguments from the stack and return.
__ PopReturnAddressTo(rcx);
SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
......@@ -371,17 +385,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true);
Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, true);
Generate_JSConstructStubHelper(masm, true, true, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);
}
......
......@@ -118,7 +118,8 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool create_implicit_receiver) {
bool create_implicit_receiver,
bool check_derived_construct) {
// ----------- S t a t e -------------
// -- eax: number of arguments
// -- edi: constructor function
......@@ -359,6 +360,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// Leave construct frame.
}
// 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) {
Label dont_throw;
__ JumpIfNotSmi(eax, &dont_throw);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
}
__ bind(&dont_throw);
}
// Remove caller arguments from the stack and return.
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ pop(ecx);
......@@ -372,17 +386,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true);
Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, true);
Generate_JSConstructStubHelper(masm, true, true, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false);
Generate_JSConstructStubHelper(masm, false, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, false, true);
}
......
......@@ -929,6 +929,7 @@
'regress/regress-444805': [SKIP],
'regress/regress-446389': [SKIP],
'regress/regress-447756': [SKIP],
'regress/regress-4509-Class-constructor-typeerror-realm' : [SKIP],
'regress/regress-4515': [SKIP],
'regress/regress-4521': [SKIP],
'regress/regress-4525': [SKIP],
......
// 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.
"use strict";
var realm = Realm.create();
var OtherTypeError = Realm.eval(realm, 'TypeError');
class Derived extends Object {
constructor() {
return null;
}
}
assertThrows(() => { new Derived() }, TypeError);
var OtherDerived = Realm.eval(realm,
"'use strict';" +
"class Derived extends Object {" +
"constructor() {" +
"return null;" +
"}};");
// Before throwing the TypeError we have to switch to the caller context.
assertThrows(() => { new OtherDerived() }, TypeError);
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