From cd3588d5823dcfa2575e6100a1d26ae48c913f56 Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Fri, 26 Aug 2011 13:53:00 +0000 Subject: [PATCH] Make (some) functions called from builtin functions use the callback's global as receiver. Changes GetGlobalReceiver() to GetDefaultReceiver(func) that returns undefined for strict and native functions, and the function's context's global proxy for "normal" functions. BUG=v8:1547 TEST=cctest/api-test/ForeignFunctionReceiver Review URL: http://codereview.chromium.org/7741042 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9030 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 33 ----------- src/array.js | 3 +- src/hydrogen.cc | 5 -- src/ia32/full-codegen-ia32.cc | 33 ----------- src/mips/full-codegen-mips.cc | 33 ----------- src/runtime.cc | 15 ++++- src/runtime.h | 5 +- src/string.js | 9 +-- src/x64/full-codegen-x64.cc | 33 ----------- test/cctest/test-api.cc | 104 ++++++++++++++++++++++++++++++++++ 10 files changed, 122 insertions(+), 151 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 97f7fea79..0973d0153 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -3580,39 +3580,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { } -void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList* args) { - ASSERT(args->length() == 1); - - // Load the function into r0. - VisitForAccumulatorValue(args->at(0)); - - // Prepare for the test. - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - // Test for strict mode function. - __ ldr(r1, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kCompilerHintsOffset)); - __ tst(r1, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ b(ne, if_true); - - // Test for native function. - __ tst(r1, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ b(ne, if_true); - - // Not native or strict-mode function. - __ b(if_false); - - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - context()->Plug(if_true, if_false); -} - - void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/src/array.js b/src/array.js index e6c13d954..a12fdc84b 100644 --- a/src/array.js +++ b/src/array.js @@ -742,8 +742,7 @@ function ArraySort(comparefn) { else return x < y ? -1 : 1; }; } - var receiver = - %_IsNativeOrStrictMode(comparefn) ? void 0 : %GetGlobalReceiver(); + var receiver = %GetDefaultReceiver(comparefn); function InsertionSort(a, from, to) { for (var i = from + 1; i < to; i++) { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index d321aacb0..a4fac71e6 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -6222,11 +6222,6 @@ void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) { } -void HGraphBuilder::GenerateIsNativeOrStrictMode(CallRuntime* call) { - return Bailout("inlined runtime function: IsNativeOrStrictMode"); -} - - #undef CHECK_BAILOUT #undef CHECK_ALIVE diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 07fd72582..3e42cc134 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -3633,39 +3633,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { } -void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList* args) { - ASSERT(args->length() == 1); - - // Load the function into eax. - VisitForAccumulatorValue(args->at(0)); - - // Prepare for the test. - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - // Test for strict mode function. - __ mov(ecx, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset), - 1 << SharedFunctionInfo::kStrictModeBitWithinByte); - __ j(not_equal, if_true); - - // Test for native function. - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset), - 1 << SharedFunctionInfo::kNativeBitWithinByte); - __ j(not_equal, if_true); - - // Not native or strict-mode function. - __ jmp(if_false); - - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - context()->Plug(if_true, if_false); -} - - void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index e1a23b4ed..9bacac453 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -3602,39 +3602,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { } -void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList* args) { - ASSERT(args->length() == 1); - - // Load the function into v0. - VisitForAccumulatorValue(args->at(0)); - - // Prepare for the test. - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - // Test for strict mode function. - __ lw(a1, FieldMemOperand(v0, JSFunction::kSharedFunctionInfoOffset)); - __ lw(a1, FieldMemOperand(a1, SharedFunctionInfo::kCompilerHintsOffset)); - __ And(at, a1, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ Branch(if_true, ne, at, Operand(zero_reg)); - - // Test for native function. - __ And(at, a1, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ Branch(if_true, ne, at, Operand(zero_reg)); - - // Not native or strict-mode function. - __ Branch(if_false); - - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - context()->Plug(if_true, if_false); -} - - void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/src/runtime.cc b/src/runtime.cc index aa8983f66..802fd6845 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1866,10 +1866,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_GetGlobalReceiver) { - // Returns a real global receiver, not one of builtins object. +RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultReceiver) { + NoHandleAllocation handle_free; + ASSERT(args.length() == 1); + CONVERT_CHECKED(JSFunction, function, args[0]); + SharedFunctionInfo* shared = function->shared(); + if (shared->native() || shared->strict_mode()) { + return isolate->heap()->undefined_value(); + } + // Returns undefined for strict or native functions, or + // the associated global receiver for "normal" functions. + Context* global_context = - isolate->context()->global()->global_context(); + function->context()->global()->global_context(); return global_context->global()->global_receiver(); } diff --git a/src/runtime.h b/src/runtime.h index 06d368454..91a19dfd4 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -65,7 +65,7 @@ namespace internal { F(ToSlowProperties, 1, 1) \ F(FinishArrayPrototypeSetup, 1, 1) \ F(SpecialArrayFunctions, 1, 1) \ - F(GetGlobalReceiver, 0, 1) \ + F(GetDefaultReceiver, 1, 1) \ \ F(GetPrototype, 1, 1) \ F(IsInPrototypeChain, 2, 1) \ @@ -491,8 +491,7 @@ namespace internal { F(IsRegExpEquivalent, 2, 1) \ F(HasCachedArrayIndex, 1, 1) \ F(GetCachedArrayIndex, 1, 1) \ - F(FastAsciiArrayJoin, 2, 1) \ - F(IsNativeOrStrictMode, 1, 1) + F(FastAsciiArrayJoin, 2, 1) // ---------------------------------------------------------------------------- diff --git a/src/string.js b/src/string.js index 08fccc4f0..a70eeade8 100644 --- a/src/string.js +++ b/src/string.js @@ -251,8 +251,7 @@ function StringReplace(search, replace) { // Compute the string to replace with. if (IS_FUNCTION(replace)) { - var receiver = - %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver(); + var receiver = %GetDefaultReceiver(replace); builder.add(%_CallFunction(receiver, search, start, @@ -420,8 +419,7 @@ function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) { var match_start = 0; var override = new InternalArray(null, 0, subject); - var receiver = - %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver(); + var receiver = %GetDefaultReceiver(replace); while (i < len) { var elem = res[i]; if (%_IsSmi(elem)) { @@ -478,8 +476,7 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { // No captures, only the match, which is always valid. var s = SubString(subject, index, endOfMatch); // Don't call directly to avoid exposing the built-in global object. - var receiver = - %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver(); + var receiver = %GetDefaultReceiver(replace); replacement = %_CallFunction(receiver, s, index, subject, replace); } else { diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 5567df2fa..ec71021b3 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -3518,39 +3518,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { } -void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList* args) { - ASSERT(args->length() == 1); - - // Load the function into rax. - VisitForAccumulatorValue(args->at(0)); - - // Prepare for the test. - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - // Test for strict mode function. - __ movq(rdx, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset)); - __ testb(FieldOperand(rdx, SharedFunctionInfo::kStrictModeByteOffset), - Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte)); - __ j(not_equal, if_true); - - // Test for native function. - __ testb(FieldOperand(rdx, SharedFunctionInfo::kNativeByteOffset), - Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); - __ j(not_equal, if_true); - - // Not native or strict-mode function. - __ jmp(if_false); - - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - context()->Plug(if_true, if_false); -} - - void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index a6201b5bb..f2af81eb7 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -15046,3 +15046,107 @@ THREADED_TEST(Regress93759) { context.Dispose(); } + + +static void TestReceiver(Local expected_result, + Local expected_receiver, + const char* code) { + Local result = CompileRun(code); + CHECK(result->IsObject()); + CHECK(expected_receiver->Equals(result->ToObject()->Get(1))); + CHECK(expected_result->Equals(result->ToObject()->Get(0))); +} + + +THREADED_TEST(ForeignFunctionReceiver) { + HandleScope scope; + + // Create two contexts with different "id" properties ('i' and 'o'). + // Call a function both from its own context and from a the foreign + // context, and see what "this" is bound to (returning both "this" + // and "this.id" for comparison). + + Persistent foreign_context = v8::Context::New(); + foreign_context->Enter(); + Local foreign_function = + CompileRun("function func() { return { 0: this.id, " + " 1: this, " + " toString: function() { " + " return this[0];" + " }" + " };" + "}" + "var id = 'i';" + "func;"); + CHECK(foreign_function->IsFunction()); + foreign_context->Exit(); + + LocalContext context; + + Local password = v8_str("Password"); + // Don't get hit by security checks when accessing foreign_context's + // global receiver (aka. global proxy). + context->SetSecurityToken(password); + foreign_context->SetSecurityToken(password); + + Local i = v8_str("i"); + Local o = v8_str("o"); + Local id = v8_str("id"); + + CompileRun("function ownfunc() { return { 0: this.id, " + " 1: this, " + " toString: function() { " + " return this[0];" + " }" + " };" + "}" + "var id = 'o';" + "ownfunc"); + context->Global()->Set(v8_str("func"), foreign_function); + + // Sanity check the contexts. + CHECK(i->Equals(foreign_context->Global()->Get(id))); + CHECK(o->Equals(context->Global()->Get(id))); + + // Checking local function's receiver. + // Calling function using its call/apply methods. + TestReceiver(o, context->Global(), "ownfunc.call()"); + TestReceiver(o, context->Global(), "ownfunc.apply()"); + // Making calls through built-in functions. + TestReceiver(o, context->Global(), "[1].map(ownfunc)[0]"); + CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/,ownfunc)[1]"))); + CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[1]"))); + CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[3]"))); + // Calling with environment record as base. + TestReceiver(o, context->Global(), "ownfunc()"); + // Calling with no base. + TestReceiver(o, context->Global(), "(1,ownfunc)()"); + + // Checking foreign function return value. + // Calling function using its call/apply methods. + TestReceiver(i, foreign_context->Global(), "func.call()"); + TestReceiver(i, foreign_context->Global(), "func.apply()"); + // Calling function using another context's call/apply methods. + TestReceiver(i, foreign_context->Global(), + "Function.prototype.call.call(func)"); + TestReceiver(i, foreign_context->Global(), + "Function.prototype.call.apply(func)"); + TestReceiver(i, foreign_context->Global(), + "Function.prototype.apply.call(func)"); + TestReceiver(i, foreign_context->Global(), + "Function.prototype.apply.apply(func)"); + // Making calls through built-in functions. + TestReceiver(i, foreign_context->Global(), "[1].map(func)[0]"); + // ToString(func()) is func()[0], i.e., the returned this.id. + CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/,func)[1]"))); + CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[1]"))); + CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[3]"))); + + // TODO(1547): Make the following also return "i". + // Calling with environment record as base. + TestReceiver(o, context->Global(), "func()"); + // Calling with no base. + TestReceiver(o, context->Global(), "(1,func)()"); + + foreign_context.Dispose(); +} -- 2.34.1