More Javascript module documentation.
This commit is contained in:
parent
02c55fc52f
commit
fa8b350cd6
1 changed files with 193 additions and 232 deletions
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue