Do not enter contexts implicitly

Blink uses Isolate::GetEnteredContext() to implement HTML's "entry
context" concept, and thus depends on it not being changed except
explicitly (by Blink.) To support this, stop entering contexts
implicitly in all external API entry points; rather just set the
context as current. The only thing that changes the entered context
is now Context::Enter()/Context::Exit() (and Context::Scope.)

BUG=v8:6307

Review-Url: https://codereview.chromium.org/2862483003
Cr-Commit-Position: refs/heads/master@{#45064}
diff --git a/src/api.cc b/src/api.cc
index 9475084..1921985 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -232,17 +232,20 @@
       i::Handle<i::Context> env = Utils::OpenHandle(*context);
       i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
       if (isolate->context() != nullptr &&
-          isolate->context()->native_context() == env->native_context() &&
-          impl->LastEnteredContextWas(env)) {
+          isolate->context()->native_context() == env->native_context()) {
         context_ = Local<Context>();
       } else {
-        context_->Enter();
+        impl->SaveContext(isolate->context());
+        isolate->set_context(*env);
       }
     }
     if (do_callback) isolate_->FireBeforeCallEnteredCallback();
   }
   ~CallDepthScope() {
-    if (!context_.IsEmpty()) context_->Exit();
+    if (!context_.IsEmpty()) {
+      i::HandleScopeImplementer* impl = isolate_->handle_scope_implementer();
+      isolate_->set_context(impl->RestoreContext());
+    }
     if (!escaped_) isolate_->handle_scope_implementer()->DecrementCallDepth();
     if (do_callback) isolate_->FireCallCompletedCallback();
 #ifdef DEBUG
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index c1d7670..c3d45cc 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -26601,3 +26601,69 @@
   }
   isolate->Dispose();
 }
+
+enum ContextId { EnteredContext, CurrentContext };
+
+void CheckContexts(v8::Isolate* isolate) {
+  CHECK_EQ(CurrentContext, isolate->GetCurrentContext()
+                               ->GetEmbedderData(1)
+                               .As<v8::Integer>()
+                               ->Value());
+  CHECK_EQ(EnteredContext, isolate->GetEnteredContext()
+                               ->GetEmbedderData(1)
+                               .As<v8::Integer>()
+                               ->Value());
+}
+
+void ContextCheckGetter(Local<String> name,
+                        const v8::PropertyCallbackInfo<v8::Value>& info) {
+  CheckContexts(info.GetIsolate());
+  info.GetReturnValue().Set(true);
+}
+
+void ContextCheckSetter(Local<String> name, Local<Value>,
+                        const v8::PropertyCallbackInfo<void>& info) {
+  CheckContexts(info.GetIsolate());
+}
+
+void ContextCheckToString(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  CheckContexts(info.GetIsolate());
+  info.GetReturnValue().Set(v8_str("foo"));
+}
+
+TEST(CorrectEnteredContext) {
+  v8::HandleScope scope(CcTest::isolate());
+
+  LocalContext currentContext;
+  currentContext->SetEmbedderData(
+      1, v8::Integer::New(currentContext->GetIsolate(), CurrentContext));
+  LocalContext enteredContext;
+  enteredContext->SetEmbedderData(
+      1, v8::Integer::New(enteredContext->GetIsolate(), EnteredContext));
+
+  v8::Context::Scope contextScope(enteredContext.local());
+
+  v8::Local<v8::ObjectTemplate> object_template =
+      ObjectTemplate::New(currentContext->GetIsolate());
+  object_template->SetAccessor(v8_str("p"), &ContextCheckGetter,
+                               &ContextCheckSetter);
+
+  v8::Local<v8::Object> object =
+      object_template->NewInstance(currentContext.local()).ToLocalChecked();
+
+  object->Get(currentContext.local(), v8_str("p")).ToLocalChecked();
+  object->Set(currentContext.local(), v8_str("p"), v8_int(0)).FromJust();
+
+  v8::Local<v8::Function> to_string =
+      v8::Function::New(currentContext.local(), ContextCheckToString)
+          .ToLocalChecked();
+
+  to_string->Call(currentContext.local(), object, 0, nullptr).ToLocalChecked();
+
+  object
+      ->CreateDataProperty(currentContext.local(), v8_str("toString"),
+                           to_string)
+      .FromJust();
+
+  object->ToString(currentContext.local()).ToLocalChecked();
+}