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.
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<v8::Object> exports)
```
void example_initialize (v8::Handle<v8::Object> 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.