Commit c34cc7a6 authored by jochen's avatar jochen Committed by Commit bot

Optionally invoke an interceptor on failed access checks

This superseeds all-can-read/all-can-write properties

BUG=chromium:618305
R=verwaest@chromium.org

Review-Url: https://codereview.chromium.org/2087823002
Cr-Commit-Position: refs/heads/master@{#37286}
parent 2db846d5
......@@ -4800,6 +4800,19 @@ class V8_EXPORT ObjectTemplate : public Template {
*/
void SetAccessCheckCallback(AccessCheckCallback callback,
Local<Value> data = Local<Value>());
/**
* Like SetAccessCheckCallback but invokes an interceptor on failed access
* checks instead of looking up all-can-read properties. You can only use
* either this method or SetAccessCheckCallback, but not both at the same
* time.
*/
void SetAccessCheckCallbackAndHandler(
AccessCheckCallback callback,
const NamedPropertyHandlerConfiguration& named_handler,
const IndexedPropertyHandlerConfiguration& indexed_handler,
Local<Value> data = Local<Value>());
/**
* Gets the number of internal fields for objects generated from
* this template.
......
......@@ -1535,20 +1535,12 @@ void ObjectTemplate::SetAccessor(v8::Local<Name> name,
signature, i::FLAG_disable_old_api_accessors);
}
template <typename Getter, typename Setter, typename Query, typename Deleter,
typename Enumerator>
static void ObjectTemplateSetNamedPropertyHandler(ObjectTemplate* templ,
Getter getter, Setter setter,
Query query, Deleter remover,
Enumerator enumerator,
Local<Value> data,
PropertyHandlerFlags flags) {
i::Isolate* isolate = Utils::OpenHandle(templ)->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
auto cons = EnsureConstructor(isolate, templ);
EnsureNotInstantiated(cons, "ObjectTemplateSetNamedPropertyHandler");
static i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
i::Isolate* isolate, Getter getter, Setter setter, Query query,
Deleter remover, Enumerator enumerator, Local<Value> data,
PropertyHandlerFlags flags) {
auto obj = i::Handle<i::InterceptorInfo>::cast(
isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE));
obj->set_flags(0);
......@@ -1570,6 +1562,24 @@ static void ObjectTemplateSetNamedPropertyHandler(ObjectTemplate* templ,
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
}
obj->set_data(*Utils::OpenHandle(*data));
return obj;
}
template <typename Getter, typename Setter, typename Query, typename Deleter,
typename Enumerator>
static void ObjectTemplateSetNamedPropertyHandler(ObjectTemplate* templ,
Getter getter, Setter setter,
Query query, Deleter remover,
Enumerator enumerator,
Local<Value> data,
PropertyHandlerFlags flags) {
i::Isolate* isolate = Utils::OpenHandle(templ)->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
auto cons = EnsureConstructor(isolate, templ);
EnsureNotInstantiated(cons, "ObjectTemplateSetNamedPropertyHandler");
auto obj = CreateInterceptorInfo(isolate, getter, setter, query, remover,
enumerator, data, flags);
cons->set_named_property_handler(*obj);
}
......@@ -1616,6 +1626,8 @@ void ObjectTemplate::SetAccessCheckCallback(AccessCheckCallback callback,
i::Handle<i::AccessCheckInfo>::cast(struct_info);
SET_FIELD_WRAPPED(info, set_callback, callback);
info->set_named_interceptor(nullptr);
info->set_indexed_interceptor(nullptr);
if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
......@@ -1626,32 +1638,54 @@ void ObjectTemplate::SetAccessCheckCallback(AccessCheckCallback callback,
cons->set_needs_access_check(true);
}
void ObjectTemplate::SetHandler(
const IndexedPropertyHandlerConfiguration& config) {
void ObjectTemplate::SetAccessCheckCallbackAndHandler(
AccessCheckCallback callback,
const NamedPropertyHandlerConfiguration& named_handler,
const IndexedPropertyHandlerConfiguration& indexed_handler,
Local<Value> data) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
auto cons = EnsureConstructor(isolate, this);
EnsureNotInstantiated(cons, "v8::ObjectTemplate::SetHandler");
auto obj = i::Handle<i::InterceptorInfo>::cast(
isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE));
obj->set_flags(0);
EnsureNotInstantiated(
cons, "v8::ObjectTemplate::SetAccessCheckCallbackWithHandler");
if (config.getter != 0) SET_FIELD_WRAPPED(obj, set_getter, config.getter);
if (config.setter != 0) SET_FIELD_WRAPPED(obj, set_setter, config.setter);
if (config.query != 0) SET_FIELD_WRAPPED(obj, set_query, config.query);
if (config.deleter != 0) SET_FIELD_WRAPPED(obj, set_deleter, config.deleter);
if (config.enumerator != 0) {
SET_FIELD_WRAPPED(obj, set_enumerator, config.enumerator);
}
obj->set_all_can_read(static_cast<int>(config.flags) &
static_cast<int>(PropertyHandlerFlags::kAllCanRead));
i::Handle<i::Struct> struct_info =
isolate->factory()->NewStruct(i::ACCESS_CHECK_INFO_TYPE);
i::Handle<i::AccessCheckInfo> info =
i::Handle<i::AccessCheckInfo>::cast(struct_info);
SET_FIELD_WRAPPED(info, set_callback, callback);
auto named_interceptor = CreateInterceptorInfo(
isolate, named_handler.getter, named_handler.setter, named_handler.query,
named_handler.deleter, named_handler.enumerator, named_handler.data,
named_handler.flags);
info->set_named_interceptor(*named_interceptor);
auto indexed_interceptor = CreateInterceptorInfo(
isolate, indexed_handler.getter, indexed_handler.setter,
indexed_handler.query, indexed_handler.deleter,
indexed_handler.enumerator, indexed_handler.data, indexed_handler.flags);
info->set_indexed_interceptor(*indexed_interceptor);
v8::Local<v8::Value> data = config.data;
if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
}
obj->set_data(*Utils::OpenHandle(*data));
info->set_data(*Utils::OpenHandle(*data));
cons->set_access_check_info(*info);
cons->set_needs_access_check(true);
}
void ObjectTemplate::SetHandler(
const IndexedPropertyHandlerConfiguration& config) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
auto cons = EnsureConstructor(isolate, this);
EnsureNotInstantiated(cons, "v8::ObjectTemplate::SetHandler");
auto obj = CreateInterceptorInfo(
isolate, config.getter, config.setter, config.query, config.deleter,
config.enumerator, config.data, config.flags);
cons->set_indexed_property_handler(*obj);
}
......
......@@ -802,21 +802,6 @@ void Isolate::SetFailedAccessCheckCallback(
}
static inline AccessCheckInfo* GetAccessCheckInfo(Isolate* isolate,
Handle<JSObject> receiver) {
Object* maybe_constructor = receiver->map()->GetConstructor();
if (!maybe_constructor->IsJSFunction()) return NULL;
JSFunction* constructor = JSFunction::cast(maybe_constructor);
if (!constructor->shared()->IsApiFunction()) return NULL;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj->IsUndefined(isolate)) return NULL;
return AccessCheckInfo::cast(data_obj);
}
void Isolate::ReportFailedAccessCheck(Handle<JSObject> receiver) {
if (!thread_local_top()->failed_access_check_callback_) {
return ScheduleThrow(*factory()->NewTypeError(MessageTemplate::kNoAccess));
......@@ -829,7 +814,7 @@ void Isolate::ReportFailedAccessCheck(Handle<JSObject> receiver) {
HandleScope scope(this);
Handle<Object> data;
{ DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info = GetAccessCheckInfo(this, receiver);
AccessCheckInfo* access_check_info = AccessCheckInfo::Get(this, receiver);
if (!access_check_info) {
AllowHeapAllocation doesnt_matter_anymore;
return ScheduleThrow(
......@@ -878,7 +863,7 @@ bool Isolate::MayAccess(Handle<Context> accessing_context,
Handle<Object> data;
v8::AccessCheckCallback callback = nullptr;
{ DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info = GetAccessCheckInfo(this, receiver);
AccessCheckInfo* access_check_info = AccessCheckInfo::Get(this, receiver);
if (!access_check_info) return false;
Object* fun_obj = access_check_info->callback();
callback = v8::ToCData<v8::AccessCheckCallback>(fun_obj);
......
......@@ -429,6 +429,29 @@ namespace {
enum IndexedOrNamed { kIndexed, kNamed };
// Returns |true| on success, |nothing| on exception.
template <class Callback, IndexedOrNamed type>
Maybe<bool> CollectInterceptorKeysInternal(Handle<JSReceiver> receiver,
Handle<JSObject> object,
Handle<InterceptorInfo> interceptor,
KeyAccumulator* accumulator) {
Isolate* isolate = accumulator->isolate();
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*object, Object::DONT_THROW);
Handle<JSObject> result;
if (!interceptor->enumerator()->IsUndefined(isolate)) {
Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
: "interceptor-named-enum";
LOG(isolate, ApiObjectAccess(log_tag, *object));
result = args.Call(enum_fun);
}
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
if (result.is_null()) return Just(true);
accumulator->AddKeys(
result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
return Just(true);
}
template <class Callback, IndexedOrNamed type>
Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
Handle<JSObject> object,
......@@ -447,21 +470,8 @@ Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
!interceptor->all_can_read()) {
return Just(true);
}
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*object, Object::DONT_THROW);
Handle<JSObject> result;
if (!interceptor->enumerator()->IsUndefined(isolate)) {
Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
: "interceptor-named-enum";
LOG(isolate, ApiObjectAccess(log_tag, *object));
result = args.Call(enum_fun);
}
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
if (result.is_null()) return Just(true);
accumulator->AddKeys(
result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
return Just(true);
return CollectInterceptorKeysInternal<Callback, type>(
receiver, object, interceptor, accumulator);
}
} // namespace
......@@ -539,6 +549,29 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
kNamed>(receiver, object, this);
}
Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
Handle<JSObject> object) {
MAYBE_RETURN(
(CollectInterceptorKeysInternal<v8::IndexedPropertyEnumeratorCallback,
kIndexed>(
receiver, object,
handle(
InterceptorInfo::cast(access_check_info->indexed_interceptor()),
isolate_),
this)),
Nothing<bool>());
MAYBE_RETURN(
(CollectInterceptorKeysInternal<
v8::GenericNamedPropertyEnumeratorCallback, kNamed>(
receiver, object,
handle(InterceptorInfo::cast(access_check_info->named_interceptor()),
isolate_),
this)),
Nothing<bool>());
return Just(true);
}
// Returns |true| on success, |false| if prototype walking should be stopped,
// |nothing| if an exception was thrown.
Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
......@@ -553,6 +586,20 @@ Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
}
// ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
DCHECK(KeyCollectionMode::kOwnOnly == mode_);
Handle<AccessCheckInfo> access_check_info;
{
DisallowHeapAllocation no_gc;
AccessCheckInfo* maybe_info = AccessCheckInfo::Get(isolate_, object);
if (maybe_info) access_check_info = handle(maybe_info, isolate_);
}
// We always have both kinds of interceptors or none.
if (!access_check_info.is_null() &&
access_check_info->named_interceptor()) {
MAYBE_RETURN(CollectAccessCheckInterceptorKeys(access_check_info,
receiver, object),
Nothing<bool>());
return Just(false);
}
filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
}
MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());
......
......@@ -49,6 +49,9 @@ class KeyAccumulator final BASE_EMBEDDED {
Handle<JSObject> object);
Maybe<bool> CollectOwnPropertyNames(Handle<JSReceiver> receiver,
Handle<JSObject> object);
Maybe<bool> CollectAccessCheckInterceptorKeys(
Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
Handle<JSObject> object);
static Handle<FixedArray> GetEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object);
......
......@@ -757,5 +757,21 @@ LookupIterator::State LookupIterator::LookupInRegularHolder(
return state_;
}
Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
const {
DCHECK_EQ(ACCESS_CHECK, state_);
DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info =
AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
if (access_check_info) {
Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
: access_check_info->named_interceptor();
if (interceptor) {
return handle(InterceptorInfo::cast(interceptor), isolate_);
}
}
return Handle<InterceptorInfo>();
}
} // namespace internal
} // namespace v8
......@@ -257,6 +257,7 @@ class LookupIterator final BASE_EMBEDDED {
: GetInterceptor<false>(JSObject::cast(*holder_));
return handle(result, isolate_);
}
Handle<InterceptorInfo> GetInterceptorForFailedAccessCheck() const;
Handle<Object> GetDataValue() const;
void WriteDataValue(Handle<Object> value);
inline void UpdateProtector() {
......
......@@ -937,6 +937,8 @@ void AccessorPair::AccessorPairVerify() {
void AccessCheckInfo::AccessCheckInfoVerify() {
CHECK(IsAccessCheckInfo());
VerifyPointer(callback());
VerifyPointer(named_interceptor());
VerifyPointer(indexed_interceptor());
VerifyPointer(data());
}
......
......@@ -5527,6 +5527,9 @@ ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
ACCESSORS(AccessCheckInfo, callback, Object, kCallbackOffset)
ACCESSORS(AccessCheckInfo, named_interceptor, Object, kNamedInterceptorOffset)
ACCESSORS(AccessCheckInfo, indexed_interceptor, Object,
kIndexedInterceptorOffset)
ACCESSORS(AccessCheckInfo, data, Object, kDataOffset)
ACCESSORS(InterceptorInfo, getter, Object, kGetterOffset)
......
......@@ -1086,6 +1086,8 @@ void AccessorPair::AccessorPairPrint(std::ostream& os) { // NOLINT
void AccessCheckInfo::AccessCheckInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "AccessCheckInfo");
os << "\n - callback: " << Brief(callback());
os << "\n - named_interceptor: " << Brief(named_interceptor());
os << "\n - indexed_interceptor: " << Brief(indexed_interceptor());
os << "\n - data: " << Brief(data());
os << "\n";
}
......
This diff is collapsed.
......@@ -10406,6 +10406,8 @@ class AccessorPair: public Struct {
class AccessCheckInfo: public Struct {
public:
DECL_ACCESSORS(callback, Object)
DECL_ACCESSORS(named_interceptor, Object)
DECL_ACCESSORS(indexed_interceptor, Object)
DECL_ACCESSORS(data, Object)
DECLARE_CAST(AccessCheckInfo)
......@@ -10414,8 +10416,13 @@ class AccessCheckInfo: public Struct {
DECLARE_PRINTER(AccessCheckInfo)
DECLARE_VERIFIER(AccessCheckInfo)
static AccessCheckInfo* Get(Isolate* isolate, Handle<JSObject> receiver);
static const int kCallbackOffset = HeapObject::kHeaderSize;
static const int kDataOffset = kCallbackOffset + kPointerSize;
static const int kNamedInterceptorOffset = kCallbackOffset + kPointerSize;
static const int kIndexedInterceptorOffset =
kNamedInterceptorOffset + kPointerSize;
static const int kDataOffset = kIndexedInterceptorOffset + kPointerSize;
static const int kSize = kDataOffset + kPointerSize;
private:
......
......@@ -84,6 +84,7 @@ v8_executable("cctest") {
"libsampler/test-sampler.cc",
"print-extension.cc",
"profiler-extension.cc",
"test-access-checks.cc",
"test-accessors.cc",
"test-api-accessors.cc",
"test-api-fast-accessor-builder.cc",
......
......@@ -119,6 +119,7 @@
'libsampler/test-sampler.cc',
'print-extension.cc',
'profiler-extension.cc',
'test-access-checks.cc',
'test-accessors.cc',
'test-api.cc',
'test-api.h',
......
// Copyright 2016 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 <stdlib.h>
#include "test/cctest/cctest.h"
namespace {
int32_t g_cross_context_int = 0;
void NamedGetter(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (property->Equals(context, v8_str("cross_context_int")).FromJust())
info.GetReturnValue().Set(g_cross_context_int);
}
void NamedSetter(v8::Local<v8::Name> property, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
return;
if (value->IsInt32()) {
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
}
info.GetReturnValue().Set(value);
}
void NamedQuery(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
return;
info.GetReturnValue().Set(v8::DontDelete);
}
void NamedDeleter(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
return;
info.GetReturnValue().Set(false);
}
void NamedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
names->Set(context, 0, v8_str("cross_context_int")).FromJust();
info.GetReturnValue().Set(names);
}
void IndexedGetter(uint32_t index,
const v8::PropertyCallbackInfo<v8::Value>& info) {
if (index == 7) info.GetReturnValue().Set(g_cross_context_int);
}
void IndexedSetter(uint32_t index, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (index != 7) return;
if (value->IsInt32()) {
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
}
info.GetReturnValue().Set(value);
}
void IndexedQuery(uint32_t index,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (index == 7) info.GetReturnValue().Set(v8::DontDelete);
}
void IndexedDeleter(uint32_t index,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
if (index == 7) info.GetReturnValue().Set(false);
}
void IndexedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
names->Set(context, 0, v8_str("7")).FromJust();
info.GetReturnValue().Set(names);
}
bool AccessCheck(v8::Local<v8::Context> accessing_context,
v8::Local<v8::Object> accessed_object,
v8::Local<v8::Value> data) {
return false;
}
void GetCrossContextInt(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(g_cross_context_int);
}
void SetCrossContextInt(v8::Local<v8::String> property,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (value->IsInt32()) {
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
}
}
void Return42(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(42);
}
} // namespace
TEST(AccessCheckWithInterceptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->SetAccessCheckCallbackAndHandler(
AccessCheck,
v8::NamedPropertyHandlerConfiguration(
NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
IndexedQuery, IndexedDeleter,
IndexedEnumerator));
global_template->SetNativeDataProperty(
v8_str("cross_context_int"), GetCrossContextInt, SetCrossContextInt);
global_template->SetNativeDataProperty(
v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
v8::Local<v8::Context> context0 =
v8::Context::New(isolate, nullptr, global_template);
context0->Enter();
// Running script in this context should work.
CompileRunChecked(isolate, "this.foo = 42; this[23] = true;");
ExpectInt32("this.all_can_read", 42);
CompileRunChecked(isolate, "this.cross_context_int = 23");
CHECK_EQ(g_cross_context_int, 23);
ExpectInt32("this.cross_context_int", 23);
// Create another context.
{
v8::HandleScope other_scope(isolate);
v8::Local<v8::Context> context1 =
v8::Context::New(isolate, nullptr, global_template);
context1->Global()
->Set(context1, v8_str("other"), context0->Global())
.FromJust();
v8::Context::Scope context_scope(context1);
{
v8::TryCatch try_catch(isolate);
CHECK(CompileRun(context1, "this.other.foo").IsEmpty());
}
{
v8::TryCatch try_catch(isolate);
CHECK(CompileRun(context1, "this.other[23]").IsEmpty());
}
// AllCanRead properties are also inaccessible.
{
v8::TryCatch try_catch(isolate);
CHECK(CompileRun(context1, "this.other.all_can_read").IsEmpty());
}
// Intercepted properties are accessible, however.
ExpectInt32("this.other.cross_context_int", 23);
CompileRunChecked(isolate, "this.other.cross_context_int = 42");
ExpectInt32("this.other[7]", 42);
ExpectString("JSON.stringify(Object.getOwnPropertyNames(this.other))",
"[\"7\",\"cross_context_int\"]");
}
}
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