0.1.2 • Published 9 years ago

quarks v0.1.2

Weekly downloads
15
License
-
Repository
github
Last release
9 years ago

node-quarks

Build Status Coverage Status

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:

  1. Data Property
  2. Accessor Property
  3. 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

  1. 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 a Function. It means that typeof object === "object is true and quarks.Value.isFunction(object) is false.
  2. Interceptors may not work as expected. While though "normal usage" of an object interceptors are called as expected, following cases has pitfalls:
  3. The Object.getOwnPropertyDescriptor method does not call interceptors and searches the [Local] directly.
  4. The for...in loop calls enumerate and query interceptors, but ignore DONT_ENUM property attribute.
  5. The Object.keys method calls only enumerate interceptor and thus ignore DONT_ENUM property attribute.
  6. The Object.defineProperty method doesn't call any interceptors and set attribute to [Local] directly.
  7. The quarks.Object.setAccessor and quarks.ObjectTemplate.prototype.setAccessor calls Object::SetAccessor and ObjectTemplate::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 a value property which will be the value returned by the getter.
0.1.2

9 years ago

0.1.1

9 years ago

0.1.0

9 years ago