diff --git a/include/v8.h b/include/v8.h index 206b0b0810..7b42178782 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2735,6 +2735,18 @@ class V8EXPORT Context { */ void DetachGlobal(); + /** + * Reattaches a global object to a context. This can be used to + * restore the connection between a global object and a context + * after DetachGlobal has been called. + * + * \param global_object The global object to reattach to the + * context. For this to work, the global object must be the global + * object that was associated with this context before a call to + * DetachGlobal. + */ + void ReattachGlobal(Handle global_object); + /** Creates a new context. */ static Persistent New( ExtensionConfiguration* extensions = NULL, diff --git a/src/api.cc b/src/api.cc index 2a87cda502..d8e85b9acd 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3078,6 +3078,16 @@ void Context::DetachGlobal() { } +void Context::ReattachGlobal(Handle global_object) { + if (IsDeadCheck("v8::Context::ReattachGlobal()")) return; + ENTER_V8; + i::Object** ctx = reinterpret_cast(this); + i::Handle context = + i::Handle::cast(i::Handle(ctx)); + i::Bootstrapper::ReattachGlobal(context, Utils::OpenHandle(*global_object)); +} + + Local ObjectTemplate::NewInstance() { ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local()); LOG_API("ObjectTemplate::NewInstance"); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 3dc24707ca..8a9fa4bf69 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -309,6 +309,17 @@ void Bootstrapper::DetachGlobal(Handle env) { } +void Bootstrapper::ReattachGlobal(Handle env, + Handle global_object) { + ASSERT(global_object->IsJSGlobalProxy()); + Handle global = Handle::cast(global_object); + env->global()->set_global_receiver(*global); + env->set_global_proxy(*global); + SetObjectPrototype(global, Handle(env->global())); + global->set_context(*env); +} + + static Handle InstallFunction(Handle target, const char* name, InstanceType type, diff --git a/src/bootstrapper.h b/src/bootstrapper.h index 72b438af9f..66b8ff478e 100644 --- a/src/bootstrapper.h +++ b/src/bootstrapper.h @@ -68,6 +68,9 @@ class Bootstrapper : public AllStatic { // Detach the environment from its outer global object. static void DetachGlobal(Handle env); + // Reattach an outer global object to an environment. + static void ReattachGlobal(Handle env, Handle global_object); + // Traverses the pointers for memory management. static void Iterate(ObjectVisitor* v); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 2eacf5a755..e57914370d 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -4465,7 +4465,7 @@ TEST(ContextDetachGlobal) { // Enter env2 env2->Enter(); - // Create a function in env1 + // Create a function in env2 and add a reference to it in env1. Local global2 = env2->Global(); global2->Set(v8_str("prop"), v8::Integer::New(1)); CompileRun("function getProp() {return prop;}"); @@ -4473,7 +4473,7 @@ TEST(ContextDetachGlobal) { env1->Global()->Set(v8_str("getProp"), global2->Get(v8_str("getProp"))); - // Detach env1's global, and reuse the global object of env1 + // Detach env2's global, and reuse the global object of env2 env2->Exit(); env2->DetachGlobal(); // env2 has a new global object. @@ -4513,6 +4513,85 @@ TEST(ContextDetachGlobal) { } +TEST(DetachAndReattachGlobal) { + v8::HandleScope scope; + LocalContext env1; + + // Create second environment. + v8::Persistent env2 = Context::New(); + + Local foo = v8_str("foo"); + + // Set same security token for env1 and env2. + env1->SetSecurityToken(foo); + env2->SetSecurityToken(foo); + + // Create a property on the global object in env2. + { + v8::Context::Scope scope(env2); + env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); + } + + // Create a reference to env2 global from env1 global. + env1->Global()->Set(v8_str("other"), env2->Global()); + + // Check that we have access to other.p in env2 from env1. + Local result = CompileRun("other.p"); + CHECK(result->IsInt32()); + CHECK_EQ(42, result->Int32Value()); + + // Hold on to global from env2 and detach global from env2. + Local global2 = env2->Global(); + env2->DetachGlobal(); + + // Check that the global has been detached. No other.p property can + // be found. + result = CompileRun("other.p"); + CHECK(result->IsUndefined()); + + // Reuse global2 for env3. + v8::Persistent env3 = + Context::New(0, v8::Handle(), global2); + CHECK_EQ(global2, env3->Global()); + + // Start by using the same security token for env3 as for env1 and env2. + env3->SetSecurityToken(foo); + + // Create a property on the global object in env3. + { + v8::Context::Scope scope(env3); + env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); + } + + // Check that other.p is now the property in env3 and that we have access. + result = CompileRun("other.p"); + CHECK(result->IsInt32()); + CHECK_EQ(24, result->Int32Value()); + + // Change security token for env3 to something different from env1 and env2. + env3->SetSecurityToken(v8_str("bar")); + + // Check that we do not have access to other.p in env1. |other| is now + // the global object for env3 which has a different security token, + // so access should be blocked. + result = CompileRun("other.p"); + CHECK(result->IsUndefined()); + + // Detach the global for env3 and reattach it to env2. + env3->DetachGlobal(); + env2->ReattachGlobal(global2); + + // Check that we have access to other.p again in env1. |other| is now + // the global object for env2 which has the same security token as env1. + result = CompileRun("other.p"); + CHECK(result->IsInt32()); + CHECK_EQ(42, result->Int32Value()); + + env2.Dispose(); + env3.Dispose(); +} + + static bool NamedAccessBlocker(Local global, Local name, v8::AccessType type,