Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232

Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232
Library/V8/Tutorial - Athile

Library/V8/Tutorial


Strict Standards: Non-static method Parser::extractTagsAndParams() should not be called statically in /home/athilene/public_html/library/wiki/extensions/MathJax/MathJax.php on line 232
From Athile

Jump to:navigation, search

Contents


PLEASE provide feedback or questions on the associated blog entry for this page so that improvements that can be made to this page.

I'd like it to be as helpful as possible for future readers, but only want to invest the time on what on information I specifically know is wanted!

Overview

This isn't precisely a tutorial, but rather a look at example code of using Google V8. The motivation for this page is largely that, while the Google V8 documentation provides all the information necessary to embed V8 in a C++ application, a bit more example code likely would get the concepts across more quickly.

In short, this article attempts (using mostly pseudo-code) to expose a HTML-like Document Object Model (DOM) to the hosted Javascript code. In the pseudo-code below, the "document" variable as well as an created Elements would be native C++ objects in the hosted app - which are exposed to Google V8 via wrapper objects:

var a = document.createElement("a");
a.setAttribute("href", "http://www.google.com");
 
var div = document.createElement("div");
div.appendChild(a);

This will more or less show how to:

Building V8

I'll assume there's no need to add any text to this section here until I get feedback otherwise...


Engine::runJavascript()

This article is about Google's V8 Javascript interpreter, not LxEngine. However, to give some context of how V8 is being used, some very brief and superficial description of LxEngine is needed - namely, one of the main functions that invokes Javascript code within the engine:

Engine::runJavascript(DocumentPtr spDocument, std::vector<std::string>& sources)

Engine::runJavascript() is the entry-point for running a series of scripts on a single, specific LxEngine Document object (i.e. scripts on one object affect only that Document and no others, similar to webpages). What an LxEngine Document actually represents is not terribly important for the purposes of this article (think of it as more or less an XML or HTML document, if that helps). What is important is simply that it's a native C++ object.

One point worth noting: the method takes a vector of scripts, not a single script. This is done so that every script in the vector shares the same execution context. In other words, the first script does affect the second script. This is useful if the first script, for example, is loading a set of utility functions which are then used by the subsequent scripts. This is worth mentioning since the notions of context and scope are intrinsic to using V8 correctly and effectively.

The basics

The Google V8 docs covers in-depth what's described in this section, so we'll run through it only very quickly:

First create a HandleScope. This is a scoped C++ object that informs V8 that all Handle object created within the HandleScope go out of scope when the HandleScope goes out of the scope. This is the standard way of dealing with Handle lifetimes in V8: it's done for efficiency - see the V8 docs for more information about why.

Then a ObjectTemplate is created for the "global" Javascript object. A "template" is a confusing word here if your gut reaction is to think "C++ template". It's essentially a template in the sense of "the basic information used to describe the object before it is actually created". Think of it as a "descriptor" which will be used to create the object, if that is helpful. When the Context object is later passed this template, it will create the global object (accessible via context->Global()) from this template descriptor object. Therefore, if there are multiple V8 Contexts, this single template can be used to create multiple global objects, one per Context. In this barebones code, nothing is done with this object, but this template is where free functions will be added to the global context (e.g. think of a free standing function like HTML's Javascript alert() function).

The Context is next created. Note this needs to be explicitly Disposed() at the end since it is a Persistent object, not a regular Handle or Local which would be disposed of automatically when the HandleScope went out of scope.

The String, Script, Run() code should be somewhat self-explanatory.

        HandleScope handle_scope;
        Handle<ObjectTemplate> global_templ = ObjectTemplate::New(); 
 
        {
            Persistent<Context> context = Context::New(0, global_templ);
            Context::Scope context_scope(context);
 
            {
                Handle<String> source = String::New(text->c_str());
                Handle<Script> script = Script::Compile(source);
 
                Handle<Value> result = script->Run();
            }
 
            context.Dispose();
        }

Adding a free function

First, let's add a free standing function. This is simple and provides a nice printf()-debugging tool. For now, it will be a simple print implementation that takes one string argument and echoes it to the console (i.e. not multi-argument, formatted types like a real printf() implementation).

Adding the function involves "Set"ing a property on the global template. When the Javascript global object is created by the Context, it will inherit these properties from the template. Add the property as a FunctionTemplate. Again, this is creating a FunctionTemplate that tells V8 what to create when it actually needs to create the Function rather than creating a Function directly.

        HandleScope handle_scope;
        Handle<ObjectTemplate> global_templ = ObjectTemplate::New(); 
 
        // Internal debugging methods to make development a little easier.
        //
        global_templ->Set(String::New("__lx_print"), FunctionTemplate::New(print));

A FunctionTemplate needs to match the fixed function signature of all Function objects: return a Handle<Value> and take an Arguments object. If the function doesn't return a value, then just return Undefined(). Note: the C++ implementation uses the name print and the function is exposed as __lx_print to Javascript; obviously, any names can be used as desired.

(Note: technically this function is not a pure 'free-standing' function. The function is actually a method on the implicit global object. In most cases, this does not particularly matter, but is worth remembering if you encounter a situation where it may matter.)

        static v8::Handle<v8::Value> print (const v8::Arguments& args)
        {
            using namespace v8;
 
            // _marshal() is an LxEngine helper to convert a V8 Handle to a native C++ type
            std::string name = _marshal(args[0]);
            std::cout << "JS print: " << name << std::endl;
 
            // Undefined() is the JS equivalent of C++'s void return
            return Undefined();
        }

We'll get to the _marshal in the next section: this is basically just a helper in the LxEngine code to convert V8's Handle<Value> to native C++ types. Also, we'll ignore the fact that this implementation doesn't check that the number of arguments is correct or other validation necessary in production-quality code. Again, this is intended to a barebones example.

Data Marshaling

The _marshal code referenced above is actually a LxEngine helper class (not part of the V8 API!) with overloaded constructors that can convert from many simple native C++ data types to V8 Handle<Value> types and vice-versa via an overloaded implicit cast operator. The details of the class don't fully fit in with the intent of this tutorial (it's about V8, not LxEngine), so let's just look at how a Handle<Value> gets converted to a std::string. Handling other types essentially simply requires taking a look at the V8 documentation (or maybe glancing at the current code for _marshal in the hpp and cpp files over in the LxEngine github repository?).

    struct _marshal
    {
        _marshal(v8::Handle<v8::Value>&v)   : mValue(v) {}
        ...
        operator std::string ()             
        {
            lx_check_error( mValue->IsString() );
            return *v8::String::AsciiValue(mValue);  
        }
        ...
    protected:
        v8::Handle<v8::Value> mValue;
    };

Adding a global object

In other words, how to add the "document" object...

This is a section adds a global variable "document" to the global V8 context. This object wraps a native C++ object (an LxEngine Document, in this case) and exposes it to Javascript. Note: there is no associated named "type" exposed to Javascript so that the user cannot directly create new Document objects; the "type" of the object is defined only implicitly, not explicitly, to the user.

Setting up the class

  1. Create a FunctionTemplate. Remember, a "class" in Javascript is a function, so to create a wrapper of a C++ class, we need a JS function to represent it. Also note that we're creating a FunctionTemplate object. This describes the Function before it is actually created by V8.

  2. Next, modify the InstanceTemplate on the FunctionTemplate. We need to describe what kind of C++ object is created when "new" is called on our Javascript function() (calling "new" on a JS function is essentially the equivalent of making a C++ constructor call). We need to do this so that we can tell V8 to allocate an "internal field" on the object where we can store the native C++ pointer to the object (thus a connection between the JS object and C++ object is maintained). Phrasing this differently: fill in the template for when a new instance of the function is created. Note: since we're creating a single global object, the object creation will only be called internally and never exposed to the user - but we still need to tell V8 how to create the new object whether it's invoked natively or from JS.

  3. Then we have to fill out the PrototypeTemplate for the function. This is essentially the C++ equivalent of accessing the Javascript "prototype" property on a function. If this doesn't make sense, a quick refresher on object-oriented Javascript might be helpful. We then add two need functions to the prototype. Note this is very much like the free function added previously, but in this case there are added to the prototype object of the function - rather than the global object (but in Javascript, functions are a type of object, so that's why there's a close similarity).

            Persistent<Context> context = Context::New(0, global_templ);
            Context::Scope context_scope(context);
 
            // Create the FunctionTemplate.  Think of the "Template" part as being the 
            // descriptor or specification that is be used to create the actual Function
            // when it is created and put into the V8 context.
            //
            // Note that this is essentially an anonymous function, since no name is assigned
            // and the only way it is being accessed is via the V8 API.
            //
            Handle<FunctionTemplate> templ = FunctionTemplate::New();
 
            // Get the ObjectTemplate for the Function.  This is the specification used when
            // the function is invoked as a constructor (i.e. var obj = new my_func()).
            //
            // Note: it seems this needs to be set before the Function is actually created
            // (http://code.google.com/p/v8/issues/detail?id=262).
            //
            Local<ObjectTemplate> objInst = templ->InstanceTemplate();
            objInst->SetInternalFieldCount(1);
 
            // Access the Javascript prototype for the function - i.e. my_func.prototype - 
            // and add the necessary properties and methods.
            //
            Local<Template> proto_t = templ->PrototypeTemplate();
            proto_t->Set("createElement",  FunctionTemplate::New(createElement));
            proto_t->Set("getElementById", FunctionTemplate::New(getElementById));

Adding a method

In short, a method is just like a function, but it needs a this pointer. The this pointer is accessible as the Holder() in the Arguments object during the invokation of a method.

LxEngine uses a _nativeThis() helper to grab the this object. Note, the code for returning the value from createElement() will be covered later.

        /*
            Wraps document.createElement()
         */
        static v8::Handle<v8::Value> 
        createElement (const v8::Arguments& args)
        {
            Document* pThis     = _nativeThis<Document>(args);
            std::string name    = _marshal(args[0]);
 
            ElementPtr spElem   = pThis->createElement(name);
 
            ...
        }

And the listing for _nativeThis() is below. Note: LxEngine assumes there will always be exactly 1 internal field on any natively wrapped object. Your code might decide to use multiple internal fields. There's no reason only 1 has to be used and this is merely an LxEngine convention.

    template <typename NativeType>
    static NativeType*
    _nativeThis (const v8::Arguments& args)
    {
        //
        // Assumes the function was invoked with a this object (i.e. Holder is not null) and that
        // the object was set with exactly one internal field of type T.   It is difficult to
        // verify these assumptions at runtime, so this is a somewhat dangerous function.
        //
        using namespace v8;
 
        Local<Object> self = args.Holder();
 
        lx_check_error(self->InternalFieldCount() == 1);
        Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
 
        NativeType* pThis = reinterpret_cast<NativeType*>( wrap->Value() );
        lx_check_error(pThis != nullptr);
 
        return pThis;
    }

Creating the object

Essentially we now need to write the C++ equivalent of...

var document = new Document();

First note that the 'class' created earlier in the tutorial is not really called "Document" - it was never assigned a name as far as the Javascript client code is concerned. It's essentially an anonymous class. This is good since we don't want the user creating their own "Document" objects - we're just exposing one singleton object under the "document" variable name. However, for conceptual purposes let's think of that Function created above as the "Document" class, even though we never expose it to the JS client code with any name,

  1. GetFunction() is called on the FunctionTemplate to actually instantiate the real Function from the template descriptor. This represents the Javascript object for the function. (Note the code comment: some code must be set on the template before the Function is every created. Note also: only one instantiation of a template can occur per context.).
  2. Now that we have the function we can call it. But we actually want to call "new" on it, not directly invoke it. That's precisely what NewInstance() does.
  3. At this point we now have an anonymous Javascript object of the "Document" type...
  4. Next, bind it to the native C++ object by setting that internal field we allocated
  5. Next, assign it a name in the global object so that it can be accessed from Javascript using the name "document"
            // Now grab a handle to the Function.  This will invoke the FunctionTemplate
            // to create the actual function.  Then call NewInstance, which is
            // the C++ equivalent of "new my_func()".   Then, since this is a wrapper on a
            // C++ object, set the internal field to point to the C++ object.
            //
            Handle<Function> ctor = templ->GetFunction();
            Handle<Object> obj = ctor->NewInstance();
            obj->SetInternalField(0, External::New(spDocument.get()));
 
            // Create a name for the object in the global namespace (i.e. global variable).
            //
            context->Global()->Set(String::New("document"), obj);

Note in the last line of code we are accessing the context global object, the global object template. The free function __lx_print was added to the object template prior to the object being created. Here the "document" object is added to the global object itself. In V8, it's usually important to understand what needs to be added to templates versus the actual objects.

Adding JS allocated native C++ objects

var elem = document.createElement();

When the javascript code calls "document.createElement()", a native Element object needs to be created and wrapped in a Javascript object. Let's cover how to do that.

Setting up the class

This is similar to creating the document object

  1. Create the FunctionTemplate representing the "class"
  2. Allocate an internal field to store the native pointer on the InstanceTemplate
  3. Add the methods to the prototype

...but then there's one more thing done here

  1. Store a Persistent<Function> handle for the function

Why the last step? We need to hang on to the Function so that Function::NewInstance() can be called from our createElement() implementation. In the code below, the LxEngine uses a static s_pActiveContext object to store that Function for duration of the script invokation.

                Handle<FunctionTemplate> templ = FunctionTemplate::New();
 
                // Create an anonymous type which will be used for the Element wrapper
                Local<ObjectTemplate> objInst = templ->InstanceTemplate();
                objInst->SetInternalFieldCount(1);
 
                // Access the Javascript prototype for the function - i.e. my_func.prototype - 
                // and add the necessary properties and methods.
                //
                Local<Template> proto_t = templ->PrototypeTemplate();
                proto_t->Set("setAttribute",  FunctionTemplate::New(setAttribute));
                proto_t->Set("appendChild", FunctionTemplate::New(appendChild));
 
                // Store a persistent reference to the function which will be used to create
                // new object wrappers
                s_pActiveContext->mElementCtor = Persistent<Function>( templ->GetFunction() );

Allocating a new object

Let's go back to document.createElement():

  1. Grab the native Document*
  2. Marshal the JS arguments to native C++ types
  3. Call the native method

...now, how to expose that newly allocated C++ Element object?

Two things happen:

  1. Store a reference to the newly allocated object
  2. Call the _wrapObject() helper to create the JS wrapper

We'll examine both those steps after the code listing:

        /*
            Wraps document.createElement()
         */
        static v8::Handle<v8::Value> 
        createElement (const v8::Arguments& args)
        {
            Document* pThis     = _nativeThis<Document>(args);
            std::string name    = _marshal(args[0]);
 
            ElementPtr spElem   = pThis->createElement(name);
 
            // The Javascript could continue to reference this open.   Keep a reference around to
            // the element for the duration of the V8 context.   (The alternative would be to use
            // V8 Persistent weak pointers that provide a callback on the last reference being
            // removed.)
            //
            s_pActiveContext->mElements.push_back(spElem);
 
            return _wrapObject(s_pActiveContext->mElementCtor, spElem.get());
        }

Storing a reference to the allocated object

This is a bit of a LxEngine idiosyncrasy, but it exposes something good to understand about V8.

Reference Counting

LxEngine uses std::shared_ptr<> on all its major objects to provide 'automatic' memory management of its resources. This is generally very helpful as it keeps things simple and straightforward. But here's the problem: V8 exposes that C++ variable as a void*, not as a std::shared_ptr<>. In other words, we have no way of knowing when a JS variable is potentially referencing the native C++ object or not. This invalidates the reference counting: in general, if any code using an object is using reference coding, then all code using those objects has to use reference counting.

The 'right' way of handling this may be to use a V8 PersistentHandle, which can be made into a weak pointer - which V8 allows a callback to be registered on when that handle goes out of scope...but that's not what the code in this article does. At least in this revision of the article, that is.

The code in this article simply keeps a list of all native objects allocated during the run of the script. It clears this list at the end of the script run. What this means is: (a) it's very simple and straightforward - all allocated native objects are guaranteed to exist as long as the script, (b) an allocated native object will still be in memory for the duration of the script, even if the JS code is no longer referencing it.

Again, it's not necessarily an ideal solution, but it's keeping things simple for the purposes of this article. For the purposes of learning to embed Google V8, suffice to say, that (a) V8 does not try to manage the memory of your native objects and (b) there is a way to use PersistentHandle to get notification on a garbage collected object but this articles does not cover that.

Wrapping the object

This is pretty simple, as it's very similar to the "document" object but we don't have to assign the created object a name.

  1. Call NewInstance on the Function (i.e. equivalent to new my_function())
  2. Set the internal field to the value of the native C++ object
  3. Return a Handle to the object
    static v8::Handle<v8::Object>
    _wrapObject (v8::Handle<v8::Function>& ctor, void* pNative)
    {
        v8::Handle<v8::Object> obj = ctor->NewInstance();
        obj->SetInternalField(0, v8::External::New(pNative));
 
        return obj;
    }

Useful Techniques

Synchronizing Native Object Reference Counts with the V8 Garbage Collector

I'll try to update the wiki with a direct description, but for now see the post Synchronizing Native Reference Counts with V8 on the blog.

In short, look into the Persistent::MakeWeak() method.

Wrapping a Javascript function as a std::function<>

See this blog post, or send leave feedback if you'd like more information in this section.

Adding the Javascript Math library

Please leave some feedback and I'll add this section.

Getting Useful Error Reporting

to be completed

Conclusion

This article was put together hastily - but given my own difficulty in find more examples of V8 embedding, I thought posting a hasty article would be more helpful than not posting anything.

If you have feedback, questions, or suggestions, please post them to the corresponding blog entry for this article:

http://athile.net/library/blog/?p=159

Hearing from others would be motivation to spend some time polishing this article up.

References

Navigation
Toolbox