diff --git a/Doc/Manual/Javascript.md b/Doc/Manual/Javascript.md index e1622aa88..b571e05c3 100644 --- a/Doc/Manual/Javascript.md +++ b/Doc/Manual/Javascript.md @@ -1,9 +1,9 @@ -% SWIG and Javascript +# SWIG and Javascript This chapter describes SWIG's support of Javascript. It does not cover SWIG basics only information that is specific to this module. -# Overview +## Overview JavaScript is a prototype-based scripting language that is dynamic, weakly typed and has first-class functions. Its arguably the most popular language for web development. @@ -14,42 +14,36 @@ Native Javascript extensions can be used for applications that embed a web-brows that embed a Javascript engine (such as *node.js*). Extending a general purpose web-browser is not possible as this would be severe security issue. -With [WebKit](http://www.webkit.org/) there is an modern and open-source browser -implementations available which can be embedded into an application. -At the moment, [Chromium Embedded Framework](http://code.google.com/p/chromiumembedded/) -can not be extended as CEF does not provide access to the V8 engine, but instead comes with -its own extension mechanism. - SWIG Javasript currently supports **JavascriptCore**, the Javascript engine used by `Safari`, and **v8**, which is used by `Chromium` and `node.js`. -# Preliminaries +With [WebKit](http://www.webkit.org/) there is a modern browser +implementation available as open-source which can be embedded into an application. +Unfortunately, [Chromium Embedded Framework](http://code.google.com/p/chromiumembedded/) +does not provide access to the native V8 engine, making it impossible to extend the engine +using the Javascript module. -# Running SWIG +## Preliminaries + +### Running SWIG Suppose that you defined a SWIG module such as the following: -```code -%module example -%{ -#include "example.h" -%} -int gcd(int x, int y); -extern double Foo; -``` + %module example + %{ + #include "example.h" + %} + int gcd(int x, int y); + extern double Foo; To build a Javascript module, run SWIG using the `-javascript` option and a desired target engine `-jsc` or `-v8`. -```shell -$ swig -javascript -jsc example.i -``` + $ swig -javascript -jsc example.i If building a C++ extension, add the -c++ option: -```shell -$ swig -c++ -javascript -jsc example.i -``` + $ swig -c++ -javascript -jsc example.i This creates a C/C++ source file example_wrap.c or example_wrap.cxx. The generated C source file contains the low-level wrappers that need to be compiled and linked with the rest of your C/C++ application to create an extension module. @@ -59,15 +53,11 @@ To change this, you can use the -o option. The wrapped module will export one function which must be called to register the module with the Javascript interpreter. For example, if your module is named `example` the corresponding initializer for JavascriptCore would be -```code -bool example_initialize(JSGlobalContextRef context, JSObjectRef *exports) -``` + bool example_initialize(JSGlobalContextRef context, JSObjectRef *exports) and for v8: -```code -void example_initialize (v8::Handle exports) -``` + void example_initialize (v8::Handle exports) ## Missing features @@ -75,26 +65,22 @@ The Javascript module is not yet as mature as other modules and some things are As it makes use of Swigs Unified typemap library (UTL), many typemaps are inherited. - Director support -- TODO: there is more +- TODO: hmpf... I suppose there is more +## Compilation and Linking -# Compilation and Linking +### Installation -## Installation - - - -## Dealing with `v8` version incompatibilities +### Dealing with `v8` version incompatibilities Unfortunately, v8 does not provide pre-processor macros do detect which version you link to. Therefore, you have to provide this information manually. - -# Integration +## Integration This should give a short overview how to integrate your module in different environments: as a `node.js` module, and as an extension for an embedded Chromium. -## Creating `node.js` Extensions +### Creating `node.js` Extensions As `v8` is written in C++ and comes as a C++ library it is crucial to compile your module using the same compiler flags as used for building v8. To make things easier, `node.js` provides a build tool called `node-gyp`. @@ -103,138 +89,132 @@ This expects configuration file named `binding.gyp` which is basically in JSON f conforms to the same format that is used with Google's build-tool `gyp`. `binding.gyp`: -```code -{ - "targets": [ + { - "target_name": "example", - "sources": [ "example.cxx", "example_wrap.cxx" ] + "targets": [ + { + "target_name": "example", + "sources": [ "example.cxx", "example_wrap.cxx" ] + } + ] } - ] -} -``` First you would create the wrapper using SWIG: -```shell -``` - -## Embedded Webkit +### Embedded Webkit TODO: Here a minimal example of how to implement -# Implementation +## Implementation The Javascript Module implementation has take a very different approach than other modules to be able to generate code for different Javascript interpreters. -## Module Source Code +### Module Source Code The Javascript module is implemented in `Source/Modules/javascript.cxx`. It dispatches the code generation to a `JSEmitter` instance, `V8Emitter` or `JSCEmitter`. Additionally there are some helpers: `Template`, for templated code generation, and `JSEmitterState`, which is used to manage state information during AST traversal. This is a rough map shall make it easier to find a way through this huge source file: -```code -// module wide defines + // module wide defines -#define NAME "name" + #define NAME "name" -... + ... -// Helper class declarations -class JSEmitterState { ... }; + // Helper class declarations + class JSEmitterState { ... }; -class Template { ... }; + class Template { ... }; -// JSEmitter declaration + // JSEmitter declaration -class JSEmitter { ... }; + class JSEmitter { ... }; -// Emitter factory declarations + // Emitter factory declarations -JSEmitter *swig_javascript_create_JSCEmitter(); -JSEmitter *swig_javascript_create_V8Emitter(); + JSEmitter *swig_javascript_create_JSCEmitter(); + JSEmitter *swig_javascript_create_V8Emitter(); -// Javascript module class declaration + // Javascript module class declaration -class JAVASCRIPT:public Language { ... }; + class JAVASCRIPT:public Language { ... }; -// Javascript module function definitions + // Javascript module function definitions -int JAVASCRIPT::functionWrapper(Node *n) { ... } + int JAVASCRIPT::functionWrapper(Node *n) { ... } -... + ... -// Module factory implementations + // Module factory implementations -static Language *new_swig_javascript() { ... } + static Language *new_swig_javascript() { ... } -extern "C" Language *swig_javascript(void) { ... } + extern "C" Language *swig_javascript(void) { ... } -// JSEmitter implementation + // JSEmitter implementation -JSEmitter::JSEmitter() { ... } + JSEmitter::JSEmitter() { ... } -Template JSEmitter::getTemplate(const String *name) { ... } + Template JSEmitter::getTemplate(const String *name) { ... } -... + ... -// JSCEmitter declaration + // JSCEmitter declaration -class JSCEmitter: public JSEmitter { ... }; + class JSCEmitter: public JSEmitter { ... }; -// JSCEmitter implementation + // JSCEmitter implementation -JSCEmitter::JSCEmitter() { ... } + JSCEmitter::JSCEmitter() { ... } -void JSCEmitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) { ... } + void JSCEmitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) { ... } -... + ... -// JSCEmitter factory + // JSCEmitter factory -JSEmitter *swig_javascript_create_JSCEmitter() { ... } + JSEmitter *swig_javascript_create_JSCEmitter() { ... } -// V8Emitter declaration + // V8Emitter declaration -class V8Emitter: public JSEmitter { ... }; + class V8Emitter: public JSEmitter { ... }; -// V8Emitter implementation + // V8Emitter implementation -V8Emitter::V8Emitter() { ... } + V8Emitter::V8Emitter() { ... } -int V8Emitter::initialize(Node *n) { ... } + int V8Emitter::initialize(Node *n) { ... } -// V8Emitter factory + // V8Emitter factory -JSEmitter *swig_javascript_create_V8Emitter() { ... } + JSEmitter *swig_javascript_create_V8Emitter() { ... } -// Helper implementation (JSEmitterState, Template) + // Helper implementation (JSEmitterState, Template) -JSEmitterState::JSEmitterState() { ... } + JSEmitterState::JSEmitterState() { ... } -... + ... -Template::Template(const String *code_) { ... } + Template::Template(const String *code_) { ... } -``` -## Code Templates +### Code Templates All generated code is created on the basis of code templates. The templates for *JavascriptCore* can be found in `Lib/javascript/jsc/javascriptcode.swg`, @@ -242,213 +222,194 @@ for *v8* in `Lib/javascript/v8/javascriptcode.swg`. To track the originating code template for generated code you can run -```shell -swig -javascript -jsc -debug-codetemplates -``` + $ swig -javascript -jsc -debug-codetemplates which wraps generated code with a descriptive comment -```code -/* begin fragment("temlate_name") */ + /* begin fragment("temlate_name") */ -...generated code ... + ...generated code ... -/* end fragment("temlate_name") */ -``` + /* end fragment("temlate_name") */ The Template class is used like this: -```code - Template t_register = getTemplate("jsv8_register_static_variable"); - t_register.replace("$jsparent", state.clazz(NAME_MANGLED)) - .replace("$jsname", state.variable(NAME)) - .replace("$jsgetter", state.variable(GETTER)) - .replace("$jssetter", state.variable(SETTER)) - .trim(). - print(f_init_static_wrappers); -``` + Template t_register = getTemplate("jsv8_register_static_variable"); + t_register.replace("$jsparent", state.clazz(NAME_MANGLED)) + .replace("$jsname", state.variable(NAME)) + .replace("$jsgetter", state.variable(GETTER)) + .replace("$jssetter", state.variable(SETTER)) + .trim(). + print(f_init_static_wrappers); A code template is registered with the *JSEmitter* via `fragment(name, "template")`, e.g., -```code -%fragment ("jsc_variable_declaration", "templates") -%{ - {"$jsname", $jsgetter, $jssetter, kJSPropertyAttributeNone}, -%} -``` + %fragment ("jsc_variable_declaration", "templates") + %{ + {"$jsname", $jsgetter, $jssetter, kJSPropertyAttributeNone}, + %} `Template` creates a copy of that string and `Template::replace` uses Swig's `Replaceall` to replace variables in the template. `Template::trim` can be used to eliminate leading and trailing whitespaces. `Template::print` is used to write the final template string to a Swig `DOH` (based on `Printv`). All methods allow chaining. - -## Emitter +### Emitter The Javascript module delegates code generation to a `JSEmitter` instance. The following extract shows the essential interface: -```code -class JSEmitter { - ... + class JSEmitter { + ... - /** - * Opens output files and temporary output DOHs. - */ - virtual int initialize(Node *n); + /** + * Opens output files and temporary output DOHs. + */ + virtual int initialize(Node *n); - /** - * Writes all collected code into the output file(s). - */ - virtual int dump(Node *n) = 0; + /** + * Writes all collected code into the output file(s). + */ + virtual int dump(Node *n) = 0; - /** - * Cleans up all open output DOHs. - */ - virtual int close() = 0; + /** + * Cleans up all open output DOHs. + */ + virtual int close() = 0; - ... + ... - /** - * Invoked at the beginning of the classHandler. - */ - virtual int enterClass(Node *); + /** + * Invoked at the beginning of the classHandler. + */ + virtual int enterClass(Node *); - /** - * Invoked at the end of the classHandler. - */ - virtual int exitClass(Node *) { - return SWIG_OK; - }; + /** + * Invoked at the end of the classHandler. + */ + virtual int exitClass(Node *) { + return SWIG_OK; + }; - /** - * Invoked at the beginning of the variableHandler. - */ - virtual int enterVariable(Node *); + /** + * Invoked at the beginning of the variableHandler. + */ + virtual int enterVariable(Node *); - /** - * Invoked at the end of the variableHandler. - */ - virtual int exitVariable(Node *) { - return SWIG_OK; - }; + /** + * Invoked at the end of the variableHandler. + */ + virtual int exitVariable(Node *) { + return SWIG_OK; + }; - /** - * Invoked at the beginning of the functionHandler. - */ - virtual int enterFunction(Node *); + /** + * Invoked at the beginning of the functionHandler. + */ + virtual int enterFunction(Node *); - /** - * Invoked at the end of the functionHandler. - */ - virtual int exitFunction(Node *) { - return SWIG_OK; - }; + /** + * Invoked at the end of the functionHandler. + */ + virtual int exitFunction(Node *) { + return SWIG_OK; + }; - /** - * Invoked by functionWrapper callback after call to Language::functionWrapper. - */ - virtual int emitWrapperFunction(Node *n); + /** + * Invoked by functionWrapper callback after call to Language::functionWrapper. + */ + virtual int emitWrapperFunction(Node *n); - /** - * Invoked from constantWrapper after call to Language::constantWrapper. - **/ - virtual int emitConstant(Node *n); + /** + * Invoked from constantWrapper after call to Language::constantWrapper. + **/ + virtual int emitConstant(Node *n); - /** - * Registers a given code snippet for a given key name. - * - * This method is called by the fragmentDirective handler - * of the JAVASCRIPT language module. - **/ - int registerTemplate(const String *name, const String *code); + /** + * Registers a given code snippet for a given key name. + * + * This method is called by the fragmentDirective handler + * of the JAVASCRIPT language module. + **/ + int registerTemplate(const String *name, const String *code); - /** - * Retrieve the code template registered for a given name. - */ - Template getTemplate(const String *name); + /** + * Retrieve the code template registered for a given name. + */ + Template getTemplate(const String *name); - State &getState(); + State &getState(); - ... + ... -} -``` + } The module calls `initialize`, `dump`, and `close` from within the `top` method: -```code -int JAVASCRIPT::top(Node *n) { - emitter->initialize(n); + int JAVASCRIPT::top(Node *n) { + emitter->initialize(n); - Language::top(n); + Language::top(n); - emitter->dump(n); - emitter->close(); + emitter->dump(n); + emitter->close(); - return SWIG_OK; -} -``` + return SWIG_OK; + } The methods `enterClass` and `exitClass` are called from within the `classHandler` method: -```code -int JAVASCRIPT::classHandler(Node *n) { + int JAVASCRIPT::classHandler(Node *n) { - emitter->enterClass(n); - Language::classHandler(n); - emitter->exitClass(n); + emitter->enterClass(n); + Language::classHandler(n); + emitter->exitClass(n); - return SWIG_OK; -} -``` + return SWIG_OK; + } In `enterClass` the emitter stores state information that is necessary when processing class members. In `exitClass` the wrapper code for the whole class is generated. -## Emitter states +### Emitter states For storing information during the AST traversal the emitter provides a `JSEmitterState` with different slots to store data representing the scopes global, class, function, and variable. -```code -class JSEmitterState { + class JSEmitterState { -public: + public: - JSEmitterState(); + JSEmitterState(); - ~JSEmitterState(); + ~JSEmitterState(); - DOH *global(); + DOH *global(); - DOH *global(const char* key, DOH *initial = 0); + DOH *global(const char* key, DOH *initial = 0); - DOH *clazz(bool reset = false); + DOH *clazz(bool reset = false); - DOH *clazz(const char* key, DOH *initial = 0); + DOH *clazz(const char* key, DOH *initial = 0); - DOH *function(bool reset = false); + DOH *function(bool reset = false); - DOH *function(const char* key, DOH *initial = 0); + DOH *function(const char* key, DOH *initial = 0); - DOH *variable(bool reset = false); + DOH *variable(bool reset = false); - DOH *variable(const char* key, DOH *initial = 0); + DOH *variable(const char* key, DOH *initial = 0); - static int IsSet(DOH *val); + static int IsSet(DOH *val); - ... -}; -``` + ... + }; When entering a scope, such as in `enterClass`, the corresponding state is reset and new data is stored: -```code - state.clazz(RESET); - state.clazz(NAME, Getattr(n, "sym:name")); -``` + state.clazz(RESET); + state.clazz(NAME, Getattr(n, "sym:name")); State information can be retrieved using `state.clazz(NAME)` or with `Getattr` on `state.clazz()` which actually returns a `Hash` instance.