[api] Add interceptor for defineProperty().
With the Indexed/GenericNamedPropertyDefinerCallback it is possible to intercept Object.defineProperty() calls. Requests that call JSReceiver::OrdinaryDefineOwnProperty() internally, also trigger the interceptor. This includes Object.freeze(), Object.preventExtensions(), and Object.seal(). BUG= Review-Url: https://codereview.chromium.org/2272383002 Cr-Commit-Position: refs/heads/master@{#39094}
This commit is contained in:
parent
8acb7ab9f1
commit
b9d985975c
65
include/v8.h
65
include/v8.h
@ -4687,6 +4687,29 @@ typedef void (*GenericNamedPropertyDeleterCallback)(
|
||||
typedef void (*GenericNamedPropertyEnumeratorCallback)(
|
||||
const PropertyCallbackInfo<Array>& info);
|
||||
|
||||
/**
|
||||
* Interceptor for defineProperty requests on an object.
|
||||
*
|
||||
* Use `info.GetReturnValue()` to indicate whether the request was intercepted
|
||||
* or not. If the definer successfully intercepts the request, i.e., if the
|
||||
* request should not be further executed, call
|
||||
* `info.GetReturnValue().Set(value)`. If the definer
|
||||
* did not intercept the request, i.e., if the request should be handled as
|
||||
* if no interceptor is present, do not not call `Set()`.
|
||||
*
|
||||
* \param property The name of the property for which the request was
|
||||
* intercepted.
|
||||
* \param desc The property descriptor which is used to define the
|
||||
* property if the request is not intercepted.
|
||||
* \param info Information about the intercepted request, such as
|
||||
* isolate, receiver, return value, or whether running in `'use strict'` mode.
|
||||
* See `PropertyCallbackInfo`.
|
||||
*
|
||||
* See also `ObjectTemplate::SetNamedPropertyHandler`.
|
||||
*/
|
||||
typedef void (*GenericNamedPropertyDefinerCallback)(
|
||||
Local<Name> key, const PropertyDescriptor& desc,
|
||||
const PropertyCallbackInfo<Value>& info);
|
||||
|
||||
/**
|
||||
* Returns the value of the property if the getter intercepts the
|
||||
@ -4733,6 +4756,9 @@ typedef void (*IndexedPropertyDeleterCallback)(
|
||||
typedef void (*IndexedPropertyEnumeratorCallback)(
|
||||
const PropertyCallbackInfo<Array>& info);
|
||||
|
||||
typedef void (*IndexedPropertyDefinerCallback)(
|
||||
uint32_t index, const PropertyDescriptor& desc,
|
||||
const PropertyCallbackInfo<Value>& info);
|
||||
|
||||
/**
|
||||
* Access type specification.
|
||||
@ -4995,6 +5021,25 @@ struct NamedPropertyHandlerConfiguration {
|
||||
query(query),
|
||||
deleter(deleter),
|
||||
enumerator(enumerator),
|
||||
definer(0),
|
||||
data(data),
|
||||
flags(flags) {}
|
||||
|
||||
NamedPropertyHandlerConfiguration(
|
||||
GenericNamedPropertyGetterCallback getter,
|
||||
GenericNamedPropertySetterCallback setter,
|
||||
GenericNamedPropertyQueryCallback query,
|
||||
GenericNamedPropertyDeleterCallback deleter,
|
||||
GenericNamedPropertyEnumeratorCallback enumerator,
|
||||
GenericNamedPropertyDefinerCallback definer,
|
||||
Local<Value> data = Local<Value>(),
|
||||
PropertyHandlerFlags flags = PropertyHandlerFlags::kNone)
|
||||
: getter(getter),
|
||||
setter(setter),
|
||||
query(query),
|
||||
deleter(deleter),
|
||||
enumerator(enumerator),
|
||||
definer(definer),
|
||||
data(data),
|
||||
flags(flags) {}
|
||||
|
||||
@ -5003,6 +5048,7 @@ struct NamedPropertyHandlerConfiguration {
|
||||
GenericNamedPropertyQueryCallback query;
|
||||
GenericNamedPropertyDeleterCallback deleter;
|
||||
GenericNamedPropertyEnumeratorCallback enumerator;
|
||||
GenericNamedPropertyDefinerCallback definer;
|
||||
Local<Value> data;
|
||||
PropertyHandlerFlags flags;
|
||||
};
|
||||
@ -5023,6 +5069,24 @@ struct IndexedPropertyHandlerConfiguration {
|
||||
query(query),
|
||||
deleter(deleter),
|
||||
enumerator(enumerator),
|
||||
definer(0),
|
||||
data(data),
|
||||
flags(flags) {}
|
||||
|
||||
IndexedPropertyHandlerConfiguration(
|
||||
IndexedPropertyGetterCallback getter,
|
||||
IndexedPropertySetterCallback setter, IndexedPropertyQueryCallback query,
|
||||
IndexedPropertyDeleterCallback deleter,
|
||||
IndexedPropertyEnumeratorCallback enumerator,
|
||||
IndexedPropertyDefinerCallback definer,
|
||||
Local<Value> data = Local<Value>(),
|
||||
PropertyHandlerFlags flags = PropertyHandlerFlags::kNone)
|
||||
: getter(getter),
|
||||
setter(setter),
|
||||
query(query),
|
||||
deleter(deleter),
|
||||
enumerator(enumerator),
|
||||
definer(definer),
|
||||
data(data),
|
||||
flags(flags) {}
|
||||
|
||||
@ -5031,6 +5095,7 @@ struct IndexedPropertyHandlerConfiguration {
|
||||
IndexedPropertyQueryCallback query;
|
||||
IndexedPropertyDeleterCallback deleter;
|
||||
IndexedPropertyEnumeratorCallback enumerator;
|
||||
IndexedPropertyDefinerCallback definer;
|
||||
Local<Value> data;
|
||||
PropertyHandlerFlags flags;
|
||||
};
|
||||
|
@ -80,6 +80,24 @@ Handle<Object> PropertyCallbackArguments::Call(
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
|
||||
Handle<Object> PropertyCallbackArguments::Call(
|
||||
GenericNamedPropertyDefinerCallback f, Handle<Name> name,
|
||||
const v8::PropertyDescriptor& desc) {
|
||||
Isolate* isolate = this->isolate();
|
||||
RuntimeCallTimerScope timer(
|
||||
isolate, &RuntimeCallStats::GenericNamedPropertyDefinerCallback);
|
||||
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
|
||||
isolate,
|
||||
&tracing::TraceEventStatsTable::GenericNamedPropertyDefinerCallback);
|
||||
VMState<EXTERNAL> state(isolate);
|
||||
ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f));
|
||||
PropertyCallbackInfo<v8::Value> info(begin());
|
||||
LOG(isolate,
|
||||
ApiNamedPropertyAccess("interceptor-named-define", holder(), *name));
|
||||
f(v8::Utils::ToLocal(name), desc, info);
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
|
||||
Handle<Object> PropertyCallbackArguments::Call(IndexedPropertySetterCallback f,
|
||||
uint32_t index,
|
||||
Handle<Object> value) {
|
||||
@ -97,6 +115,23 @@ Handle<Object> PropertyCallbackArguments::Call(IndexedPropertySetterCallback f,
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
|
||||
Handle<Object> PropertyCallbackArguments::Call(
|
||||
IndexedPropertyDefinerCallback f, uint32_t index,
|
||||
const v8::PropertyDescriptor& desc) {
|
||||
Isolate* isolate = this->isolate();
|
||||
RuntimeCallTimerScope timer(
|
||||
isolate, &RuntimeCallStats::IndexedPropertyDefinerCallback);
|
||||
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
|
||||
isolate, &tracing::TraceEventStatsTable::IndexedPropertyDefinerCallback);
|
||||
VMState<EXTERNAL> state(isolate);
|
||||
ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f));
|
||||
PropertyCallbackInfo<v8::Value> info(begin());
|
||||
LOG(isolate,
|
||||
ApiIndexedPropertyAccess("interceptor-indexed-define", holder(), index));
|
||||
f(index, desc, info);
|
||||
return GetReturnValue<Object>(isolate);
|
||||
}
|
||||
|
||||
void PropertyCallbackArguments::Call(AccessorNameSetterCallback f,
|
||||
Handle<Name> name, Handle<Object> value) {
|
||||
Isolate* isolate = this->isolate();
|
||||
|
@ -119,9 +119,16 @@ class PropertyCallbackArguments
|
||||
inline Handle<Object> Call(GenericNamedPropertySetterCallback f,
|
||||
Handle<Name> name, Handle<Object> value);
|
||||
|
||||
inline Handle<Object> Call(GenericNamedPropertyDefinerCallback f,
|
||||
Handle<Name> name,
|
||||
const v8::PropertyDescriptor& desc);
|
||||
|
||||
inline Handle<Object> Call(IndexedPropertySetterCallback f, uint32_t index,
|
||||
Handle<Object> value);
|
||||
|
||||
inline Handle<Object> Call(IndexedPropertyDefinerCallback f, uint32_t index,
|
||||
const v8::PropertyDescriptor& desc);
|
||||
|
||||
inline void Call(AccessorNameSetterCallback f, Handle<Name> name,
|
||||
Handle<Object> value);
|
||||
|
||||
|
34
src/api.cc
34
src/api.cc
@ -1506,10 +1506,10 @@ void ObjectTemplate::SetAccessor(v8::Local<Name> name,
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename Query, typename Deleter,
|
||||
typename Enumerator>
|
||||
typename Enumerator, typename Definer>
|
||||
static i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
|
||||
i::Isolate* isolate, Getter getter, Setter setter, Query query,
|
||||
Deleter remover, Enumerator enumerator, Local<Value> data,
|
||||
Deleter remover, Enumerator enumerator, Definer definer, Local<Value> data,
|
||||
PropertyHandlerFlags flags) {
|
||||
auto obj = i::Handle<i::InterceptorInfo>::cast(
|
||||
isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE));
|
||||
@ -1520,6 +1520,7 @@ static i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
|
||||
if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query);
|
||||
if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover);
|
||||
if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator);
|
||||
if (definer != 0) SET_FIELD_WRAPPED(obj, set_definer, definer);
|
||||
obj->set_can_intercept_symbols(
|
||||
!(static_cast<int>(flags) &
|
||||
static_cast<int>(PropertyHandlerFlags::kOnlyInterceptStrings)));
|
||||
@ -1536,39 +1537,35 @@ static i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
|
||||
}
|
||||
|
||||
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) {
|
||||
typename Enumerator, typename Definer>
|
||||
static void ObjectTemplateSetNamedPropertyHandler(
|
||||
ObjectTemplate* templ, Getter getter, Setter setter, Query query,
|
||||
Deleter remover, Enumerator enumerator, Definer definer, 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);
|
||||
enumerator, definer, data, flags);
|
||||
cons->set_named_property_handler(*obj);
|
||||
}
|
||||
|
||||
|
||||
void ObjectTemplate::SetNamedPropertyHandler(
|
||||
NamedPropertyGetterCallback getter, NamedPropertySetterCallback setter,
|
||||
NamedPropertyQueryCallback query, NamedPropertyDeleterCallback remover,
|
||||
NamedPropertyEnumeratorCallback enumerator, Local<Value> data) {
|
||||
ObjectTemplateSetNamedPropertyHandler(
|
||||
this, getter, setter, query, remover, enumerator, data,
|
||||
this, getter, setter, query, remover, enumerator, nullptr, data,
|
||||
PropertyHandlerFlags::kOnlyInterceptStrings);
|
||||
}
|
||||
|
||||
|
||||
void ObjectTemplate::SetHandler(
|
||||
const NamedPropertyHandlerConfiguration& config) {
|
||||
ObjectTemplateSetNamedPropertyHandler(
|
||||
this, config.getter, config.setter, config.query, config.deleter,
|
||||
config.enumerator, config.data, config.flags);
|
||||
config.enumerator, config.definer, config.data, config.flags);
|
||||
}
|
||||
|
||||
|
||||
@ -1628,13 +1625,14 @@ void ObjectTemplate::SetAccessCheckCallbackAndHandler(
|
||||
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);
|
||||
named_handler.deleter, named_handler.enumerator, named_handler.definer,
|
||||
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);
|
||||
indexed_handler.enumerator, indexed_handler.definer, indexed_handler.data,
|
||||
indexed_handler.flags);
|
||||
info->set_indexed_interceptor(*indexed_interceptor);
|
||||
|
||||
if (data.IsEmpty()) {
|
||||
@ -1655,7 +1653,7 @@ void ObjectTemplate::SetHandler(
|
||||
EnsureNotInstantiated(cons, "v8::ObjectTemplate::SetHandler");
|
||||
auto obj = CreateInterceptorInfo(
|
||||
isolate, config.getter, config.setter, config.query, config.deleter,
|
||||
config.enumerator, config.data, config.flags);
|
||||
config.enumerator, config.definer, config.data, config.flags);
|
||||
cons->set_indexed_property_handler(*obj);
|
||||
}
|
||||
|
||||
|
@ -677,9 +677,11 @@ class RuntimeCallTimer {
|
||||
V(DeoptimizeCode) \
|
||||
V(FunctionCallback) \
|
||||
V(GC) \
|
||||
V(GenericNamedPropertyDefinerCallback) \
|
||||
V(GenericNamedPropertyDeleterCallback) \
|
||||
V(GenericNamedPropertyQueryCallback) \
|
||||
V(GenericNamedPropertySetterCallback) \
|
||||
V(IndexedPropertyDefinerCallback) \
|
||||
V(IndexedPropertyDeleterCallback) \
|
||||
V(IndexedPropertyGetterCallback) \
|
||||
V(IndexedPropertyQueryCallback) \
|
||||
|
@ -5709,6 +5709,7 @@ ACCESSORS(InterceptorInfo, setter, Object, kSetterOffset)
|
||||
ACCESSORS(InterceptorInfo, query, Object, kQueryOffset)
|
||||
ACCESSORS(InterceptorInfo, deleter, Object, kDeleterOffset)
|
||||
ACCESSORS(InterceptorInfo, enumerator, Object, kEnumeratorOffset)
|
||||
ACCESSORS(InterceptorInfo, definer, Object, kDefinerOffset)
|
||||
ACCESSORS(InterceptorInfo, data, Object, kDataOffset)
|
||||
SMI_ACCESSORS(InterceptorInfo, flags, kFlagsOffset)
|
||||
BOOL_ACCESSORS(InterceptorInfo, flags, can_intercept_symbols,
|
||||
|
@ -1677,6 +1677,71 @@ Maybe<bool> SetPropertyWithInterceptorInternal(
|
||||
return Just(result);
|
||||
}
|
||||
|
||||
Maybe<bool> DefinePropertyWithInterceptorInternal(
|
||||
LookupIterator* it, Handle<InterceptorInfo> interceptor,
|
||||
Object::ShouldThrow should_throw, PropertyDescriptor& desc) {
|
||||
Isolate* isolate = it->isolate();
|
||||
// Make sure that the top context does not change when doing callbacks or
|
||||
// interceptor calls.
|
||||
AssertNoContextChange ncc(isolate);
|
||||
|
||||
if (interceptor->definer()->IsUndefined(isolate)) return Just(false);
|
||||
|
||||
Handle<JSObject> holder = it->GetHolder<JSObject>();
|
||||
bool result;
|
||||
Handle<Object> receiver = it->GetReceiver();
|
||||
if (!receiver->IsJSReceiver()) {
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
|
||||
Object::ConvertReceiver(isolate, receiver),
|
||||
Nothing<bool>());
|
||||
}
|
||||
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
|
||||
*holder, should_throw);
|
||||
|
||||
std::unique_ptr<v8::PropertyDescriptor> descriptor(
|
||||
new v8::PropertyDescriptor());
|
||||
if (PropertyDescriptor::IsAccessorDescriptor(&desc)) {
|
||||
descriptor.reset(new v8::PropertyDescriptor(
|
||||
v8::Utils::ToLocal(desc.get()), v8::Utils::ToLocal(desc.set())));
|
||||
} else if (PropertyDescriptor::IsDataDescriptor(&desc)) {
|
||||
if (desc.has_writable()) {
|
||||
descriptor.reset(new v8::PropertyDescriptor(
|
||||
v8::Utils::ToLocal(desc.value()), desc.writable()));
|
||||
} else {
|
||||
descriptor.reset(
|
||||
new v8::PropertyDescriptor(v8::Utils::ToLocal(desc.value())));
|
||||
}
|
||||
}
|
||||
if (desc.has_enumerable()) {
|
||||
descriptor->set_enumerable(desc.enumerable());
|
||||
}
|
||||
if (desc.has_configurable()) {
|
||||
descriptor->set_configurable(desc.configurable());
|
||||
}
|
||||
|
||||
if (it->IsElement()) {
|
||||
uint32_t index = it->index();
|
||||
v8::IndexedPropertyDefinerCallback definer =
|
||||
v8::ToCData<v8::IndexedPropertyDefinerCallback>(interceptor->definer());
|
||||
result = !args.Call(definer, index, *descriptor).is_null();
|
||||
} else {
|
||||
Handle<Name> name = it->name();
|
||||
DCHECK(!name->IsPrivate());
|
||||
|
||||
if (name->IsSymbol() && !interceptor->can_intercept_symbols()) {
|
||||
return Just(false);
|
||||
}
|
||||
|
||||
v8::GenericNamedPropertyDefinerCallback definer =
|
||||
v8::ToCData<v8::GenericNamedPropertyDefinerCallback>(
|
||||
interceptor->definer());
|
||||
result = !args.Call(definer, name, *descriptor).is_null();
|
||||
}
|
||||
|
||||
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>());
|
||||
return Just(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck(
|
||||
@ -6535,6 +6600,35 @@ Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate,
|
||||
it.Next();
|
||||
}
|
||||
|
||||
// Handle interceptor
|
||||
if (it.state() == LookupIterator::INTERCEPTOR) {
|
||||
Handle<Map> store_target_map;
|
||||
if (it.GetReceiver()->IsJSObject()) {
|
||||
store_target_map = handle(it.GetStoreTarget()->map(), it.isolate());
|
||||
}
|
||||
if (it.HolderIsReceiverOrHiddenPrototype()) {
|
||||
Maybe<bool> result = DefinePropertyWithInterceptorInternal(
|
||||
&it, it.GetInterceptor(), should_throw, *desc);
|
||||
if (result.IsNothing() || result.FromJust()) {
|
||||
return result;
|
||||
}
|
||||
// Interceptor modified the store target but failed to set the
|
||||
// property.
|
||||
if (!store_target_map.is_null() &&
|
||||
*store_target_map != it.GetStoreTarget()->map()) {
|
||||
it.isolate()->PushStackTraceAndDie(
|
||||
0xabababaa, v8::ToCData<void*>(it.GetInterceptor()->setter()),
|
||||
nullptr, 0xabababab);
|
||||
}
|
||||
Utils::ApiCheck(store_target_map.is_null() ||
|
||||
*store_target_map == it.GetStoreTarget()->map(),
|
||||
it.IsElement() ? "v8::IndexedPropertySetterCallback"
|
||||
: "v8::NamedPropertySetterCallback",
|
||||
"Interceptor silently changed store target.");
|
||||
}
|
||||
it.Next();
|
||||
}
|
||||
|
||||
return OrdinaryDefineOwnProperty(&it, desc, should_throw);
|
||||
}
|
||||
|
||||
|
@ -10790,6 +10790,7 @@ class InterceptorInfo: public Struct {
|
||||
DECL_ACCESSORS(query, Object)
|
||||
DECL_ACCESSORS(deleter, Object)
|
||||
DECL_ACCESSORS(enumerator, Object)
|
||||
DECL_ACCESSORS(definer, Object)
|
||||
DECL_ACCESSORS(data, Object)
|
||||
DECL_BOOLEAN_ACCESSORS(can_intercept_symbols)
|
||||
DECL_BOOLEAN_ACCESSORS(all_can_read)
|
||||
@ -10809,7 +10810,8 @@ class InterceptorInfo: public Struct {
|
||||
static const int kQueryOffset = kSetterOffset + kPointerSize;
|
||||
static const int kDeleterOffset = kQueryOffset + kPointerSize;
|
||||
static const int kEnumeratorOffset = kDeleterOffset + kPointerSize;
|
||||
static const int kDataOffset = kEnumeratorOffset + kPointerSize;
|
||||
static const int kDefinerOffset = kEnumeratorOffset + kPointerSize;
|
||||
static const int kDataOffset = kDefinerOffset + kPointerSize;
|
||||
static const int kFlagsOffset = kDataOffset + kPointerSize;
|
||||
static const int kSize = kFlagsOffset + kPointerSize;
|
||||
|
||||
|
@ -239,6 +239,26 @@ void CheckThisNamedPropertyHandler(
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
void CheckThisIndexedPropertyDefiner(
|
||||
uint32_t index, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner));
|
||||
ApiTestFuzzer::Fuzz();
|
||||
CHECK(info.This()
|
||||
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
void CheckThisNamedPropertyDefiner(
|
||||
Local<Name> property, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner));
|
||||
ApiTestFuzzer::Fuzz();
|
||||
CHECK(info.This()
|
||||
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
void CheckThisIndexedPropertySetter(
|
||||
uint32_t index, Local<Value> value,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
@ -1199,6 +1219,435 @@ THREADED_TEST(NamedPropertyHandlerGetter) {
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
namespace {
|
||||
void NotInterceptingPropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
// Do not intercept by not calling info.GetReturnValue().Set()
|
||||
}
|
||||
|
||||
void InterceptingPropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
|
||||
void CheckDescriptorInDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(!desc.has_writable());
|
||||
CHECK(!desc.has_value());
|
||||
CHECK(!desc.has_enumerable());
|
||||
CHECK(desc.has_configurable());
|
||||
CHECK(!desc.configurable());
|
||||
CHECK(desc.has_get());
|
||||
CHECK(desc.get()->IsFunction());
|
||||
CHECK(desc.has_set());
|
||||
CHECK(desc.set()->IsUndefined());
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
THREADED_TEST(PropertyDefinerCallback) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
|
||||
{ // Intercept defineProperty()
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, NotInterceptingPropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {value: 42});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(42, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
{ // Intercept defineProperty() for correct accessor descriptor
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckDescriptorInDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {"
|
||||
"get: function(){ return 42; }, "
|
||||
"set: undefined,"
|
||||
"configurable: 0"
|
||||
"});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
{ // Do not intercept defineProperty()
|
||||
v8::Local<v8::FunctionTemplate> templ2 =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ2->InstanceTemplate()->SetHandler(
|
||||
v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, InterceptingPropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {value: 42});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void NotInterceptingPropertyDefineCallbackIndexed(
|
||||
uint32_t index, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
// Do not intercept by not calling info.GetReturnValue().Set()
|
||||
}
|
||||
|
||||
void InterceptingPropertyDefineCallbackIndexed(
|
||||
uint32_t index, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(index);
|
||||
}
|
||||
|
||||
void CheckDescriptorInDefineCallbackIndexed(
|
||||
uint32_t index, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(!desc.has_writable());
|
||||
CHECK(!desc.has_value());
|
||||
CHECK(desc.has_enumerable());
|
||||
CHECK(desc.enumerable());
|
||||
CHECK(!desc.has_configurable());
|
||||
CHECK(desc.has_get());
|
||||
CHECK(desc.get()->IsFunction());
|
||||
CHECK(desc.has_set());
|
||||
CHECK(desc.set()->IsUndefined());
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(index);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
THREADED_TEST(PropertyDefinerCallbackIndexed) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
|
||||
{ // Intercept defineProperty()
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(
|
||||
v8::IndexedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, NotInterceptingPropertyDefineCallbackIndexed));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj[2] = 17; "
|
||||
"Object.defineProperty(obj, 2, {value: 42});"
|
||||
"obj[2];";
|
||||
CHECK_EQ(42, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
{ // Intercept defineProperty() for correct accessor descriptor
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(
|
||||
v8::IndexedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckDescriptorInDefineCallbackIndexed));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj[2] = 17; "
|
||||
"Object.defineProperty(obj, 2, {"
|
||||
"get: function(){ return 42; }, "
|
||||
"set: undefined,"
|
||||
"enumerable: true"
|
||||
"});"
|
||||
"obj[2];";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
{ // Do not intercept defineProperty()
|
||||
v8::Local<v8::FunctionTemplate> templ2 =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ2->InstanceTemplate()->SetHandler(
|
||||
v8::IndexedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, InterceptingPropertyDefineCallbackIndexed));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
|
||||
const char* code =
|
||||
"obj[2] = 17; "
|
||||
"Object.defineProperty(obj, 2, {value: 42});"
|
||||
"obj[2];";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
}
|
||||
|
||||
// Test that freeze() is intercepted.
|
||||
THREADED_TEST(PropertyDefinerCallbackForFreeze) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, InterceptingPropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.freeze(obj.x); "
|
||||
"Object.isFrozen(obj.x);";
|
||||
|
||||
CHECK(v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->BooleanValue(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Check that the descriptor passed to the callback is enumerable.
|
||||
namespace {
|
||||
void CheckEnumerablePropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(desc.has_value());
|
||||
CHECK_EQ(42, desc.value()
|
||||
->Int32Value(info.GetIsolate()->GetCurrentContext())
|
||||
.FromJust());
|
||||
CHECK(desc.has_enumerable());
|
||||
CHECK(desc.enumerable());
|
||||
CHECK(!desc.has_writable());
|
||||
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
THREADED_TEST(PropertyDefinerCallbackEnumerable) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckEnumerablePropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {value: 42, enumerable: true});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Check that the descriptor passed to the callback is configurable.
|
||||
namespace {
|
||||
void CheckConfigurablePropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(desc.has_value());
|
||||
CHECK_EQ(42, desc.value()
|
||||
->Int32Value(info.GetIsolate()->GetCurrentContext())
|
||||
.FromJust());
|
||||
CHECK(desc.has_configurable());
|
||||
CHECK(desc.configurable());
|
||||
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
THREADED_TEST(PropertyDefinerCallbackConfigurable) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckConfigurablePropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {value: 42, configurable: true});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Check that the descriptor passed to the callback is writable.
|
||||
namespace {
|
||||
void CheckWritablePropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(desc.has_writable());
|
||||
CHECK(desc.writable());
|
||||
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
THREADED_TEST(PropertyDefinerCallbackWritable) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckWritablePropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17; "
|
||||
"Object.defineProperty(obj, 'x', {value: 42, writable: true});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Check that the descriptor passed to the callback has a getter.
|
||||
namespace {
|
||||
void CheckGetterPropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(desc.has_get());
|
||||
CHECK(!desc.has_set());
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
THREADED_TEST(PropertyDefinerCallbackWithGetter) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckGetterPropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"obj.x = 17;"
|
||||
"Object.defineProperty(obj, 'x', {get: function() {return 42;}});"
|
||||
"obj.x;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// Check that the descriptor passed to the callback has a setter.
|
||||
namespace {
|
||||
void CheckSetterPropertyDefineCallback(
|
||||
Local<Name> name, const v8::PropertyDescriptor& desc,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
CHECK(desc.has_set());
|
||||
CHECK(!desc.has_get());
|
||||
// intercept the callback by setting a non-empty handle
|
||||
info.GetReturnValue().Set(name);
|
||||
}
|
||||
} // namespace
|
||||
THREADED_TEST(PropertyDefinerCallbackWithSetter) {
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
LocalContext env;
|
||||
v8::Local<v8::FunctionTemplate> templ =
|
||||
v8::FunctionTemplate::New(CcTest::isolate());
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
0, 0, 0, 0, 0, CheckSetterPropertyDefineCallback));
|
||||
env->Global()
|
||||
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
->NewInstance(env.local())
|
||||
.ToLocalChecked())
|
||||
.FromJust();
|
||||
const char* code =
|
||||
"Object.defineProperty(obj, 'x', {set: function() {return 42;}});"
|
||||
"obj.x = 17;";
|
||||
CHECK_EQ(17, v8_compile(code)
|
||||
->Run(env.local())
|
||||
.ToLocalChecked()
|
||||
->Int32Value(env.local())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
int echo_indexed_call_count = 0;
|
||||
|
||||
@ -1246,12 +1695,12 @@ THREADED_TEST(PropertyHandlerInPrototype) {
|
||||
templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
||||
CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
|
||||
CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter,
|
||||
CheckThisIndexedPropertyEnumerator));
|
||||
CheckThisIndexedPropertyEnumerator, CheckThisIndexedPropertyDefiner));
|
||||
|
||||
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
||||
CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
|
||||
CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter,
|
||||
CheckThisNamedPropertyEnumerator));
|
||||
CheckThisNamedPropertyEnumerator, CheckThisNamedPropertyDefiner));
|
||||
|
||||
bottom = templ->GetFunction(env.local())
|
||||
.ToLocalChecked()
|
||||
@ -1288,6 +1737,10 @@ THREADED_TEST(PropertyHandlerInPrototype) {
|
||||
|
||||
// Enumerators.
|
||||
CompileRun("for (var p in obj) ;");
|
||||
|
||||
// Indexed and named definer.
|
||||
CompileRun("Object.defineProperty(obj, 2, {});");
|
||||
CompileRun("Object.defineProperty(obj, 'z', {});");
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user