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`: static, from swg-file - `INCLUDES`: static, module property - `CLASS_TEMPLATES`: dynamically growing, on class declarations - `FUNCTION_WRAPPERS`: dynamically growing, on method declarations - `INITIALIZER`: dynamically growing, aggregates everything (to be specified in more detail) ### INCLUDES ~~~~ #include ~~~~ `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 SWIGV8_${NAME_MANGLED}; ~~~~ Notes: - it is very important to consider namespaces from the beginning. `NAME_MANGLED` is the mangled qualified class name, e.g., `foo_bar_MyClass` for `foo::bar::MyClass`, which is retrieved ny `Swig_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 wrap_${NAME_MANGLED}(const v8::Arguments &args) { v8::HandleScope scope; v8::Handle ret; ${LOCALS} ${MARSHAL_INPUT} ${ACTION} ${MARSHAL_OUTPUT} return scope.Close(ret); } ~~~~ ### Constructors ~~~~ v8::Handle ${NAME_MANGLED}_new(const v8::Arguments& args) { v8::HandleScope scope; v8::Handle self = args.Holder(); ${LOCALS} ${MARSHAL_INPUT} ${ACTION} self->SetInternalField(0, v8::External::New(ptr)); return self; } ~~~~ - `LOCALS`: declaration and marshalling for input arguments - `MARSHAL_INPUT`: code is generated by applying input typemaps - `ACTION`: the C/C++ ctor to be executed ### Destructors TODO: I haven't found out yet how a descrtuctor can be registered ### Getters ~~~~ v8::Handle ${NAME_MANGLED}_get(v8::Local property, const v8::AccessorInfo& info) { v8::HandleScope scope; v8::Handle 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_x` - `LOCALS`: declare C return variable - `MARSHAL_OUTPUT`: code is generated by applying output typemaps ### Setters ~~~~ void ${NAME_MANGLED}_set(v8::Local property, v8::Local 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_x` - `LOCALS`: declarations for input arguments - `MARSHAL_INPUT`: code is generated by applying input typemaps ### Functions ~~~~ v8::Handle ${NAME_MANGLED}(const Arguments &args) { v8::HandleScope scope; v8::Handle 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 context) { v8::HandleScope scope; // register the module in globale context v8::Local 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 ${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} = 0` for 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 getter - `SETTER`: the name of the generated wrapper for property setter; optional (i.e., maybe `NULL`) ### 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 SWIGV8_CreateClassTemplate(const char* symbol) { v8::Local class_templ = v8::FunctionTemplate::New(); class_templ->SetClassName(v8::String::NewSymbol(symbol)); v8::Handle inst_templ = class_templ->InstanceTemplate(); inst_templ->SetInternalFieldCount(1); return v8::Persistent::New(class_templ); } /** * Creates a class template for a class with specified initialization function. */ v8::Persistent SWIGV8_CreateClassTemplate(const char* symbol, v8::InvocationCallback _func) { v8::Local class_templ = v8::FunctionTemplate::New(_func); class_templ->SetClassName(v8::String::NewSymbol(symbol)); v8::Handle inst_templ = class_templ->InstanceTemplate(); inst_templ->SetInternalFieldCount(1); return v8::Persistent::New(class_templ); } /** * Sets the pimpl data of a V8 class. */ v8::Handle V8GeneratorUtils::SetInstance(const v8::Arguments& args, void* data) { v8::HandleScope scope; v8::Handle 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 class_templ, const char* symbol, v8::InvocationCallback _func) { v8::Handle 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 class_templ, const char* varname, v8::AccessorGetter getter, v8::AccessorSetter setter) { v8::Handle 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 property, v8::Local value, const v8::AccessorInfo& info) { v8::HandleScope scope; double arg1 ; arg1 = value->NumberValue(); Foo = arg1; } v8::Handle Foo_get(v8::Local property, const v8::AccessorInfo& info) { v8::HandleScope scope; v8::Handle ret; double result; result = Foo; ret = v8::Number::New(result); return scope.Close(ret); } int GlobalVar_Initialize(v8::Handle context) { v8::Local 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 wrap_foo(const v8::Arguments &args) { v8::HandleScope scope; v8::Handle 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 context) { v8::Local 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 property, v8::Local value, const v8::AccessorInfo& info) { v8::HandleScope scope; double arg1 ; arg1 = value->NumberValue(); foo::bar = arg1; } v8::Handle foo_bar_get(v8::Local property, const v8::AccessorInfo& info) { v8::HandleScope scope; v8::Handle ret; double result; result = foo::bar; ret = v8::Number::New(result); return scope.Close(ret); } int Namespace_Initialize(v8::Handle context) { v8::Local global = context->Global(); v8::Handle 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 ~~~~