quarks
v0.1.2
Published
A library that exposes V8 API inside javascript code.
Downloads
18
Readme
node-quarks
A library that exposes V8 API inside javascript code.
The V8 API has many useful function and object that helps V8 C++ embedder. NodeJS allow to embed C++ modules with the "require" implementation. However writing C++ code to use the V8 API is somehow too tedious. This library provide pure in-javascript access to some V8 API features. Be warned that not all features are suitable for in-javascript access. Also not all features works as expected.
Brief introduction to API
This section provides required terminology to read the API and the issues. Although exposure is wrapped in "secured" functions, so no call will fall to C++ exception and destroy the NodeJS process, there are cases that doesn't work as expected. If you are familiar with internals of V8 engine version 3.14 and newer, you can skip this section.
Object
Objects in javascript are map from key to something. There are three types of properties:
- Data Property
- Accessor Property
- Interceptors
Note: Interceptors are intentionally not called "properties".
Data Property
That is the usual value property, it can be any javascript value - primitive, null, undefined, object, function, etc.
Accessor Property
Properties of this type does not contain value, but has a pair of functions knows as getter and setter. The getter is called each time someone trying to get the value of that property. The setter (if exists) is called each time someone is trying to set a value to that property.
Example:
document.body.innerHTML = "<b>asd</b>";
This calls internal function of HTMLElement which does not store the string directly, but parses it and modify the DOM tree.
Object.defineProperty(MyClass.prototype, "flag", {
"get": function() {
return this._storage.flag;
},
"set": function(value) {
this._storage.flag = !!value;
}
});
This is one way to define accessor purely in javascript from ECMAScript5.1 version and above. Instances of
MyClass
will have a property flag, that when set to any value, will convert it to boolean before store it.
Interceptors
These are not really a properties, but a callback functions set on object. These callbacks are called to determine what properties the object has, when someone trying to get or set property value or even to determine if property is configurable, enumerable or read-only. These are quite useful, when determining the properties of an object are dependent on something external to the object.
There are no way to use interceptors purely in javascript. Interceptors are provided by V8 API and currently have a lot of issues.
[[Local]] and [[Prototype]]
Each object in javascript has two source of properties. The local storage of properties is known as "own properties", "local properties" and so on. We will call the storage for local properties - [[Local]]. The prototype storage of properties is known as "prototype properties", "properties on __proto__" and so on. We will call the storage for prototype properties - [[Prototype]]. The square brackets are intentional, because all function have a special property prototype, which although connected to [[Prototype]] it is not the same.
So how this storage spaces are mixed? Each time someone is trying to access a property of an object that property is
searched in [[Local]]. If the property is not found in [[Local]] it is searched on [[Prototype]]. The [[Prototype]]
storage however is an javascript object (the [[Local]] is not). Searching a javascript object repeat the process by
looking that javascript object [[Local]] first, then its [[Prototype]]. This search completes when a property is found
or when a point is reached that there is no [[Prototype]]. The Object.prototype
object has null
value for
[[Prototype]].
Where the [[Local]] and [[Prototype]] comes from? The [[Local]] is internally created when an object is created.
var o = {};
//Now o has [[Local]] storage.
The [[Prototype]] however is the very same object that is given by prototype property of the function used to construct the object:
var literal = {};
var object = new Object();
//Now "literal" and "object" has Object.prototype for [[Prototype]].
var MyClass = function() {}
var myObject = new MyClass();
//Now "myObject" has MyClass.prototype for [[Prototype]].
//Note: If not modified, the MyClass.prototype has Object.prototype for [[Prototype]].
Because [[Local]] is checked first, the local properties could shadow the prototype properties.
var MyClass = function() {}
MyClass.prototype.foo = "bar";
var myObject = new MyClass();
myObject.foo = "baz";
console.log(myObject.foo); //baz
console.log(MyClass.prototype.foo); //bar
//Now myObject.foo shadows the property with the same name coming from MyClass.prototype
Interceptors and local properties
Once an object has an interceptors callback set, they shadow the [[Local]]. However interceptors are API only and they
could return empty handles. These are not handles to any javascript value, including undefined
or null
.
Returning an empty handle means that "this property is not found within the interceptors". If that is the case, the
search continue with [[Local]] and [[Prototype]] as described above.
Be warned that interceptors are not stored within the object, it stores only callbacks and properties are retrieved by
calling the respective callback instead of searching the [[Local]] or [[Prototype]] storage. Thus this properties are
not real, none of the functions of object containing Real in their name will retrieve those properties (e.g.
Object::HasRealNamedProperty
and etc).
Issues
- Callback handlers set on object cannot be called with Function.prototype.apply or Function.prototype.call. Thus
using ObjectTemplate::SetCallAsFunctionHandler should be used with care. The result of template is an
Object
and not aFunction
. It means thattypeof object === "object
is true andquarks.Value.isFunction(object)
is false. - Interceptors may not work as expected. While though "normal usage" of an object interceptors are called as expected, following cases has pitfalls:
- The
Object.getOwnPropertyDescriptor
method does not call interceptors and searches the [[Local]] directly. - The
for...in
loop callsenumerate
andquery
interceptors, but ignoreDONT_ENUM
property attribute. - The
Object.keys
method calls onlyenumerate
interceptor and thus ignoreDONT_ENUM
property attribute. - The
Object.defineProperty
method doesn't call any interceptors and set attribute to [[Local]] directly. - The
quarks.Object.setAccessor
andquarks.ObjectTemplate.prototype.setAccessor
callsObject::SetAccessor
andObjectTemplate::SetAccessor
respectively. Because those methods are exposed to call C++ functions and not javascript functions,Object.getOwnPropertyDescriptor
will not retrieve the callbacks given. Instead the descriptor will contain avalue
property which will be the value returned by the getter.