From 10b8928e0e585caa984df948868cdc51ab99830e Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Wed, 20 May 2009 07:44:59 +0000 Subject: [PATCH] Expose the calling context through the API. In C++ callbacks, we can now get the context of the javascript code that called the callback. Review URL: http://codereview.chromium.org/113622 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2006 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 7 ++++++ src/api.cc | 7 ++++++ src/top.cc | 9 +++++++ src/top.h | 5 ++++ test/cctest/test-api.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+) diff --git a/include/v8.h b/include/v8.h index a4b73fd..321d4a1 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2251,6 +2251,13 @@ class V8EXPORT Context { static Local GetCurrent(); /** + * Returns the context of the calling JavaScript code. That is the + * context of the top-most JavaScript frame. If there are no + * JavaScript frames an empty handle is returned. + */ + static Local GetCalling(); + + /** * Sets the security token for the context. To access an object in * another context, the security tokens must match. */ diff --git a/src/api.cc b/src/api.cc index 943b5e1..c16920b 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2612,6 +2612,13 @@ v8::Local Context::GetCurrent() { } +v8::Local Context::GetCalling() { + if (IsDeadCheck("v8::Context::GetCalling()")) return Local(); + i::Handle context(i::Top::GetCallingGlobalContext()); + return Utils::ToLocal(context); +} + + v8::Local Context::Global() { if (IsDeadCheck("v8::Context::Global()")) return Local(); i::Object** ctx = reinterpret_cast(this); diff --git a/src/top.cc b/src/top.cc index 96f98a5..b5a0b94 100644 --- a/src/top.cc +++ b/src/top.cc @@ -881,6 +881,15 @@ Handle Top::global_context() { } +Handle Top::GetCallingGlobalContext() { + JavaScriptFrameIterator it; + if (it.done()) return Handle::null(); + JavaScriptFrame* frame = it.frame(); + Context* context = Context::cast(frame->context()); + return Handle(context->global_context()); +} + + Object* Top::LookupSpecialFunction(JSObject* receiver, JSObject* prototype, JSFunction* function) { diff --git a/src/top.h b/src/top.h index 1e5ec5a..e0cfdc3 100644 --- a/src/top.h +++ b/src/top.h @@ -255,8 +255,13 @@ class Top { return context()->global_proxy(); } + // Returns the current global context. static Handle global_context(); + // Returns the global context of the calling JavaScript code. That + // is, the global context of the top-most JavaScript frame. + static Handle GetCallingGlobalContext(); + static Handle builtins() { return Handle(thread_local_.context_->builtins()); } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 59e3e50..926823b 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -6608,3 +6608,67 @@ TEST(ForceSetWithInterceptor) { CHECK_EQ(1, force_set_set_count); CHECK_EQ(6, force_set_get_count); } + + +v8::Persistent calling_context0; +v8::Persistent calling_context1; +v8::Persistent calling_context2; + + +// Check that the call to the callback is initiated in +// calling_context2, the directly calling context is calling_context1 +// and the callback itself is in calling_context0. +static v8::Handle GetCallingContextCallback(const v8::Arguments& args) { + ApiTestFuzzer::Fuzz(); + CHECK(Context::GetCurrent() == calling_context0); + CHECK(Context::GetCalling() == calling_context1); + CHECK(Context::GetEntered() == calling_context2); + return v8::Integer::New(42); +} + + +THREADED_TEST(GetCallingContext) { + v8::HandleScope scope; + + calling_context0 = Context::New(); + calling_context1 = Context::New(); + calling_context2 = Context::New(); + + // Allow cross-domain access. + Local token = v8_str(""); + calling_context0->SetSecurityToken(token); + calling_context1->SetSecurityToken(token); + calling_context2->SetSecurityToken(token); + + // Create an object with a C++ callback in context0. + calling_context0->Enter(); + Local callback_templ = + v8::FunctionTemplate::New(GetCallingContextCallback); + calling_context0->Global()->Set(v8_str("callback"), + callback_templ->GetFunction()); + calling_context0->Exit(); + + // Expose context0 in context1 and setup a function that calls the + // callback function. + calling_context1->Enter(); + calling_context1->Global()->Set(v8_str("context0"), + calling_context0->Global()); + CompileRun("function f() { context0.callback() }"); + calling_context1->Exit(); + + // Expose context1 in context2 and call the callback function in + // context0 indirectly through f in context1. + calling_context2->Enter(); + calling_context2->Global()->Set(v8_str("context1"), + calling_context1->Global()); + CompileRun("context1.f()"); + calling_context2->Exit(); + + // Dispose the contexts to allow them to be garbage collected. + calling_context0.Dispose(); + calling_context1.Dispose(); + calling_context2.Dispose(); + calling_context0.Clear(); + calling_context1.Clear(); + calling_context2.Clear(); +} -- 2.7.4