Commit ed110a17 authored by vogelheim's avatar vogelheim Committed by Commit bot

Allow JavaScript accessors on API objects.

(This is somewhat experimental; hence protected by #ifdef.)

R=epertoso@chromium.org, jochen@chromium.org
BUG=chromium:508898
LOG=N

Review URL: https://codereview.chromium.org/1367953002

Cr-Commit-Position: refs/heads/master@{#31002}
parent 46579c0b
......@@ -67,6 +67,9 @@
# Set to 1 to enable building with wasm prototype.
'v8_wasm%': 0,
# Enable/disable JavaScript API accessors.
'v8_js_accessors%': 0,
},
'target_defaults': {
'conditions': [
......@@ -109,6 +112,9 @@
['v8_wasm!=0', {
'defines': ['V8_WASM',],
}],
['v8_js_accessors!=0', {
'defines': ['V8_JS_ACCESSORS'],
}],
], # conditions
'configurations': {
'DebugBaseCommon': {
......
......@@ -4027,6 +4027,13 @@ class V8_EXPORT Template : public Data {
PropertyAttribute attribute = None,
AccessControl settings = DEFAULT);
#ifdef V8_JS_ACCESSORS
void SetAccessorProperty(Local<Name> name,
Local<Function> getter = Local<Function>(),
Local<Function> setter = Local<Function>(),
PropertyAttribute attribute = None);
#endif // V8_JS_ACCESSORS
/**
* Whenever the property with the given name is accessed on objects
* created from this Template the getter and setter callbacks
......
......@@ -37,6 +37,25 @@ MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data,
}
MaybeHandle<JSFunction> InstantiateFunctionOrMaybeDont(Isolate* isolate,
Handle<Object> data) {
DCHECK(data->IsFunctionTemplateInfo() || data->IsJSFunction());
if (data->IsFunctionTemplateInfo()) {
// A function template needs to be instantiated.
return InstantiateFunction(isolate,
Handle<FunctionTemplateInfo>::cast(data));
#ifdef V8_JS_ACCESSORS
} else if (data->IsJSFunction()) {
// If we already have a proper function, we do not need additional work.
// (This should only happen for JavaScript API accessors.)
return Handle<JSFunction>::cast(data);
#endif // V8_JS_ACCESSORS
} else {
UNREACHABLE();
return MaybeHandle<JSFunction>();
}
}
MaybeHandle<Object> DefineAccessorProperty(Isolate* isolate,
Handle<JSObject> object,
Handle<Name> name,
......@@ -44,18 +63,14 @@ MaybeHandle<Object> DefineAccessorProperty(Isolate* isolate,
Handle<Object> setter,
PropertyAttributes attributes) {
if (!getter->IsUndefined()) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, getter,
InstantiateFunction(isolate,
Handle<FunctionTemplateInfo>::cast(getter)),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, getter,
InstantiateFunctionOrMaybeDont(isolate, getter),
Object);
}
if (!setter->IsUndefined()) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, setter,
InstantiateFunction(isolate,
Handle<FunctionTemplateInfo>::cast(setter)),
Object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, setter,
InstantiateFunctionOrMaybeDont(isolate, setter),
Object);
}
RETURN_ON_EXCEPTION(isolate, JSObject::DefineAccessor(object, name, getter,
setter, attributes),
......@@ -364,10 +379,19 @@ void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
void ApiNatives::AddAccessorProperty(Isolate* isolate,
Handle<TemplateInfo> info,
Handle<Name> name,
Handle<FunctionTemplateInfo> getter,
Handle<FunctionTemplateInfo> setter,
Handle<Name> name, Handle<Object> getter,
Handle<Object> setter,
PropertyAttributes attributes) {
#ifdef V8_JS_ACCESSORS
DCHECK(getter.is_null() || getter->IsFunctionTemplateInfo() ||
getter->IsJSFunction());
DCHECK(setter.is_null() || setter->IsFunctionTemplateInfo() ||
setter->IsJSFunction());
#else
DCHECK(getter.is_null() || getter->IsFunctionTemplateInfo());
DCHECK(setter.is_null() || setter->IsFunctionTemplateInfo());
#endif // V8_JS_ACCESSORS
const int kSize = 4;
PropertyDetails details(attributes, ACCESSOR, 0, PropertyCellType::kNoCell);
auto details_handle = handle(details.AsSmi(), isolate);
......
......@@ -45,9 +45,8 @@ class ApiNatives {
PropertyAttributes attributes);
static void AddAccessorProperty(Isolate* isolate, Handle<TemplateInfo> info,
Handle<Name> name,
Handle<FunctionTemplateInfo> getter,
Handle<FunctionTemplateInfo> setter,
Handle<Name> name, Handle<Object> getter,
Handle<Object> setter,
PropertyAttributes attributes);
static void AddNativeDataProperty(Isolate* isolate, Handle<TemplateInfo> info,
......
......@@ -955,6 +955,25 @@ void Template::SetAccessorProperty(
}
#ifdef V8_JS_ACCESSORS
void Template::SetAccessorProperty(v8::Local<v8::Name> name,
v8::Local<Function> getter,
v8::Local<Function> setter,
v8::PropertyAttribute attribute) {
auto templ = Utils::OpenHandle(this);
auto isolate = templ->GetIsolate();
ENTER_V8(isolate);
DCHECK(!name.IsEmpty());
DCHECK(!getter.IsEmpty() || !setter.IsEmpty());
i::HandleScope scope(isolate);
i::ApiNatives::AddAccessorProperty(
isolate, templ, Utils::OpenHandle(*name),
Utils::OpenHandle(*getter, true), Utils::OpenHandle(*setter, true),
static_cast<PropertyAttributes>(attribute));
}
#endif // V8_JS_ACCESSORS
// --- F u n c t i o n T e m p l a t e ---
static void InitializeFunctionTemplate(
i::Handle<i::FunctionTemplateInfo> info) {
......
......@@ -99,6 +99,7 @@
'test-alloc.cc',
'test-api.cc',
'test-api.h',
'test-api-accessors.cc',
'test-api-interceptors.cc',
'test-array-list.cc',
'test-ast.cc',
......
// Copyright 2015 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.
// TODO(jochen): Remove this after the setting is turned on globally.
#define V8_IMMINENT_DEPRECATION_WARNINGS
#include "test/cctest/cctest.h"
#include "include/v8.h"
#ifdef V8_JS_ACCESSORS
static void CppAccessor(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(42);
}
static const char* JsAccessor =
"function firstChildJS(value) { return 41; }; firstChildJS";
TEST(JavascriptAccessors) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
LocalContext env;
// We emulate Embedder-created DOM Node instances. Specifically:
// - 'parent': FunctionTemplate ~= DOM Node superclass
// - 'child': FunctionTemplate ~= a specific DOM node type, like a <div />
//
// We'll install both a C++-based and a JS-based accessor on the parent,
// and expect it to be callable on the child.
// Setup the parent template ( =~ DOM Node w/ accessors).
v8::Local<v8::FunctionTemplate> parent = v8::FunctionTemplate::New(isolate);
{
auto signature = v8::Signature::New(isolate, parent);
// cpp accessor as "firstChild":
parent->PrototypeTemplate()->SetAccessorProperty(
v8_str("firstChild"),
v8::FunctionTemplate::New(isolate, CppAccessor, v8::Local<v8::Value>(),
signature));
// JS accessor as "firstChildJS":
auto js_accessor = v8::Local<v8::Function>::Cast(CompileRun(JsAccessor));
parent->PrototypeTemplate()->SetAccessorProperty(v8_str("firstChildJS"),
js_accessor);
}
// Setup child object ( =~ a specific DOM Node, e.g. a <div> ).
// Also, make a creation function on the global object, so we can access it
// in a test.
v8::Local<v8::FunctionTemplate> child = v8::FunctionTemplate::New(isolate);
child->Inherit(parent);
CHECK(env->Global()
->Set(env.local(), v8_str("Node"),
child->GetFunction(env.local()).ToLocalChecked())
.IsJust());
// Setup done: Let's test it:
// The simple case: Run it once.
ExpectInt32("var n = new Node(); n.firstChild", 42);
ExpectInt32("var n = new Node(); n.firstChildJS", 41);
// Run them in a loop. This will likely trigger the optimizing compiler:
ExpectInt32(
"var m = new Node(); "
"var sum = 0; "
"for (var i = 0; i < 3; ++i) { "
" sum += m.firstChild; "
" sum += m.firstChildJS; "
"}; "
"sum;",
3 * (42 + 41));
// Obtain the accessor and call it via apply on the Node:
ExpectInt32(
"var n = new Node(); "
"var g = Object.getOwnPropertyDescriptor("
" n.__proto__.__proto__, 'firstChild')['get']; "
"g.apply(n);",
42);
ExpectInt32(
"var n = new Node(); "
"var g = Object.getOwnPropertyDescriptor("
" n.__proto__.__proto__, 'firstChildJS')['get']; "
"g.apply(n);",
41);
// TODO(vogelheim): Verify compatible receiver check works.
}
#endif // V8_JS_ACCESSORS
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