git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/branches/oliverb-javascript-v8@13747 626c5289-ae23-0410-ae9c-e8d60b6d4f22
19 KiB
Javascript: Specification of a Code Generator for V8
The aim of this is to evolve a specification for a code generator.
Top Level structure
The generated code consists of the following blocks:
<HELPER_FUNCTIONS>
<INCLUDES>
<CLASS_TEMPLATES>
<FUNCTION_WRAPPERS>
<INITIALIZER>
HELPER_FUNCTIONS: static, from swg-fileINCLUDES: static, module propertyCLASS_TEMPLATES: dynamically growing, on class declarationsFUNCTION_WRAPPERS: dynamically growing, on method declarationsINITIALIZER: dynamically growing, aggregates everything (to be specified in more detail)
INCLUDES
#include <v8.h>
<USER_DEFINED_INCLUDES>
USER_DEFINED_INCLUDES: a module property
CLASS_TEMPLATES
Static references to class templates which are (should be) read-only and can be reused.
v8::Persistent<v8::FunctionTemplate> SWIGV8_${NAME_MANGLED};
Notes:
- it is very important to consider namespaces from the beginning.
NAME_MANGLEDis the mangled qualified class name, e.g.,foo_bar_MyClassforfoo::bar::MyClass, which is retrieved nySwig_string_mangle(Getattr(n, "name")) - namespaces do not need a function template, as they will not be instantiated
FUNCTION_WRAPPERS
There are different types of function wrappers:
- Global Functions (global/namespace/class)
- Constructors / Destructors
- Getters / Settters
- Member Functions
Global Functions
v8::Handle<v8::Value> wrap_${NAME_MANGLED}(const v8::Arguments &args) {
v8::HandleScope scope;
v8::Handle<v8::Value> ret;
${LOCALS}
${MARSHAL_INPUT}
${ACTION}
${MARSHAL_OUTPUT}
return scope.Close(ret);
}
Constructors
v8::Handle<v8::Value> ${NAME_MANGLED}_new(const v8::Arguments& args) {
v8::HandleScope scope;
v8::Handle<v8::Object> self = args.Holder();
${LOCALS}
${MARSHAL_INPUT}
${ACTION}
self->SetInternalField(0, v8::External::New(ptr));
return self;
}
LOCALS: declaration and marshalling for input argumentsMARSHAL_INPUT: code is generated by applying input typemapsACTION: the C/C++ ctor to be executed
Destructors
TODO: I haven't found out yet how a descrtuctor can be registered
Getters
v8::Handle<v8::Value> ${NAME_MANGLED}_get(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
v8::HandleScope scope;
v8::Handle<v8::Object> ret;
${LOCALS}
${ACTION}
${MARSHAL_OUTPUT}
return scope.Close(ret);
}
NAME_MANGLED: the qualified mangled name of the variable, E.g.,foo::x -> foo_x,A.x -> A_xLOCALS: declare C return variableMARSHAL_OUTPUT: code is generated by applying output typemaps
Setters
void ${NAME_MANGLED}_set(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
${LOCALS}
${MARSHAL_INPUT}
${ACTION}
}
NAME_MANGLED: the qualified mangled name of the variable, E.g.,foo::x -> foo_x,A.x -> A_xLOCALS: declarations for input argumentsMARSHAL_INPUT: code is generated by applying input typemaps
Functions
v8::Handle<v8::Value> ${NAME_MANGLED}(const Arguments &args) {
v8::HandleScope scope;
v8::Handle<v8::Object> ret;
${LOCALS}
${MARSHAL_INPUT}
${ACTION}
${MARSHAL_OUTPUT}
return scope.Close(ret);
}
- if the function does not have a return value, return v8::Undefined
Overloading
TODO: if a function or a ctor is overloaded, a dispatcher function must be generated which calls overloading wrappers depending on number and type of input arguments.
Initializer
void ${MODULE}_Initialize(v8::Handle<v8::Context> context)
{
v8::HandleScope scope;
// register the module in globale context
v8::Local<v8::Object> global = context->Global();
${PART_NAMESPACES}
${PART_CLASS_TEMPLATES}
${PART_WRAPPERS}
${PART_INHERITANCE}
${PART_REGISTER}
}
Namespaces
Namespaces are objects without class templates. I.e., instances are created, referenced locally, used as contexts for other registrations, and stored in the according parent contexts.
v8::Handle<v8::ObjectTemplate> ${NAME_MANGLED} = v8::ObjectTemplate::New();
Class Templates
SWIGV8_${NAME_MANGLED} = SWIGV8_CreateClassTemplate("${NAME_UNQUALIFIED}" , ${NAME_MANGLED}_new);
NAME_UNQUALIFIED: the class name without context, i.e., namespaces
Inheritance
SWIGV8_${NAME_MANGLED}->Inherit(SWIGV8_${BASE_CLASS});
- Note: multiple inheritance is not possible; thus we will always take the first parent class
Registration
The registration part consists of registering classes at contexts (i.e., global or namespace), methods and properties at classes or contexts, and namespaces as objects at parent contexts.
Global Variable
${CONTEXT}->SetAccessor(v8::String::NewSymbol("${NAME_UNQUALIFIED}"), ${GETTER}, ${SETTER});
CONTEXT: either global, or the according namespace template${SETTER} = 0for read-only variables
Global Function
${CONTEXT}->Set(v8::String::NewSymbol("${NAME_UNQUALIFIED}"), v8::FunctionTemplate::New(wrap_${NAME_QUALIFIED})->GetFunction());
CONTEXT: either global, or the according namespace template
Class
${CONTEXT}->Set(v8::String::NewSymbol("${NAME_UNQUALIFIED}", SWIGV8_${NAME_MANGLED}->GetFunction()));
- Note: every class template has an associated ctor function wrapper, which is registered here
CONTEXT: either global, or the according namespace instance
Class method
SWIGV8_AddClassMethod(SWIGV8_${CLASSNAME_MANGLED}, "${NAME_UNQUALIFIED}", wrap_${NAME_MANGLED});
Note: implemented in static helper function
Class variable
SWIGV8_AddProperty(SWIGV8_${CLASSNAME_MANGLED}, "${NAME_UNQUALIFIED}", ${GETTER}, ${SETTER});
GETTER: the name of the generated wrapper for the property getterSETTER: the name of the generated wrapper for property setter; optional (i.e., maybeNULL)
Namespace
${CONTEXT}->Set(v8::String::NewSymbol("${NAME_UNQUALIFIED}", ${NAME_MANGLED}->NewInstance()));
Note: it is important to register the namespace objects in reverse order, e.g.,
namespace foo {
namespace bar {}
}
would be registered in this order:
foo->Set(v8::String::NewSymbol("bar", bar->NewInstance()));
global->Set(v8::String::NewSymbol("foo", foo->NewInstance()));
HELPER_FUNCTIONS
A lot of boiler-plate code can be shifted into static helper functions:
/**
* Creates a class template for a class without extra initialization function.
*/
v8::Persistent<v8::FunctionTemplate> SWIGV8_CreateClassTemplate(const char* symbol) {
v8::Local<v8::FunctionTemplate> class_templ = v8::FunctionTemplate::New();
class_templ->SetClassName(v8::String::NewSymbol(symbol));
v8::Handle<v8::ObjectTemplate> inst_templ = class_templ->InstanceTemplate();
inst_templ->SetInternalFieldCount(1);
return v8::Persistent<v8::FunctionTemplate>::New(class_templ);
}
/**
* Creates a class template for a class with specified initialization function.
*/
v8::Persistent<v8::FunctionTemplate> SWIGV8_CreateClassTemplate(const char* symbol, v8::InvocationCallback _func) {
v8::Local<v8::FunctionTemplate> class_templ = v8::FunctionTemplate::New(_func);
class_templ->SetClassName(v8::String::NewSymbol(symbol));
v8::Handle<v8::ObjectTemplate> inst_templ = class_templ->InstanceTemplate();
inst_templ->SetInternalFieldCount(1);
return v8::Persistent<v8::FunctionTemplate>::New(class_templ);
}
/**
* Sets the pimpl data of a V8 class.
*/
v8::Handle<v8::Value> V8GeneratorUtils::SetInstance(const v8::Arguments& args, void* data) {
v8::HandleScope scope;
v8::Handle<v8::Object> self = args.Holder();
self->SetInternalField(0, v8::External::New(data));
return self;
}
/**
* Registers a class method with given name for a given class template.
*/
void V8GeneratorUtils::AddClassMethod(v8::Handle<v8::FunctionTemplate> class_templ, const char* symbol, v8::InvocationCallback _func) {
v8::Handle<v8::ObjectTemplate> proto_templ = class_templ->PrototypeTemplate();
proto_templ->Set(v8::String::NewSymbol(symbol), v8::FunctionTemplate::New(_func));
}
/**
* Registers a class property with given name for a given class template.
*/
void V8GeneratorUtils::AddProperty(v8::Handle<v8::FunctionTemplate> class_templ, const char* varname, v8::AccessorGetter getter, v8::AccessorSetter setter) {
v8::Handle<v8::ObjectTemplate> proto_templ = class_templ->InstanceTemplate();
proto_templ->SetAccessor(v8::String::New(varname), getter, setter);
}
Examples
In this chapter manually coded wrappers are presented. This has been useful for studying the addressed code generation on basis of examples.
Global variable
static double Foo = 42.0;
void Foo_set(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
v8::HandleScope scope;
double arg1 ;
arg1 = value->NumberValue();
Foo = arg1;
}
v8::Handle<v8::Value> Foo_get(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
v8::HandleScope scope;
v8::Handle<v8::Value> ret;
double result;
result = Foo;
ret = v8::Number::New(result);
return scope.Close(ret);
}
int GlobalVar_Initialize(v8::Handle<v8::Context> context) {
v8::Local<v8::Object> global = context->Global();
global->SetAccessor(v8::String::New("Foo"), Foo_get, Foo_set);
return 0;
}
Global functions
static double foo(int bla) {
return (bla * 2.1);
}
v8::Handle<v8::Value> wrap_foo(const v8::Arguments &args) {
v8::HandleScope scope;
v8::Handle<v8::Value> ret;
int arg1 ;
double result;
arg1 = args[0]->Int32Value();
result = foo(arg1);
ret = v8::Number::New(result);
return scope.Close(ret);
}
int GlobalFunc_Initialize(v8::Handle<v8::Context> context) {
v8::Local<v8::Object> global = context->Global();
global->Set(v8::String::NewSymbol("foo"), v8::FunctionTemplate::New(wrap_foo)->GetFunction());
return 0;
}
Namespaces
namespace foo {
static double bar = 42.0;
}
void foo_bar_set(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
v8::HandleScope scope;
double arg1 ;
arg1 = value->NumberValue();
foo::bar = arg1;
}
v8::Handle<v8::Value> foo_bar_get(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
v8::HandleScope scope;
v8::Handle<v8::Value> ret;
double result;
result = foo::bar;
ret = v8::Number::New(result);
return scope.Close(ret);
}
int Namespace_Initialize(v8::Handle<v8::Context> context) {
v8::Local<v8::Object> global = context->Global();
v8::Handle<v8::ObjectTemplate> foo = v8::ObjectTemplate::New();
foo->SetAccessor(v8::String::New("bar"), foo_bar_get, foo_bar_set);
global->Set(v8::String::New("foo"), foo->NewInstance());
return 0;
}
Control flow analysis
Global variables
Example:
int x;
namespace foo {
double y;
}
Control flow:
Command:
swig -analyze -c++ functionWrapper +before globalvariableHandler +before example.i
enter top() of example
enter variableHandler() of x
enter globalvariableHandler() of x
| sym:name - "x"
| name - "x"
| type - "int"
enter variableWrapper() of x
enter functionWrapper() of x
| name - "x"
| sym:name - "x_set"
| parms - int
| wrap:action - "x = arg1;"
| type - "void"
exit functionWrapper() of x
enter functionWrapper() of x
| name - "x"
| sym:name - "x_get"
| type - "int"
exit functionWrapper() of x
exit variableWrapper() of x
exit globalvariableHandler() of x
exit variableHandler() of x
enter variableHandler() of foo::y
enter globalvariableHandler() of foo::y
| sym:name - "y"
| name - "foo::y"
| type - "double"
enter variableWrapper() of foo::y
enter functionWrapper() of foo::y
| name - "foo::y"
| sym:name - "y_set"
| parms - double
| wrap:action - "foo::y = arg1;"
| type - "void"
exit functionWrapper() of foo::y
enter functionWrapper() of foo::y
| name - "foo::y"
| sym:name - "y_get"
| wrap:action - "result = (double)foo::y;"
| type - "double"
exit functionWrapper() of foo::y
exit variableWrapper() of foo::y
exit globalvariableHandler() of foo::y
exit variableHandler() of foo::y
exit top() of example
Simple class
Example:
class A {
public:
A();
~A();
};
namespace foo {
class B {
};
}
Control flow:
enter top() of example
enter classHandler() of A
enter constructorHandler() of A
enter functionWrapper() of A
| name - "A"
| sym:name - "new_A"
| access - "public"
| wrap:action - "result = (A *)new A();"
| type - "p.A"
exit functionWrapper() of A
exit constructorHandler() of A
enter destructorHandler() of ~A
enter functionWrapper() of ~A
| name - "~A"
| sym:name - "delete_A"
| parms - A *
| wrap:action - "delete arg1;"
exit functionWrapper() of ~A
exit destructorHandler() of ~A
exit classHandler() of A
enter classHandler() of foo::B
enter constructorHandler() of foo::B::B
enter functionWrapper() of foo::B::B
| name - "foo::B::B"
| sym:name - "new_B"
| access - "public"
| wrap:action - "result = (foo::B *)new foo::B();"
| type - "p.foo::B"
exit functionWrapper() of foo::B::B
exit constructorHandler() of foo::B::B
enter destructorHandler() of foo::B::~B
enter functionWrapper() of foo::B::~B
| name - "foo::B::~B"
| sym:name - "delete_B"
| parms - foo::B *
| wrap:action - "delete arg1;"
| type - "void"
exit functionWrapper() of foo::B::~B
exit destructorHandler() of foo::B::~B
exit classHandler() of foo::B
exit top() of example
Example
class A {
public:
int x;
};
Control flow
enter top() of example
enter classHandler() of A
enter variableHandler() of x
enter membervariableHandler() of x
enter functionWrapper() of x
| name - "x"
| sym:name - "A_x_set"
| access - "public"
| parms - A *,int
| wrap:action - "if (arg1) (arg1)->x = arg2;"
| type - "void"
| memberset - "1"
exit functionWrapper() of x
enter functionWrapper() of x
| name - "x"
| sym:name - "A_x_get"
| access - "public"
| parms - A *
| wrap:action - "result = (int) ((arg1)->x);"
| type - "int"
| memberset - "1"
| memberget - "1"
exit functionWrapper() of x
exit membervariableHandler() of x
exit variableHandler() of x
enter constructorHandler() of A::A
enter functionWrapper() of A::A
exit functionWrapper() of A::A
exit constructorHandler() of A::A
enter destructorHandler() of A::~A
enter functionWrapper() of A::~A
exit functionWrapper() of A::~A
exit destructorHandler() of A::~A
exit classHandler() of A
exit top() of example
Class method
Example
class A {
public:
void foo(int x, double y);
private:
void bar();
};
Control flow
enter top() of example
enter classHandler() of A
enter functionHandler() of foo
enter memberfunctionHandler() of foo
enter functionWrapper() of foo
| name - "foo"
| sym:name - "A_foo"
| access - "public"
| parms - A *,int,double
| wrap:action - "(arg1)->foo(arg2,arg3);"
| type - "void"
exit functionWrapper() of foo
exit memberfunctionHandler() of foo
exit functionHandler() of foo
...
exit classHandler() of A
exit top() of example
Static class variable and method
Example
class A {
public:
static int x;
static void foo();
};
Control flow
enter top() of example
enter classHandler() of A
enter variableHandler() of x
enter staticmembervariableHandler() of x
enter variableWrapper() of A::x
enter functionWrapper() of A::x
| name - "A::x"
| sym:name - "A_x_set"
| parms - int
| wrap:action - "A::x = arg1;"
| type - "void"
exit functionWrapper() of A::x
enter functionWrapper() of A::x
+++ cdecl ----------------------------------------
| name - "A::x"
| ismember - "1"
| sym:name - "A_x_get"
| variableWrapper:sym:name - "A_x"
| wrap:action - "result = (int)A::x;"
| type - "int"
exit functionWrapper() of A::x
exit variableWrapper() of A::x
exit staticmembervariableHandler() of x
exit variableHandler() of x
enter functionHandler() of foo
enter staticmemberfunctionHandler() of foo
enter globalfunctionHandler() of A::foo
enter functionWrapper() of A::foo
| name - "A::foo"
| sym:name - "A_foo"
| wrap:action - "A::foo();"
| type - "void"
exit functionWrapper() of A::foo
exit globalfunctionHandler() of A::foo
exit staticmemberfunctionHandler() of foo
exit functionHandler() of foo
enter constructorHandler() of A::A
exit constructorHandler() of A::A
enter destructorHandler() of A::~A
exit destructorHandler() of A::~A
exit classHandler() of A
exit top() of example
Inheritance
Example
class A {
};
class B: public A {
};
Control flow
enter top() of example
enter classHandler() of A
+++ class ----------------------------------------
| name - "A"
| sym:name - "A"
...
exit classHandler() of A
enter classHandler() of B
| name - "B"
| sym:name - "B"
| privatebaselist - 0xf1238f10
| protectedbaselist - 0xf1238ef0
| baselist - 0xf1238ed0
| bases - 0xf1239830
| allbases - 0xf1239c30
...
exit classHandler() of B
exit top() of example