Commit 692cccce authored by dcheng's avatar dcheng Committed by Commit bot

Make instance checks understand remote contexts.

https://crrev.com/2500363002 updated FunctionTemplate::HasInstance to
follow the hidden prototype chain of a global proxy to the global
object. However, remote contexts don't have a global object to check;
instead, teach the instance check knows about the conventions of
global proxy setup and have it also check the constructor's prototype.

Similarly, also teach Object::FindInstanceInPrototypeChain about the
unusual conventions for remote contexts.

BUG=527190

Review-Url: https://codereview.chromium.org/2698683003
Cr-Commit-Position: refs/heads/master@{#43263}
parent d1d4b9ce
......@@ -4490,16 +4490,51 @@ bool v8::Object::SetPrototype(Local<Value> value) {
return SetPrototype(context, value).FromMaybe(false);
}
static bool HasInstanceInGlobalProxy(i::JSGlobalProxy* global_proxy,
i::FunctionTemplateInfo* target_template) {
auto* constructor_object = global_proxy->map()->GetConstructor();
if (!constructor_object->IsJSFunction()) return false;
auto* constructor = i::JSFunction::cast(constructor_object);
if (!constructor->shared()->function_data()->IsFunctionTemplateInfo())
return false;
auto* proxy_constructor_template =
i::FunctionTemplateInfo::cast(constructor->shared()->function_data());
if (!proxy_constructor_template->prototype_template()->IsObjectTemplateInfo())
return false;
auto* global_template = i::ObjectTemplateInfo::cast(
proxy_constructor_template->prototype_template());
// Iterate through the chain of inheriting function templates to
// see if the required one occurs.
for (i::Object* type = global_template->constructor();
type->IsFunctionTemplateInfo();
type = i::FunctionTemplateInfo::cast(type)->parent_template()) {
if (type == target_template) return true;
}
// Didn't find the required type in the inheritance chain.
return false;
}
Local<Object> v8::Object::FindInstanceInPrototypeChain(
v8::Local<FunctionTemplate> tmpl) {
auto isolate = Utils::OpenHandle(this)->GetIsolate();
i::PrototypeIterator iter(isolate, *Utils::OpenHandle(this),
i::kStartAtReceiver);
auto self = Utils::OpenHandle(this);
auto isolate = self->GetIsolate();
i::PrototypeIterator iter(isolate, *self, i::kStartAtReceiver);
auto tmpl_info = *Utils::OpenHandle(*tmpl);
while (!tmpl_info->IsTemplateFor(iter.GetCurrent<i::JSObject>())) {
iter.Advance();
if (iter.IsAtEnd()) return Local<Object>();
if (iter.IsAtEnd()) {
// Normally, a standard prototype walk is sufficient; however, global
// proxies aren't directly constructed with the supplied template.
// Normally, this is not a problem, because the prototype chain includes
// the global object; however, a remote context has no global object.
if (self->IsJSGlobalProxy() &&
HasInstanceInGlobalProxy(i::JSGlobalProxy::cast(*self), tmpl_info))
return Utils::ToLocal(self);
return Local<Object>();
}
if (!iter.GetCurrent()->IsJSObject()) return Local<Object>();
}
// IsTemplateFor() ensures that iter.GetCurrent() can't be a Proxy here.
......@@ -6567,10 +6602,12 @@ bool FunctionTemplate::HasInstance(v8::Local<v8::Value> value) {
return true;
}
if (obj->IsJSGlobalProxy()) {
// If it's a global proxy object, then test with the global object.
i::PrototypeIterator iter(i::JSObject::cast(*obj)->map());
if (iter.IsAtEnd()) return false;
return self->IsTemplateFor(iter.GetCurrent<i::JSGlobalObject>());
auto* global_proxy = i::JSGlobalProxy::cast(*obj);
// For global proxies, check the constructor's prototype instead. Remote
// global proxies have no global object to perform instance checks on, but
// the constructor's prototype's constructor corresponds to the original
// template used to create the context.
return HasInstanceInGlobalProxy(global_proxy, *self);
}
return false;
}
......
......@@ -12,6 +12,7 @@ v8_executable("unittests") {
"../../testing/gtest-support.h",
"api/exception-unittest.cc",
"api/isolate-unittest.cc",
"api/remote-object-unittest.cc",
"api/v8-object-unittest.cc",
"base/atomic-utils-unittest.cc",
"base/bits-unittest.cc",
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/gtest/include/gtest/gtest.h"
#include "include/v8.h"
#include "test/unittests/test-utils.h"
namespace v8 {
typedef TestWithIsolate RemoteObjectTest;
namespace {
bool AccessCheck(Local<Context> accessing_context,
Local<Object> accessed_object, Local<Value> data) {
return false;
}
void NamedGetter(Local<Name> property,
const PropertyCallbackInfo<Value>& info) {}
void Constructor(const FunctionCallbackInfo<Value>& info) {
ASSERT_TRUE(info.IsConstructCall());
}
} // namespace
TEST_F(RemoteObjectTest, RemoteContextInstanceChecks) {
Local<FunctionTemplate> parent_template =
FunctionTemplate::New(isolate(), Constructor);
Local<FunctionTemplate> constructor_template =
FunctionTemplate::New(isolate(), Constructor);
constructor_template->InstanceTemplate()->SetAccessCheckCallbackAndHandler(
AccessCheck, NamedPropertyHandlerConfiguration(NamedGetter),
IndexedPropertyHandlerConfiguration());
constructor_template->Inherit(parent_template);
Local<Object> remote_context =
Context::NewRemoteContext(isolate(),
constructor_template->InstanceTemplate())
.ToLocalChecked();
EXPECT_TRUE(parent_template->HasInstance(remote_context));
EXPECT_TRUE(constructor_template->HasInstance(remote_context));
EXPECT_EQ(remote_context,
remote_context->FindInstanceInPrototypeChain(parent_template));
EXPECT_EQ(remote_context,
remote_context->FindInstanceInPrototypeChain(constructor_template));
}
} // namespace v8
......@@ -10,6 +10,7 @@
'unittests_sources': [ ### gcmole(all) ###
'api/exception-unittest.cc',
'api/isolate-unittest.cc',
'api/remote-object-unittest.cc',
'api/v8-object-unittest.cc',
'base/atomic-utils-unittest.cc',
'base/bits-unittest.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment