More Javascript module documentation.

This commit is contained in:
Oliver Buchtala 2014-02-14 00:00:12 +01:00
commit fa8b350cd6

View file

@ -1,9 +1,9 @@
% SWIG and Javascript # SWIG and Javascript
This chapter describes SWIG's support of Javascript. This chapter describes SWIG's support of Javascript.
It does not cover SWIG basics only information that is specific to this module. 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 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. 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*). 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. 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`, SWIG Javasript currently supports **JavascriptCore**, the Javascript engine used by `Safari`,
and **v8**, which is used by `Chromium` and `node.js`. 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: Suppose that you defined a SWIG module such as the following:
```code %module example
%module example %{
%{ #include "example.h"
#include "example.h" %}
%} int gcd(int x, int y);
int gcd(int x, int y); extern double Foo;
extern double Foo;
```
To build a Javascript module, run SWIG using the `-javascript` option To build a Javascript module, run SWIG using the `-javascript` option
and a desired target engine `-jsc` or `-v8`. 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: 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. 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. 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 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: and for v8:
```code void example_initialize (v8::Handle<v8::Object> exports)
void example_initialize (v8::Handle<v8::Object> exports)
```
## Missing features ## 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. As it makes use of Swigs Unified typemap library (UTL), many typemaps are inherited.
- Director support - 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. Unfortunately, v8 does not provide pre-processor macros do detect which version you link to.
Therefore, you have to provide this information manually. 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. 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 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`. 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`. conforms to the same format that is used with Google's build-tool `gyp`.
`binding.gyp`: `binding.gyp`:
```code
{
"targets": [
{ {
"target_name": "example", "targets": [
"sources": [ "example.cxx", "example_wrap.cxx" ] {
"target_name": "example",
"sources": [ "example.cxx", "example_wrap.cxx" ]
}
]
} }
]
}
```
First you would create the wrapper using SWIG: First you would create the wrapper using SWIG:
```shell
``` ### Embedded Webkit
## Embedded Webkit
TODO: Here a minimal example of how to implement TODO: Here a minimal example of how to implement
# Implementation ## Implementation
The Javascript Module implementation has take a very different approach than other modules The Javascript Module implementation has take a very different approach than other modules
to be able to generate code for different Javascript interpreters. 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`. 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. 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: 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 // Helper class declarations
class JSEmitterState { ... }; 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_JSCEmitter();
JSEmitter *swig_javascript_create_V8Emitter(); 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. All generated code is created on the basis of code templates.
The templates for *JavascriptCore* can be found in `Lib/javascript/jsc/javascriptcode.swg`, 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 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 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: The Template class is used like this:
```code Template t_register = getTemplate("jsv8_register_static_variable");
Template t_register = getTemplate("jsv8_register_static_variable"); t_register.replace("$jsparent", state.clazz(NAME_MANGLED))
t_register.replace("$jsparent", state.clazz(NAME_MANGLED)) .replace("$jsname", state.variable(NAME))
.replace("$jsname", state.variable(NAME)) .replace("$jsgetter", state.variable(GETTER))
.replace("$jsgetter", state.variable(GETTER)) .replace("$jssetter", state.variable(SETTER))
.replace("$jssetter", state.variable(SETTER)) .trim().
.trim(). print(f_init_static_wrappers);
print(f_init_static_wrappers);
```
A code template is registered with the *JSEmitter* via `fragment(name, "template")`, e.g., A code template is registered with the *JSEmitter* via `fragment(name, "template")`, e.g.,
```code %fragment ("jsc_variable_declaration", "templates")
%fragment ("jsc_variable_declaration", "templates") %{
%{ {"$jsname", $jsgetter, $jssetter, kJSPropertyAttributeNone},
{"$jsname", $jsgetter, $jssetter, kJSPropertyAttributeNone}, %}
%}
```
`Template` creates a copy of that string and `Template::replace` uses Swig's `Replaceall` `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 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 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. to a Swig `DOH` (based on `Printv`). All methods allow chaining.
### Emitter
## Emitter
The Javascript module delegates code generation to a `JSEmitter` instance. The Javascript module delegates code generation to a `JSEmitter` instance.
The following extract shows the essential interface: The following extract shows the essential interface:
```code class JSEmitter {
class JSEmitter { ...
...
/** /**
* Opens output files and temporary output DOHs. * Opens output files and temporary output DOHs.
*/ */
virtual int initialize(Node *n); virtual int initialize(Node *n);
/** /**
* Writes all collected code into the output file(s). * Writes all collected code into the output file(s).
*/ */
virtual int dump(Node *n) = 0; virtual int dump(Node *n) = 0;
/** /**
* Cleans up all open output DOHs. * Cleans up all open output DOHs.
*/ */
virtual int close() = 0; virtual int close() = 0;
... ...
/** /**
* Invoked at the beginning of the classHandler. * Invoked at the beginning of the classHandler.
*/ */
virtual int enterClass(Node *); virtual int enterClass(Node *);
/** /**
* Invoked at the end of the classHandler. * Invoked at the end of the classHandler.
*/ */
virtual int exitClass(Node *) { virtual int exitClass(Node *) {
return SWIG_OK; return SWIG_OK;
}; };
/** /**
* Invoked at the beginning of the variableHandler. * Invoked at the beginning of the variableHandler.
*/ */
virtual int enterVariable(Node *); virtual int enterVariable(Node *);
/** /**
* Invoked at the end of the variableHandler. * Invoked at the end of the variableHandler.
*/ */
virtual int exitVariable(Node *) { virtual int exitVariable(Node *) {
return SWIG_OK; return SWIG_OK;
}; };
/** /**
* Invoked at the beginning of the functionHandler. * Invoked at the beginning of the functionHandler.
*/ */
virtual int enterFunction(Node *); virtual int enterFunction(Node *);
/** /**
* Invoked at the end of the functionHandler. * Invoked at the end of the functionHandler.
*/ */
virtual int exitFunction(Node *) { virtual int exitFunction(Node *) {
return SWIG_OK; return SWIG_OK;
}; };
/** /**
* Invoked by functionWrapper callback after call to Language::functionWrapper. * Invoked by functionWrapper callback after call to Language::functionWrapper.
*/ */
virtual int emitWrapperFunction(Node *n); virtual int emitWrapperFunction(Node *n);
/** /**
* Invoked from constantWrapper after call to Language::constantWrapper. * Invoked from constantWrapper after call to Language::constantWrapper.
**/ **/
virtual int emitConstant(Node *n); virtual int emitConstant(Node *n);
/** /**
* Registers a given code snippet for a given key name. * Registers a given code snippet for a given key name.
* *
* This method is called by the fragmentDirective handler * This method is called by the fragmentDirective handler
* of the JAVASCRIPT language module. * of the JAVASCRIPT language module.
**/ **/
int registerTemplate(const String *name, const String *code); int registerTemplate(const String *name, const String *code);
/** /**
* Retrieve the code template registered for a given name. * Retrieve the code template registered for a given name.
*/ */
Template getTemplate(const String *name); Template getTemplate(const String *name);
State &getState(); State &getState();
... ...
} }
```
The module calls `initialize`, `dump`, and `close` from within the `top` method: The module calls `initialize`, `dump`, and `close` from within the `top` method:
```code int JAVASCRIPT::top(Node *n) {
int JAVASCRIPT::top(Node *n) { emitter->initialize(n);
emitter->initialize(n);
Language::top(n); Language::top(n);
emitter->dump(n); emitter->dump(n);
emitter->close(); emitter->close();
return SWIG_OK; return SWIG_OK;
} }
```
The methods `enterClass` and `exitClass` are called from within the `classHandler` method: 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); emitter->enterClass(n);
Language::classHandler(n); Language::classHandler(n);
emitter->exitClass(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. 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 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. 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 When entering a scope, such as in `enterClass`, the corresponding state is reset and new data
is stored: is stored:
```code state.clazz(RESET);
state.clazz(RESET); state.clazz(NAME, Getattr(n, "sym:name"));
state.clazz(NAME, Getattr(n, "sym:name"));
```
State information can be retrieved using `state.clazz(NAME)` or State information can be retrieved using `state.clazz(NAME)` or
with `Getattr` on `state.clazz()` which actually returns a `Hash` instance. with `Getattr` on `state.clazz()` which actually returns a `Hash` instance.