Commit 7c401bd8 authored by franzih's avatar franzih Committed by Commit bot

[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().

As without this patch, the query interceptor triggers on
defineProperty, unless the definer callback
intercepts the request.

As without this patch, the query interceptor triggers on defineProperty, unless the definer callback intercepts the request.

BUG=

Committed: https://crrev.com/b9d985975cf3bab0ded0cec9fafd3799f9bde29a
Review-Url: https://codereview.chromium.org/2272383002
Cr-Original-Commit-Position: refs/heads/master@{#39094}
Cr-Commit-Position: refs/heads/master@{#39122}
parent 1cb2a70c
......@@ -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);
......
......@@ -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,34 @@ 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.");
}
}
return OrdinaryDefineOwnProperty(&it, desc, should_throw);
}
......
......@@ -10791,6 +10791,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)
......@@ -10810,7 +10811,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;
......
This diff is collapsed.
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