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
This commit is contained in:
lrn@chromium.org 2011-08-26 13:53:00 +00:00
parent daf8135ba6
commit cd3588d582
10 changed files with 122 additions and 151 deletions

View File

@ -3580,39 +3580,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList<Expression*>* 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<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {

View File

@ -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++) {

View File

@ -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

View File

@ -3633,39 +3633,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList<Expression*>* 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<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {

View File

@ -3602,39 +3602,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList<Expression*>* 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<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {

View File

@ -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();
}

View File

@ -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)
// ----------------------------------------------------------------------------

View File

@ -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 {

View File

@ -3518,39 +3518,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsNativeOrStrictMode(ZoneList<Expression*>* 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<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {

View File

@ -15046,3 +15046,107 @@ THREADED_TEST(Regress93759) {
context.Dispose();
}
static void TestReceiver(Local<Value> expected_result,
Local<Value> expected_receiver,
const char* code) {
Local<Value> 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<Context> foreign_context = v8::Context::New();
foreign_context->Enter();
Local<Value> 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<String> 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<String> i = v8_str("i");
Local<String> o = v8_str("o");
Local<String> 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();
}