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:
parent
daf8135ba6
commit
cd3588d582
@ -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) == '_') {
|
||||
|
@ -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++) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) == '_') {
|
||||
|
@ -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) == '_') {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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 {
|
||||
|
@ -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) == '_') {
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user