Implement support for reattaching a global object to a context. This
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 24 Mar 2010 13:24:46 +0000 (13:24 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 24 Mar 2010 13:24:46 +0000 (13:24 +0000)
can be used to use the same global object for different contexts at
different points in time.

Review URL: http://codereview.chromium.org/1207003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4253 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/bootstrapper.cc
src/bootstrapper.h
test/cctest/test-api.cc

index 206b0b0..7b42178 100644 (file)
@@ -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<Object> global_object);
+
   /** Creates a new context. */
   static Persistent<Context> New(
       ExtensionConfiguration* extensions = NULL,
index 2a87cda..d8e85b9 100644 (file)
@@ -3078,6 +3078,16 @@ void Context::DetachGlobal() {
 }
 
 
+void Context::ReattachGlobal(Handle<Object> global_object) {
+  if (IsDeadCheck("v8::Context::ReattachGlobal()")) return;
+  ENTER_V8;
+  i::Object** ctx = reinterpret_cast<i::Object**>(this);
+  i::Handle<i::Context> context =
+      i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
+  i::Bootstrapper::ReattachGlobal(context, Utils::OpenHandle(*global_object));
+}
+
+
 Local<v8::Object> ObjectTemplate::NewInstance() {
   ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
   LOG_API("ObjectTemplate::NewInstance");
index 3dc2470..8a9fa4b 100644 (file)
@@ -309,6 +309,17 @@ void Bootstrapper::DetachGlobal(Handle<Context> env) {
 }
 
 
+void Bootstrapper::ReattachGlobal(Handle<Context> env,
+                                  Handle<Object> global_object) {
+  ASSERT(global_object->IsJSGlobalProxy());
+  Handle<JSGlobalProxy> global = Handle<JSGlobalProxy>::cast(global_object);
+  env->global()->set_global_receiver(*global);
+  env->set_global_proxy(*global);
+  SetObjectPrototype(global, Handle<JSObject>(env->global()));
+  global->set_context(*env);
+}
+
+
 static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
                                           const char* name,
                                           InstanceType type,
index 72b438a..66b8ff4 100644 (file)
@@ -68,6 +68,9 @@ class Bootstrapper : public AllStatic {
   // Detach the environment from its outer global object.
   static void DetachGlobal(Handle<Context> env);
 
+  // Reattach an outer global object to an environment.
+  static void ReattachGlobal(Handle<Context> env, Handle<Object> global_object);
+
   // Traverses the pointers for memory management.
   static void Iterate(ObjectVisitor* v);
 
index 2eacf5a..e579143 100644 (file)
@@ -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<v8::Object> 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<Context> env2 = Context::New();
+
+  Local<Value> 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<Value> result = CompileRun("other.p");
+  CHECK(result->IsInt32());
+  CHECK_EQ(42, result->Int32Value());
+
+  // Hold on to global from env2 and detach global from env2.
+  Local<v8::Object> 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<Context> env3 =
+      Context::New(0, v8::Handle<v8::ObjectTemplate>(), 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<v8::Object> global,
                                Local<Value> name,
                                v8::AccessType type,