# Set to 1 to enable building with wasm prototype.
'v8_wasm%': 0,
+
+ # Enable/disable JavaScript API accessors.
+ 'v8_js_accessors%': 0,
},
'target_defaults': {
'conditions': [
['v8_wasm!=0', {
'defines': ['V8_WASM',],
}],
+ ['v8_js_accessors!=0', {
+ 'defines': ['V8_JS_ACCESSORS'],
+ }],
], # conditions
'configurations': {
'DebugBaseCommon': {
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
}
+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,
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),
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);
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,
}
+#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) {
'test-alloc.cc',
'test-api.cc',
'test-api.h',
+ 'test-api-accessors.cc',
'test-api-interceptors.cc',
'test-array-list.cc',
'test-ast.cc',
--- /dev/null
+// 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