Commit 3226e980 authored by arv's avatar arv Committed by Commit bot

[strong] Check arity of functions

In strong mode it is an error to call a function with too few
arguments.

This is enforced inside the ArgumentsAdaptorTrampoline.

This does not yet handle rest parameters

BUG=v8:3956
LOG=N
R=rossberg@chromium.org, dslomov@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#28346}
parent 5bbe7992
...@@ -1722,6 +1722,22 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1722,6 +1722,22 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected { // Too few parameters: Actual < expected
__ bind(&too_few); __ bind(&too_few);
// If the function is strong we need to throw an error.
Label weak_function;
__ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ ldr(r4, FieldMemOperand(r4, SharedFunctionInfo::kCompilerHintsOffset));
__ tst(r4, Operand(1 << (SharedFunctionInfo::kStrongModeFunction +
kSmiTagSize)));
__ b(eq, &weak_function);
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm); EnterArgumentsAdaptorFrame(masm);
// Calculate copy start address into r0 and copy end address is fp. // Calculate copy start address into r0 and copy end address is fp.
......
...@@ -1733,13 +1733,31 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1733,13 +1733,31 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected { // Too few parameters: Actual < expected
__ Bind(&too_few); __ Bind(&too_few);
EnterArgumentsAdaptorFrame(masm);
Register copy_from = x10; Register copy_from = x10;
Register copy_end = x11; Register copy_end = x11;
Register copy_to = x12; Register copy_to = x12;
Register scratch1 = x13, scratch2 = x14; Register scratch1 = x13, scratch2 = x14;
// If the function is strong we need to throw an error.
Label weak_function;
__ Ldr(scratch1,
FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
__ Ldr(scratch2.W(),
FieldMemOperand(scratch1, SharedFunctionInfo::kCompilerHintsOffset));
__ TestAndBranchIfAllClear(scratch2.W(),
(1 << SharedFunctionInfo::kStrongModeFunction),
&weak_function);
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm);
__ Lsl(argc_expected, argc_expected, kPointerSizeLog2); __ Lsl(argc_expected, argc_expected, kPointerSizeLog2);
__ Lsl(argc_actual, argc_actual, kPointerSizeLog2); __ Lsl(argc_actual, argc_actual, kPointerSizeLog2);
......
...@@ -61,7 +61,7 @@ class JSCallFunctionAccessor { ...@@ -61,7 +61,7 @@ class JSCallFunctionAccessor {
namespace { namespace {
// A facade on a JSFunction's graph to facilitate inlining. It assumes the // A facade on a JSFunction's graph to facilitate inlining. It assumes
// that the function graph has only one return statement, and provides // that the function graph has only one return statement, and provides
// {UnifyReturn} to convert a function graph to that end. // {UnifyReturn} to convert a function graph to that end.
class Inlinee { class Inlinee {
...@@ -363,6 +363,12 @@ Reduction JSInliner::Reduce(Node* node) { ...@@ -363,6 +363,12 @@ Reduction JSInliner::Reduce(Node* node) {
Node* outer_frame_state = call.frame_state(); Node* outer_frame_state = call.frame_state();
// Insert argument adaptor frame if required. // Insert argument adaptor frame if required.
if (call.formal_arguments() != inlinee.formal_parameters()) { if (call.formal_arguments() != inlinee.formal_parameters()) {
// In strong mode, in case of too few arguments we need to throw a
// TypeError so we must not inline this call.
if (is_strong(info.language_mode()) &&
call.formal_arguments() < inlinee.formal_parameters()) {
return NoChange();
}
outer_frame_state = outer_frame_state =
CreateArgumentsAdaptorFrameState(&call, function, info.zone()); CreateArgumentsAdaptorFrameState(&call, function, info.zone());
} }
......
...@@ -8121,6 +8121,15 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target, ...@@ -8121,6 +8121,15 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
return false; return false;
} }
// In strong mode it is an error to call a function with too few arguments.
// In that case do not inline because then the arity check would be skipped.
if (is_strong(function->language_mode()) &&
arguments_count < function->parameter_count()) {
TraceInline(target, caller,
"too few arguments passed to a strong function");
return false;
}
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// After this point, we've made a decision to inline this function (so // After this point, we've made a decision to inline this function (so
// TryInline should always return true). // TryInline should always return true).
......
...@@ -1577,6 +1577,21 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1577,6 +1577,21 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected. { // Too few parameters: Actual < expected.
__ bind(&too_few); __ bind(&too_few);
// If the function is strong we need to throw an error.
Label weak_function;
__ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrongModeByteOffset),
1 << SharedFunctionInfo::kStrongModeBitWithinByte);
__ j(equal, &weak_function, Label::kNear);
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm); EnterArgumentsAdaptorFrame(masm);
// Copy receiver and all actual arguments. // Copy receiver and all actual arguments.
......
...@@ -141,6 +141,7 @@ var kMessages = { ...@@ -141,6 +141,7 @@ var kMessages = {
strong_constructor_this: ["In strong mode, 'this' can only be used to initialize properties, and cannot be nested inside another statement or expression"], strong_constructor_this: ["In strong mode, 'this' can only be used to initialize properties, and cannot be nested inside another statement or expression"],
strong_constructor_return_value: ["In strong mode, returning a value from a constructor is deprecated"], strong_constructor_return_value: ["In strong mode, returning a value from a constructor is deprecated"],
strong_constructor_return_misplaced: ["In strong mode, returning from a constructor before its super constructor invocation or all assignments to 'this' is deprecated"], strong_constructor_return_misplaced: ["In strong mode, returning from a constructor before its super constructor invocation or all assignments to 'this' is deprecated"],
strong_arity: ["In strong mode, calling a function with too few arguments is deprecated"],
sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"], sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"],
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"], malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"], cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
......
...@@ -1742,6 +1742,22 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1742,6 +1742,22 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected. { // Too few parameters: Actual < expected.
__ bind(&too_few); __ bind(&too_few);
// If the function is strong we need to throw an error.
Label weak_function;
__ lw(t1, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
__ lw(t1, FieldMemOperand(t1, SharedFunctionInfo::kCompilerHintsOffset));
__ And(t2, t1, Operand(1 << (SharedFunctionInfo::kStrongModeFunction +
kSmiTagSize)));
__ Branch(&weak_function, eq, t2, Operand(zero_reg));
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm); EnterArgumentsAdaptorFrame(masm);
// Calculate copy start address into a0 and copy end address is fp. // Calculate copy start address into a0 and copy end address is fp.
......
...@@ -1751,6 +1751,21 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1751,6 +1751,21 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected. { // Too few parameters: Actual < expected.
__ bind(&too_few); __ bind(&too_few);
// If the function is strong we need to throw an error.
Label weak_function;
__ ld(t1, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
__ lbu(t1, FieldMemOperand(t1, SharedFunctionInfo::kCompilerHintsOffset));
__ And(t2, t1, Operand(1 << SharedFunctionInfo::kStrongModeBitWithinByte));
__ Branch(&weak_function, eq, t2, Operand(zero_reg));
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm); EnterArgumentsAdaptorFrame(masm);
// Calculate copy start address into a0 and copy end address is fp. // Calculate copy start address into a0 and copy end address is fp.
......
...@@ -7406,6 +7406,8 @@ class SharedFunctionInfo: public HeapObject { ...@@ -7406,6 +7406,8 @@ class SharedFunctionInfo: public HeapObject {
// Allows to use byte-width instructions. // Allows to use byte-width instructions.
static const int kStrictModeBitWithinByte = static const int kStrictModeBitWithinByte =
(kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte; (kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte;
static const int kStrongModeBitWithinByte =
(kStrongModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte;
static const int kNativeBitWithinByte = static const int kNativeBitWithinByte =
(kNative + kCompilerHintsSmiTagSize) % kBitsPerByte; (kNative + kCompilerHintsSmiTagSize) % kBitsPerByte;
...@@ -7413,12 +7415,18 @@ class SharedFunctionInfo: public HeapObject { ...@@ -7413,12 +7415,18 @@ class SharedFunctionInfo: public HeapObject {
#if defined(V8_TARGET_LITTLE_ENDIAN) #if defined(V8_TARGET_LITTLE_ENDIAN)
static const int kStrictModeByteOffset = kCompilerHintsOffset + static const int kStrictModeByteOffset = kCompilerHintsOffset +
(kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte; (kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
static const int kStrongModeByteOffset =
kCompilerHintsOffset +
(kStrongModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
static const int kNativeByteOffset = kCompilerHintsOffset + static const int kNativeByteOffset = kCompilerHintsOffset +
(kNative + kCompilerHintsSmiTagSize) / kBitsPerByte; (kNative + kCompilerHintsSmiTagSize) / kBitsPerByte;
#elif defined(V8_TARGET_BIG_ENDIAN) #elif defined(V8_TARGET_BIG_ENDIAN)
static const int kStrictModeByteOffset = kCompilerHintsOffset + static const int kStrictModeByteOffset = kCompilerHintsOffset +
(kCompilerHintsSize - 1) - (kCompilerHintsSize - 1) -
((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte); ((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte);
static const int kStrongModeByteOffset =
kCompilerHintsOffset + (kCompilerHintsSize - 1) -
((kStrongModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte);
static const int kNativeByteOffset = kCompilerHintsOffset + static const int kNativeByteOffset = kCompilerHintsOffset +
(kCompilerHintsSize - 1) - (kCompilerHintsSize - 1) -
((kNative + kCompilerHintsSmiTagSize) / kBitsPerByte); ((kNative + kCompilerHintsSmiTagSize) / kBitsPerByte);
......
...@@ -605,5 +605,13 @@ RUNTIME_FUNCTION(Runtime_IsFunction) { ...@@ -605,5 +605,13 @@ RUNTIME_FUNCTION(Runtime_IsFunction) {
CONVERT_ARG_CHECKED(Object, obj, 0); CONVERT_ARG_CHECKED(Object, obj, 0);
return isolate->heap()->ToBoolean(obj->IsJSFunction()); return isolate->heap()->ToBoolean(obj->IsJSFunction());
} }
RUNTIME_FUNCTION(Runtime_ThrowStrongModeTooFewArguments) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError("strong_arity", HandleVector<Object>(NULL, 0)));
}
} }
} // namespace v8::internal } // namespace v8::internal
...@@ -207,6 +207,7 @@ namespace internal { ...@@ -207,6 +207,7 @@ namespace internal {
F(FunctionIsBuiltin, 1, 1) \ F(FunctionIsBuiltin, 1, 1) \
F(SetCode, 2, 1) \ F(SetCode, 2, 1) \
F(SetNativeFlag, 1, 1) \ F(SetNativeFlag, 1, 1) \
F(ThrowStrongModeTooFewArguments, 0, 1) \
F(IsConstructor, 1, 1) \ F(IsConstructor, 1, 1) \
F(SetInlineBuiltinFlag, 1, 1) \ F(SetInlineBuiltinFlag, 1, 1) \
F(FunctionBindArguments, 4, 1) \ F(FunctionBindArguments, 4, 1) \
......
...@@ -1642,6 +1642,23 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -1642,6 +1642,23 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
{ // Too few parameters: Actual < expected. { // Too few parameters: Actual < expected.
__ bind(&too_few); __ bind(&too_few);
// If the function is strong we need to throw an error.
Label weak_function;
__ movp(kScratchRegister,
FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ testb(FieldOperand(kScratchRegister,
SharedFunctionInfo::kStrongModeByteOffset),
Immediate(1 << SharedFunctionInfo::kStrongModeBitWithinByte));
__ j(equal, &weak_function, Label::kNear);
{
FrameScope frame(masm, StackFrame::MANUAL);
EnterArgumentsAdaptorFrame(masm);
__ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0);
}
__ bind(&weak_function);
EnterArgumentsAdaptorFrame(masm); EnterArgumentsAdaptorFrame(masm);
// Copy receiver and all actual arguments. // Copy receiver and all actual arguments.
......
...@@ -526,4 +526,32 @@ TEST(InlineNestedBuiltin) { ...@@ -526,4 +526,32 @@ TEST(InlineNestedBuiltin) {
T.CheckCall(T.true_value()); T.CheckCall(T.true_value());
} }
TEST(StrongModeArity) {
FLAG_turbo_deoptimization = true;
FLAG_strong_mode = true;
FunctionTester T(
"(function () {"
" function foo(x, y) { 'use strong'; return x; }"
" function bar(x, y) { return foo(x); }"
" return bar;"
"})();",
kInlineFlags);
T.CheckThrows(T.undefined(), T.undefined());
}
TEST(StrongModeArityOuter) {
FLAG_turbo_deoptimization = true;
FLAG_strong_mode = true;
FunctionTester T(
"(function () {"
" 'use strong';"
" function foo(x, y) { return x; }"
" function bar(x, y) { return foo(x); }"
" return bar;"
"})();",
kInlineFlags);
T.CheckThrows(T.undefined(), T.undefined());
}
#endif // V8_TURBOFAN_TARGET #endif // V8_TURBOFAN_TARGET
...@@ -21029,3 +21029,78 @@ TEST(SealHandleScopeNested) { ...@@ -21029,3 +21029,78 @@ TEST(SealHandleScopeNested) {
USE(obj); USE(obj);
} }
} }
TEST(StrongModeArityCallFromApi) {
i::FLAG_strong_mode = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<Function> fun;
{
v8::TryCatch try_catch;
fun = Local<Function>::Cast(CompileRun(
"function f(x) { 'use strong'; }"
"f"));
CHECK(!try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
fun->Call(v8::Undefined(isolate), 0, nullptr);
CHECK(try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
v8::Handle<Value> args[] = {v8_num(42)};
fun->Call(v8::Undefined(isolate), arraysize(args), args);
CHECK(!try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
v8::Handle<Value> args[] = {v8_num(42), v8_num(555)};
fun->Call(v8::Undefined(isolate), arraysize(args), args);
CHECK(!try_catch.HasCaught());
}
}
TEST(StrongModeArityCallFromApi2) {
i::FLAG_strong_mode = true;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<Function> fun;
{
v8::TryCatch try_catch;
fun = Local<Function>::Cast(CompileRun(
"'use strong';"
"function f(x) {}"
"f"));
CHECK(!try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
fun->Call(v8::Undefined(isolate), 0, nullptr);
CHECK(try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
v8::Handle<Value> args[] = {v8_num(42)};
fun->Call(v8::Undefined(isolate), arraysize(args), args);
CHECK(!try_catch.HasCaught());
}
{
v8::TryCatch try_catch;
v8::Handle<Value> args[] = {v8_num(42), v8_num(555)};
fun->Call(v8::Undefined(isolate), arraysize(args), args);
CHECK(!try_catch.HasCaught());
}
}
...@@ -211,13 +211,14 @@ function assertThrowsHelper(code) { ...@@ -211,13 +211,14 @@ function assertThrowsHelper(code) {
func2; func2;
function func4(p, ...rest) { p; rest; this; func2; } function func4(p, ...rest) { p; rest; this; func2; }
func4(); // TODO(arv): The arity checking is not correct with rest parameters.
func4(1, 2);
let func5 = (p1, p2) => { p1; p2; }; let func5 = (p1, p2) => { p1; p2; };
func5(); func5(1, 2);
let func5b = p1 => p1; let func5b = p1 => p1;
func5b(); func5b(1);
function func6() { function func6() {
var1, var2a, var2b, var2c; var1, var2a, var2b, var2c;
......
// 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: --strong-mode --harmony-arrow-functions --harmony-reflect
// Flags: --harmony-spreadcalls --allow-natives-syntax
'use strict';
function generateArguments(n, prefix) {
let a = [];
if (prefix) {
a.push(prefix);
}
for (let i = 0; i < n; i++) {
a.push(String(i));
}
return a.join(', ');
}
function generateParams(n) {
let a = [];
for (let i = 0; i < n; i++) {
a[i] = `p${i}`;
}
return a.join(', ');
}
function generateSpread(n) {
return `...[${generateArguments(n)}]`;
}
(function FunctionCall() {
for (let parameter_count = 0; parameter_count < 3; parameter_count++) {
let defs = [
`'use strong'; function f(${generateParams(parameter_count)}) {}`,
`'use strong'; function* f(${generateParams(parameter_count)}) {}`,
`'use strong'; let f = (${generateParams(parameter_count)}) => {}`,
`function f(${generateParams(parameter_count)}) { 'use strong'; }`,
`function* f(${generateParams(parameter_count)}) { 'use strong'; }`,
`let f = (${generateParams(parameter_count)}) => { 'use strong'; }`,
];
for (let def of defs) {
for (let argument_count = 0; argument_count < 3; argument_count++) {
let calls = [
`f(${generateArguments(argument_count)})`,
`f(${generateSpread(argument_count)})`,
`f.call(${generateArguments(argument_count, 'undefined')})`,
`f.call(undefined, ${generateSpread(argument_count)})`,
`f.apply(undefined, [${generateArguments(argument_count)}])`,
`f.bind(undefined)(${generateArguments(argument_count)})`,
`%_CallFunction(${generateArguments(argument_count, 'undefined')},
f)`,
`%Call(${generateArguments(argument_count, 'undefined')}, f)`,
`%Apply(f, undefined, [${generateArguments(argument_count)}], 0,
${argument_count})`,
];
for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`;
if (argument_count < parameter_count) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
let calls = [
`f.call()`,
`f.apply()`,
`f.apply(undefined)`,
];
for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`;
if (parameter_count > 0) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
}
})();
(function MethodCall() {
for (let parameter_count = 0; parameter_count < 3; parameter_count++) {
let defs = [
`let o = new class {
m(${generateParams(parameter_count)}) { 'use strong'; }
}`,
`let o = new class {
*m(${generateParams(parameter_count)}) { 'use strong'; }
}`,
`let o = { m(${generateParams(parameter_count)}) { 'use strong'; } }`,
`let o = { *m(${generateParams(parameter_count)}) { 'use strong'; } }`,
`'use strong';
let o = new class { m(${generateParams(parameter_count)}) {} }`,
`'use strong';
let o = new class { *m(${generateParams(parameter_count)}) {} }`,
`'use strong'; let o = { m(${generateParams(parameter_count)}) {} }`,
`'use strong'; let o = { *m(${generateParams(parameter_count)}) {} }`,
];
for (let def of defs) {
for (let argument_count = 0; argument_count < 3; argument_count++) {
let calls = [
`o.m(${generateArguments(argument_count)})`,
`o.m(${generateSpread(argument_count)})`,
`o.m.call(${generateArguments(argument_count, 'o')})`,
`o.m.call(o, ${generateSpread(argument_count)})`,
`o.m.apply(o, [${generateArguments(argument_count)}])`,
`o.m.bind(o)(${generateArguments(argument_count)})`,
`%_CallFunction(${generateArguments(argument_count, 'o')}, o.m)`,
`%Call(${generateArguments(argument_count, 'o')}, o.m)`,
`%Apply(o.m, o, [${generateArguments(argument_count)}], 0,
${argument_count})`,
];
for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`;
if (argument_count < parameter_count) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
let calls = [
`o.m.call()`,
`o.m.apply()`,
`o.m.apply(o)`,
];
for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`;
if (parameter_count > 0) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
}
})();
(function Constructor() {
for (let argument_count = 0; argument_count < 3; argument_count++) {
for (let parameter_count = 0; parameter_count < 3; parameter_count++) {
let defs = [
`'use strong';
class C { constructor(${generateParams(parameter_count)}) {} }`,
`'use strict';
class C {
constructor(${generateParams(parameter_count)}) { 'use strong'; }
}`,
];
for (let def of defs) {
let calls = [
`new C(${generateArguments(argument_count)})`,
`new C(${generateSpread(argument_count)})`,
`Reflect.construct(C, [${generateArguments(argument_count)}])`,
];
for (let call of calls) {
let code = `${def}; ${call};`;
if (argument_count < parameter_count) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
}
}
})();
(function DerivedConstructor() {
for (let genArgs of [generateArguments, generateSpread]) {
for (let argument_count = 0; argument_count < 3; argument_count++) {
for (let parameter_count = 0; parameter_count < 3; parameter_count++) {
let defs = [
`'use strong';
class B {
constructor(${generateParams(parameter_count)}) {}
}
class C extends B {
constructor() {
super(${genArgs(argument_count)});
}
}`,
`'use strict';
class B {
constructor(${generateParams(parameter_count)}) { 'use strong'; }
}
class C extends B {
constructor() {
super(${genArgs(argument_count)});
}
}`,
];
for (let def of defs) {
let code = `${def}; new C();`;
if (argument_count < parameter_count) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
}
}
})();
(function DerivedConstructorDefaultConstructorInDerivedClass() {
for (let genArgs of [generateArguments, generateSpread]) {
for (let argument_count = 0; argument_count < 3; argument_count++) {
for (let parameter_count = 0; parameter_count < 3; parameter_count++) {
let defs = [
`'use strong';
class B {
constructor(${generateParams(parameter_count)}) {}
}
class C extends B {}`,
`'use strict';
class B {
constructor(${generateParams(parameter_count)}) { 'use strong'; }
}
class C extends B {}`,
];
for (let def of defs) {
let code = `${def}; new C(${genArgs(argument_count)})`;
if (argument_count < parameter_count) {
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
}
}
}
}
}
})();
(function TestOptimized() {
function f(x, y) { 'use strong'; }
assertThrows(f, TypeError);
%OptimizeFunctionOnNextCall(f);
assertThrows(f, TypeError);
function g() {
f(1);
}
assertThrows(g, TypeError);
%OptimizeFunctionOnNextCall(g);
assertThrows(g, TypeError);
f(1, 2);
%OptimizeFunctionOnNextCall(f);
f(1, 2);
})();
(function TestOptimized2() {
'use strong';
function f(x, y) {}
assertThrows(f, TypeError);
%OptimizeFunctionOnNextCall(f);
assertThrows(f, TypeError);
function g() {
f(1);
}
assertThrows(g, TypeError);
%OptimizeFunctionOnNextCall(g);
assertThrows(g, TypeError);
f(1, 2);
%OptimizeFunctionOnNextCall(f);
f(1, 2);
})();
// https://code.google.com/p/v8/issues/detail?id=4077
// (function NoParametersSuper() {
// 'use strong';
//
// class B {
// m() {}
// }
// class D extends B {
// m0() { super.m(); }
// m1() { super.m(1); }
// s0() { super.m(); }
// s1() { super.m(1); }
// }
//
// new D().m0();
// new D().m1();
//
// new D().s0();
// new D().s1();
// })();
// https://code.google.com/p/v8/issues/detail?id=4077
// (function OneParamentSuper() {
// 'use strong';
//
// class B {
// m(x) {}
// }
// class D extends B {
// m0() { super.m(); }
// m1() { super.m(1); }
// m2() { super.m(1, 2); }
// s0() { super.m(...[]); }
// s1() { super.m(...[1]); }
// s2() { super.m(...[1, 2]); }
// }
//
// assertThrows(function() { new D().m0(); }, TypeError);
// new D().m1();
// new D().m2();
//
// assertThrows(function() { new D().s0(); }, TypeError);
// new D().s1();
// new D().s2();
// })();
// https://code.google.com/p/v8/issues/detail?id=4077
// (function TwoParametersSuper() {
// 'use strong';
//
// class B {
// m(x, y) {}
// }
// class D extends B {
// m0() { super.m(); }
// m1() { super.m(1); }
// m2() { super.m(1, 2); }
// m3() { super.m(1, 2, 3); }
// s0() { super.m(...[]); }
// s1() { super.m(...[1]); }
// s2() { super.m(...[1, 2]); }
// s3() { super.m(...[1, 2, 3]); }
// }
//
// assertThrows(function() { new D().m0(); }, TypeError);
// assertThrows(function() { new D().m1(); }, TypeError);
// new D().m2();
// new D().m3();
//
// assertThrows(function() { new D().s0(); }, TypeError);
// assertThrows(function() { new D().s1(); }, TypeError);
// new D().s2();
// new D().s3();
// })();
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