diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 519c04a2cf..9c7a42ab17 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -58,6 +58,16 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // -- sp[...]: constructor arguments // ----------------------------------- + Label non_function_call; + // Check that the function is not a smi. + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &non_function_call); + // Check that the function is a JSFunction. + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); + __ cmp(r2, Operand(JS_FUNCTION_TYPE)); + __ b(ne, &non_function_call); + // Enter a construct frame. __ EnterConstructFrame(); @@ -169,7 +179,17 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ LeaveConstructFrame(); __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); __ add(sp, sp, Operand(kPointerSize)); - __ mov(pc, Operand(lr)); + __ Jump(lr); + + // r0: number of arguments + // r1: called object + __ bind(&non_function_call); + + // Set expected number of arguments to zero (not changing r0). + __ mov(r2, Operand(0)); + __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); + __ Jump(Handle(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET); } @@ -235,7 +255,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Exit the JS frame and remove the parameters (except function), and return. // Respect ABI stack constraint. __ LeaveInternalFrame(); - __ mov(pc, lr); + __ Jump(lr); // r0: result } @@ -544,7 +564,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // Tear down the internal frame and remove function, receiver and args. __ LeaveInternalFrame(); __ add(sp, sp, Operand(3 * kPointerSize)); - __ mov(pc, lr); + __ Jump(lr); } @@ -663,14 +683,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Exit frame and return. LeaveArgumentsAdaptorFrame(masm); - __ mov(pc, lr); + __ Jump(lr); // ------------------------------------------- // Dont adapt arguments. // ------------------------------------------- __ bind(&dont_adapt_arguments); - __ mov(pc, r3); + __ Jump(r3); } diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 57e98c1579..1930a7c2f2 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -277,7 +277,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { frame_->Exit(); __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize)); - __ mov(pc, lr); + __ Jump(lr); } // Code generation state must be reset. @@ -5034,13 +5034,13 @@ void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) { // Nothing to do: The formal number of parameters has already been // passed in register r0 by calling function. Just return it. - __ mov(pc, lr); + __ Jump(lr); // Arguments adaptor case: Read the arguments length from the // adaptor frame and return it. __ bind(&adaptor); __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ mov(pc, lr); + __ Jump(lr); } @@ -5072,7 +5072,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { __ sub(r3, r0, r1); __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); __ ldr(r0, MemOperand(r3, kDisplacement)); - __ mov(pc, lr); + __ Jump(lr); // Arguments adaptor case: Check index against actual arguments // limit found in the arguments adaptor frame. Use unsigned @@ -5086,7 +5086,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { __ sub(r3, r0, r1); __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); __ ldr(r0, MemOperand(r3, kDisplacement)); - __ mov(pc, lr); + __ Jump(lr); // Slow-case: Handle non-smi or out-of-bounds access to arguments // by calling the runtime system. diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index a59b1d40dd..1b9621e3a5 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -4627,35 +4627,42 @@ THREADED_TEST(CallAsFunction) { Local value; CHECK(!try_catch.HasCaught()); - value = Script::Compile(v8_str("obj(42)"))->Run(); + value = CompileRun("obj(42)"); CHECK(!try_catch.HasCaught()); CHECK_EQ(42, value->Int32Value()); - value = Script::Compile(v8_str("(function(o){return o(49)})(obj)"))->Run(); + value = CompileRun("(function(o){return o(49)})(obj)"); CHECK(!try_catch.HasCaught()); CHECK_EQ(49, value->Int32Value()); // test special case of call as function - value = Script::Compile(v8_str("[obj]['0'](45)"))->Run(); + value = CompileRun("[obj]['0'](45)"); CHECK(!try_catch.HasCaught()); CHECK_EQ(45, value->Int32Value()); - value = Script::Compile(v8_str("obj.call = Function.prototype.call;" - "obj.call(null, 87)"))->Run(); + value = CompileRun("obj.call = Function.prototype.call;" + "obj.call(null, 87)"); CHECK(!try_catch.HasCaught()); CHECK_EQ(87, value->Int32Value()); // Regression tests for bug #1116356: Calling call through call/apply // must work for non-function receivers. const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; - value = Script::Compile(v8_str(apply_99))->Run(); + value = CompileRun(apply_99); CHECK(!try_catch.HasCaught()); CHECK_EQ(99, value->Int32Value()); const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; - value = Script::Compile(v8_str(call_17))->Run(); + value = CompileRun(call_17); CHECK(!try_catch.HasCaught()); CHECK_EQ(17, value->Int32Value()); + + // Check that the call-as-function handler can be called through + // new. Currently, there is no way to check in the call-as-function + // handler if it has been called through new or not. + value = CompileRun("new obj(42)"); + CHECK(!try_catch.HasCaught()); + CHECK_EQ(42, value->Int32Value()); }