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;
......
This diff is collapsed.
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