blob: 71950d98ff2a99d87f0c790ee7a0556dd7fce2d9 [file] [log] [blame]
/* -----------------------------------------------------------------------------
* This file is part of SWIG, which is licensed as a whole under version 3
* (or any later version) of the GNU General Public License. Some additional
* terms also apply to certain portions of SWIG. The full details of the SWIG
* license and copyrights can be found in the LICENSE and COPYRIGHT files
* included with the SWIG source code as distributed by the SWIG developers
* and at https://www.swig.org/legal.html.
*
* javascript.cxx
*
* Javascript language module for SWIG.
* ----------------------------------------------------------------------------- */
#include "swigmod.h"
#include "cparse.h"
/**
* Enables extra debugging information in typemaps.
*/
static bool js_template_enable_debug = false;
#define ERR_MSG_ONLY_ONE_ENGINE_PLEASE "Only one engine can be specified at a time."
// keywords used for state variables
#define NAME "name"
#define NAME_MANGLED "name_mangled"
#define TYPE "type"
#define TYPE_MANGLED "type_mangled"
#define WRAPPER_NAME "wrapper"
#define IS_IMMUTABLE "is_immutable"
#define IS_STATIC "is_static"
#define IS_ABSTRACT "is_abstract"
#define GETTER "getter"
#define SETTER "setter"
#define PARENT "parent"
#define PARENT_MANGLED "parent_mangled"
#define CTOR "ctor"
#define CTOR_WRAPPERS "ctor_wrappers"
#define CTOR_DISPATCHERS "ctor_dispatchers"
#define DTOR "dtor"
#define ARGCOUNT "wrap:argc"
#define HAS_TEMPLATES "has_templates"
#define FORCE_CPP "force_cpp"
#define RESET true
// keys for global state variables
#define CREATE_NAMESPACES "create_namespaces"
#define REGISTER_NAMESPACES "register_namespaces"
#define INITIALIZER "initializer"
// keys for class scoped state variables
#define MEMBER_VARIABLES "member_variables"
#define MEMBER_FUNCTIONS "member_functions"
#define STATIC_FUNCTIONS "static_functions"
#define STATIC_VARIABLES "static_variables"
/**
* A convenience class to manage state variables for emitters.
* The implementation delegates to SWIG Hash DOHs and provides
* named sub-hashes for class, variable, and function states.
*/
class JSEmitterState {
public:
JSEmitterState();
~JSEmitterState();
DOH *globals();
DOH *globals(const char *key, DOH *initial = 0);
DOH *clazz(bool reset = false);
DOH *clazz(const char *key, DOH *initial = 0);
DOH *function(bool reset = false);
DOH *function(const char *key, DOH *initial = 0);
DOH *variable(bool reset = false);
DOH *variable(const char *key, DOH *initial = 0);
static int IsSet(DOH *val);
private:
DOH *getState(const char *key, bool reset = false);
Hash *globalHash;
};
/**
* A convenience class that wraps a code snippet used as template
* for code generation.
*/
class Template {
public:
Template(const String *code);
Template(const String *code, const String *templateName);
Template(const Template & other);
~Template();
String *str();
Template & replace(const String *pattern, const String *repl);
Template & print(DOH *doh);
Template & pretty_print(DOH *doh);
void operator=(const Template & t);
Template & trim();
private:
String *code;
String *templateName;
};
/**
* JSEmitter represents an abstraction of javascript code generators
* for different javascript engines.
**/
class JSEmitter {
protected:
typedef JSEmitterState State;
enum MarshallingMode {
Setter,
Getter,
Ctor,
Function
};
public:
enum JSEngine {
JavascriptCore,
V8,
NodeJS
};
JSEmitter(JSEngine engine);
virtual ~ JSEmitter();
/**
* 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;
/**
* Cleans up all open output DOHs.
*/
virtual int close() = 0;
/**
* Switches the context for code generation.
*
* Classes, global variables and global functions may need to
* be registered in certain static tables.
* This method should be used to switch output DOHs correspondingly.
*/
virtual int switchNamespace(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 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 beginning of the functionHandler.
*/
virtual int enterFunction(Node *);
/**
* 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 nativeWrapper callback
*/
virtual int emitNativeFunction(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);
/**
* Retrieve the code template registered for a given name.
*/
Template getTemplate(const String *name);
State & getState();
protected:
/**
* Generates code for a constructor function.
*/
virtual int emitCtor(Node *n);
/**
* Generates code for a destructor function.
*/
virtual int emitDtor(Node *n);
/**
* Generates code for a function.
*/
virtual int emitFunction(Node *n, bool is_member, bool is_static);
virtual int emitFunctionDispatcher(Node *n, bool /*is_member */ );
/**
* Generates code for a getter function.
*/
virtual int emitGetter(Node *n, bool is_member, bool is_static);
/**
* Generates code for a setter function.
*/
virtual int emitSetter(Node *n, bool is_member, bool is_static);
virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) = 0;
virtual String *emitInputTypemap(Node *n, Parm *params, Wrapper *wrapper, String *arg);
virtual String *emitCheckTypemap(Node *n, Parm *params, Wrapper *wrapper, String *arg);
virtual void marshalOutput(Node *n, ParmList *params, Wrapper *wrapper, String *actioncode, const String *cresult = 0, bool emitReturnVariable = true);
virtual void emitCleanupCode(Node *n, Wrapper *wrapper, ParmList *params);
/**
* Helper function to retrieve the first parent class node.
*/
Node *getBaseClass(Node *n);
virtual int createNamespace(String *scope);
virtual Hash *createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled);
virtual int emitNamespaces() = 0;
protected:
JSEngine engine;
Hash *templates;
State state;
// contains context specific data (DOHs)
// to allow generation of namespace related code
// which are switched on namespace change
Hash *namespaces;
Hash *current_namespace;
String *defaultResultName;
File *f_wrappers;
};
/* factory methods for concrete JSEmitters: */
JSEmitter *swig_javascript_create_JSCEmitter();
JSEmitter *swig_javascript_create_V8Emitter();
JSEmitter *swig_javascript_create_NodeJSEmitter();
/**********************************************************************
* JAVASCRIPT: SWIG module implementation
**********************************************************************/
class JAVASCRIPT:public Language {
public:
JAVASCRIPT():emitter(NULL) {
}
~JAVASCRIPT() {
delete emitter;
}
virtual int functionHandler(Node *n);
virtual int globalfunctionHandler(Node *n);
virtual int variableHandler(Node *n);
virtual int globalvariableHandler(Node *n);
virtual int staticmemberfunctionHandler(Node *n);
virtual int classHandler(Node *n);
virtual int functionWrapper(Node *n);
virtual int constantWrapper(Node *n);
virtual int nativeWrapper(Node *n);
virtual void main(int argc, char *argv[]);
virtual int top(Node *n);
/**
* Registers all %fragments assigned to section "templates".
**/
virtual int fragmentDirective(Node *n);
public:
virtual String *getNSpace() const;
private:
JSEmitter *emitter;
};
/* ---------------------------------------------------------------------
* functionWrapper()
*
* Low level code generator for functions
* --------------------------------------------------------------------- */
int JAVASCRIPT::functionWrapper(Node *n) {
// note: the default implementation only prints a message
// Language::functionWrapper(n);
emitter->emitWrapperFunction(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* functionHandler()
*
* Function handler for generating wrappers for functions
* --------------------------------------------------------------------- */
int JAVASCRIPT::functionHandler(Node *n) {
if (GetFlag(n, "isextension") == 1) {
SetFlag(n, "ismember");
}
emitter->enterFunction(n);
Language::functionHandler(n);
emitter->exitFunction(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* globalfunctionHandler()
*
* Function handler for generating wrappers for functions
* --------------------------------------------------------------------- */
int JAVASCRIPT::globalfunctionHandler(Node *n) {
emitter->switchNamespace(n);
Language::globalfunctionHandler(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* staticmemberfunctionHandler()
*
* Function handler for generating wrappers for static member functions
* --------------------------------------------------------------------- */
int JAVASCRIPT::staticmemberfunctionHandler(Node *n) {
/*
* Note: storage=static is removed by Language::staticmemberfunctionHandler.
* So, don't rely on that after here. Instead use the state variable which is
* set by JSEmitter::enterFunction().
*/
Language::staticmemberfunctionHandler(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* variableHandler()
*
* Function handler for generating wrappers for variables
* --------------------------------------------------------------------- */
int JAVASCRIPT::variableHandler(Node *n) {
emitter->enterVariable(n);
Language::variableHandler(n);
emitter->exitVariable(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* globalvariableHandler()
*
* Function handler for generating wrappers for global variables
* --------------------------------------------------------------------- */
int JAVASCRIPT::globalvariableHandler(Node *n) {
emitter->switchNamespace(n);
Language::globalvariableHandler(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* constantHandler()
*
* Function handler for generating wrappers for constants
* --------------------------------------------------------------------- */
int JAVASCRIPT::constantWrapper(Node *n) {
emitter->switchNamespace(n);
// Note: callbacks trigger this wrapper handler
// TODO: handle callback declarations
if (Equal(Getattr(n, "kind"), "function")) {
return SWIG_OK;
}
// TODO: the emitter for constants must be implemented in a cleaner way
// currently we treat it like a read-only variable
// however, there is a remaining bug with function pointer constants
// which could be fixed with a cleaner approach
emitter->emitConstant(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* nativeWrapper()
*
* Function wrapper for generating placeholders for native functions
* --------------------------------------------------------------------- */
int JAVASCRIPT::nativeWrapper(Node *n) {
emitter->emitNativeFunction(n);
return SWIG_OK;
}
/* ---------------------------------------------------------------------
* classHandler()
*
* Function handler for generating wrappers for class
* --------------------------------------------------------------------- */
int JAVASCRIPT::classHandler(Node *n) {
emitter->switchNamespace(n);
emitter->enterClass(n);
Language::classHandler(n);
emitter->exitClass(n);
return SWIG_OK;
}
int JAVASCRIPT::fragmentDirective(Node *n) {
// catch all fragment directives that have "templates" as location
// and register them at the emitter.
String *section = Getattr(n, "section");
if (Equal(section, "templates") && !ImportMode) {
emitter->registerTemplate(Getattr(n, "value"), Getattr(n, "code"));
} else {
return Language::fragmentDirective(n);
}
return SWIG_OK;
}
String *JAVASCRIPT::getNSpace() const {
return Language::getNSpace();
}
/* ---------------------------------------------------------------------
* top()
*
* Function handler for processing top node of the parse tree
* Wrapper code generation essentially starts from here
* --------------------------------------------------------------------- */
int JAVASCRIPT::top(Node *n) {
emitter->initialize(n);
Language::top(n);
emitter->dump(n);
emitter->close();
return SWIG_OK;
}
static const char *usage = (char *) "\
Javascript Options (available with -javascript)\n\
-jsc - creates a JavascriptCore extension \n\
-v8 - creates a v8 extension \n\
-node - creates a node.js extension \n\
-debug-codetemplates - generates information about the origin of code templates\n";
/* ---------------------------------------------------------------------
* main()
*
* Entry point for the JAVASCRIPT module
* --------------------------------------------------------------------- */
void JAVASCRIPT::main(int argc, char *argv[]) {
// Set javascript subdirectory in SWIG library
SWIG_library_directory("javascript");
int engine = -1;
for (int i = 1; i < argc; i++) {
if (argv[i]) {
if (strcmp(argv[i], "-v8") == 0) {
if (engine != -1) {
Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
Exit(EXIT_FAILURE);
}
Swig_mark_arg(i);
engine = JSEmitter::V8;
} else if (strcmp(argv[i], "-jsc") == 0) {
if (engine != -1) {
Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
Exit(EXIT_FAILURE);
}
Swig_mark_arg(i);
engine = JSEmitter::JavascriptCore;
} else if (strcmp(argv[i], "-node") == 0) {
if (engine != -1) {
Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
Exit(EXIT_FAILURE);
}
Swig_mark_arg(i);
engine = JSEmitter::NodeJS;
} else if (strcmp(argv[i], "-debug-codetemplates") == 0) {
Swig_mark_arg(i);
js_template_enable_debug = true;
} else if (strcmp(argv[i], "-help") == 0) {
fputs(usage, stdout);
return;
}
}
}
switch (engine) {
case JSEmitter::NodeJS:
case JSEmitter::V8:
{
emitter = swig_javascript_create_V8Emitter();
Preprocessor_define("SWIG_JAVASCRIPT_V8 1", 0);
SWIG_library_directory("javascript/v8");
// V8 API is C++, so output must be C++ compatible even when wrapping C code
if (!cparse_cplusplus) {
Swig_cparse_cplusplusout(1);
}
if (engine == JSEmitter::NodeJS) {
Preprocessor_define("BUILDING_NODE_EXTENSION 1", 0);
}
break;
}
case JSEmitter::JavascriptCore:
{
emitter = swig_javascript_create_JSCEmitter();
Preprocessor_define("SWIG_JAVASCRIPT_JSC 1", 0);
SWIG_library_directory("javascript/jsc");
break;
}
default:
{
Printf(stderr, "SWIG Javascript: Unknown engine. Please specify one of '-jsc', '-v8' or '-node'.\n");
Exit(EXIT_FAILURE);
break;
}
}
// Add a symbol to the parser for conditional compilation
Preprocessor_define("SWIGJAVASCRIPT 1", 0);
// Add typemap definitions
SWIG_typemap_lang("javascript");
// Set configuration file
SWIG_config_file("javascript.swg");
allow_overloading();
}
/* -----------------------------------------------------------------------------
* swig_javascript() - Instantiate module
* ----------------------------------------------------------------------------- */
static Language *new_swig_javascript() {
return new JAVASCRIPT();
}
extern "C" Language *swig_javascript(void) {
return new_swig_javascript();
}
/**********************************************************************
* Emitter implementations
**********************************************************************/
/* -----------------------------------------------------------------------------
* JSEmitter()
* ----------------------------------------------------------------------------- */
JSEmitter::JSEmitter(JSEmitter::JSEngine engine)
: engine(engine), templates(NewHash()), namespaces(NULL), current_namespace(NULL), defaultResultName(NewString("result")), f_wrappers(NULL) {
}
/* -----------------------------------------------------------------------------
* ~JSEmitter()
* ----------------------------------------------------------------------------- */
JSEmitter::~JSEmitter() {
Delete(templates);
}
/* -----------------------------------------------------------------------------
* JSEmitter::RegisterTemplate() : Registers a code template
*
* Note: this is used only by JAVASCRIPT::fragmentDirective().
* ----------------------------------------------------------------------------- */
int JSEmitter::registerTemplate(const String *name, const String *code) {
if (!State::IsSet(state.globals(HAS_TEMPLATES))) {
SetFlag(state.globals(), HAS_TEMPLATES);
}
return Setattr(templates, name, code);
}
/* -----------------------------------------------------------------------------
* JSEmitter::getTemplate() : Provides a registered code template
* ----------------------------------------------------------------------------- */
Template JSEmitter::getTemplate(const String *name) {
String *templ = Getattr(templates, name);
if (!templ) {
Printf(stderr, "Could not find template %s\n.", name);
Exit(EXIT_FAILURE);
}
Template t(templ, name);
return t;
}
JSEmitterState & JSEmitter::getState() {
return state;
}
int JSEmitter::initialize(Node * /*n */ ) {
if (namespaces != NULL) {
Delete(namespaces);
}
namespaces = NewHash();
Hash *global_namespace = createNamespaceEntry("exports", 0, 0);
Setattr(namespaces, "::", global_namespace);
current_namespace = global_namespace;
f_wrappers = NewString("");
return SWIG_OK;
}
/* -----------------------------------------------------------------------------
* JSEmitter::getBaseClass() : the node of the base class or NULL
*
* Note: the first base class is provided. Multiple inheritance is not
* supported.
* ----------------------------------------------------------------------------- */
Node *JSEmitter::getBaseClass(Node *n) {
// retrieve the first base class that is not %ignored
List *baselist = Getattr(n, "bases");
if (baselist) {
Iterator base = First(baselist);
while (base.item && GetFlag(base.item, "feature:ignore")) {
base = Next(base);
}
return base.item;
}
return NULL;
}
/* -----------------------------------------------------------------------------
* JSEmitter::emitWrapperFunction() : dispatches emitter functions.
*
* This allows having small sized, dedicated emitting functions.
* All state dependent branching is done here.
* ----------------------------------------------------------------------------- */
int JSEmitter::emitWrapperFunction(Node *n) {
int ret = SWIG_OK;
String *kind = Getattr(n, "kind");
if (kind) {
if (Equal(kind, "function")
// HACK: sneaky.ctest revealed that typedef'd (global) functions must be
// detected via the 'view' attribute.
|| (Equal(kind, "variable") && Equal(Getattr(n, "view"), "globalfunctionHandler"))
) {
bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
bool is_static = GetFlag(state.function(), IS_STATIC) != 0;
ret = emitFunction(n, is_member, is_static);
} else if (Cmp(kind, "variable") == 0) {
bool is_static = GetFlag(state.variable(), IS_STATIC) != 0;
// HACK: smartpointeraccessed static variables are not treated as statics
if (GetFlag(n, "allocate:smartpointeraccess")) {
is_static = false;
}
bool is_member = GetFlag(n, "ismember") != 0;
bool is_setter = GetFlag(n, "memberset") != 0 || GetFlag(n, "varset") != 0;
bool is_getter = GetFlag(n, "memberget") != 0 || GetFlag(n, "varget") != 0;
if (is_setter) {
ret = emitSetter(n, is_member, is_static);
} else if (is_getter) {
ret = emitGetter(n, is_member, is_static);
}
} else {
Printf(stderr, "Warning: unsupported wrapper function type\n");
ret = SWIG_ERROR;
}
} else {
String *view = Getattr(n, "view");
if (Cmp(view, "constructorHandler") == 0) {
ret = emitCtor(n);
} else if (Cmp(view, "destructorHandler") == 0) {
ret = emitDtor(n);
} else {
Printf(stderr, "Warning: unsupported wrapper function type");
ret = SWIG_ERROR;
}
}
return ret;
}
int JSEmitter::emitNativeFunction(Node *n) {
String *wrapname = Getattr(n, "wrap:name");
enterFunction(n);
state.function(WRAPPER_NAME, wrapname);
exitFunction(n);
return SWIG_OK;
}
int JSEmitter::enterClass(Node *n) {
state.clazz(RESET);
state.clazz(NAME, Getattr(n, "sym:name"));
state.clazz("nspace", current_namespace);
// Creating a mangled name using the current namespace and the symbol name
String *mangled_name = NewString("");
Printf(mangled_name, "%s_%s", Getattr(current_namespace, NAME_MANGLED), Getattr(n, "sym:name"));
state.clazz(NAME_MANGLED, SwigType_manglestr(mangled_name));
Delete(mangled_name);
state.clazz(TYPE, NewString(Getattr(n, "classtype")));
String *type = SwigType_manglestr(Getattr(n, "classtypeobj"));
String *classtype_mangled = NewString("");
Printf(classtype_mangled, "p%s", type);
state.clazz(TYPE_MANGLED, classtype_mangled);
Delete(type);
String *ctor_wrapper = NewString("_wrap_new_veto_");
Append(ctor_wrapper, state.clazz(NAME));
state.clazz(CTOR, ctor_wrapper);
state.clazz(CTOR_DISPATCHERS, NewString(""));
state.clazz(DTOR, NewString("0"));
// HACK: assume that a class is abstract
// this is resolved by emitCtor (which is only called for non abstract classes)
SetFlag(state.clazz(), IS_ABSTRACT);
return SWIG_OK;
}
int JSEmitter::enterFunction(Node *n) {
state.function(RESET);
state.function(NAME, Getattr(n, "sym:name"));
if (Equal(Getattr(n, "storage"), "static")) {
SetFlag(state.function(), IS_STATIC);
}
return SWIG_OK;
}
int JSEmitter::enterVariable(Node *n) {
// reset the state information for variables.
state.variable(RESET);
// Retrieve a pure symbol name. Using 'sym:name' as a basis, as it considers %renamings.
if (Equal(Getattr(n, "view"), "memberconstantHandler")) {
// Note: this is kind of hacky/experimental
// For constants/enums 'sym:name' contains e.g., 'Foo_Hello' instead of 'Hello'
state.variable(NAME, Getattr(n, "memberconstantHandler:sym:name"));
} else {
state.variable(NAME, Swig_scopename_last(Getattr(n, "sym:name")));
}
if (Equal(Getattr(n, "storage"), "static")) {
SetFlag(state.variable(), IS_STATIC);
}
if (!Language::instance()->is_assignable(n)) {
SetFlag(state.variable(), IS_IMMUTABLE);
}
// FIXME: test "arrays_global" does not compile with that as it is not allowed to assign to char[]
if (Equal(Getattr(n, "type"), "a().char")) {
SetFlag(state.variable(), IS_IMMUTABLE);
}
return SWIG_OK;
}
int JSEmitter::emitCtor(Node *n) {
Wrapper *wrapper = NewWrapper();
bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
Template t_ctor(getTemplate("js_ctor"));
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
if (is_overloaded) {
t_ctor = getTemplate("js_overloaded_ctor");
Append(wrap_name, Getattr(n, "sym:overname"));
}
Setattr(n, "wrap:name", wrap_name);
// note: we can remove the is_abstract flag now, as this
// is called for non-abstract classes only.
Setattr(state.clazz(), IS_ABSTRACT, 0);
ParmList *params = Getattr(n, "parms");
emit_parameter_variables(params, wrapper);
emit_attach_parmmaps(params, wrapper);
// HACK: in test-case `ignore_parameter` emit_attach_parmmaps generated an extra line of applied typemaps.
// Deleting wrapper->code here, to reset, and as it seemed to have no side effect elsewhere
Delete(wrapper->code);
wrapper->code = NewString("");
Printf(wrapper->locals, "%sresult;", SwigType_str(Getattr(n, "type"), 0));
marshalInputArgs(n, params, wrapper, Ctor, true, false);
String *action = emit_action(n);
Printv(wrapper->code, action, "\n", 0);
emitCleanupCode(n, wrapper, params);
t_ctor.replace("$jswrapper", wrap_name)
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code)
.replace("$jsargcount", Getattr(n, ARGCOUNT))
.pretty_print(f_wrappers);
Template t_ctor_case(getTemplate("js_ctor_dispatch_case"));
t_ctor_case.replace("$jswrapper", wrap_name)
.replace("$jsargcount", Getattr(n, ARGCOUNT));
Append(state.clazz(CTOR_DISPATCHERS), t_ctor_case.str());
DelWrapper(wrapper);
// create a dispatching ctor
if (is_overloaded) {
if (!Getattr(n, "sym:nextSibling")) {
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
Template t_mainctor(getTemplate("js_ctor_dispatcher"));
t_mainctor.replace("$jswrapper", wrap_name)
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsdispatchcases", state.clazz(CTOR_DISPATCHERS))
.pretty_print(f_wrappers);
state.clazz(CTOR, wrap_name);
}
} else {
state.clazz(CTOR, wrap_name);
}
return SWIG_OK;
}
int JSEmitter::emitDtor(Node *n) {
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
SwigType *type = state.clazz(TYPE);
String *p_classtype = SwigType_add_pointer(state.clazz(TYPE));
String *ctype = SwigType_lstr(p_classtype, "");
String *jsfree = NewString("");
// (Taken from JSCore implementation.)
/* The if (Extend) block was taken from the Ruby implementation.
* The problem is that in the case of an %extend to create a destructor for a struct to coordinate automatic memory cleanup with the Javascript collector,
* the SWIG function was not being generated. More specifically:
struct MyData {
%extend {
~MyData() {
FreeData($self);
}
}
};
%newobject CreateData;
struct MyData* CreateData(void);
%delobject FreeData;
void FreeData(struct MyData* the_data);
where the use case is something like:
var my_data = example.CreateData();
my_data = null;
This function was not being generated:
SWIGINTERN void delete_MyData(struct MyData *self){
FreeData(self);
}
I don't understand fully why it wasn't being generated. It just seems to happen in the Lua generator.
There is a comment about staticmemberfunctionHandler having an inconsistency and I tracked down dome of the SWIGINTERN void delete_*
code to that function in the Language base class.
The Ruby implementation seems to have an explicit check for if(Extend) and explicitly generates the code, so that's what I'm doing here.
The Ruby implementation does other stuff which I omit.
*/
if (Extend) {
String *wrap = Getattr(n, "wrap:code");
if (wrap) {
Printv(f_wrappers, wrap, NIL);
}
}
// HACK: this is only for the v8 emitter. maybe set an attribute wrap:action of node
// TODO: generate dtors more similar to other wrappers
// EW: I think this is wrong. delete should only be used when new was used to create. If malloc was used, free needs to be used.
if (SwigType_isarray(type)) {
Printf(jsfree, "delete [] (%s)", ctype);
} else {
Printf(jsfree, "delete (%s)", ctype);
}
String *destructor_action = Getattr(n, "wrap:action");
// Adapted from the JSCore implementation.
/* The next challenge is to generate the correct finalize function for JavaScriptCore to call.
Originally, it would use this fragment from javascriptcode.swg
%fragment ("JS_destructordefn", "templates")
%{
void _wrap_${classname_mangled}_finalize(JSObjectRef thisObject)
{
SWIG_PRV_DATA* t = (SWIG_PRV_DATA*)JSObjectGetPrivate(thisObject);
if(t && t->swigCMemOwn) free ((${type}*)t->swigCObject);
free(t);
}
%}
But for the above example case of %extend to define a destructor on a struct, we need to override the system to not call
free ((${type}*)t->swigCObject);
and substitute it with what the user has provided.
To solve this, I created a variation fragment called JS_destructoroverridedefn:
SWIG_PRV_DATA* t = (SWIG_PRV_DATA*)JSObjectGetPrivate(thisObject);
if(t && t->swigCMemOwn) {
${type}* arg1 = (${type}*)t->swigCObject;
${destructor_action}
}
free(t);
Based on what I saw in the Lua and Ruby modules, I use Getattr(n, "wrap:action")
to decide if the user has a preferred destructor action.
Based on that, I decide which fragment to use.
And in the case of the custom action, I substitute that action in.
I noticed that destructor_action has the form
delete_MyData(arg1);
The explicit arg1 is a little funny, so I structured the fragment to create a temporary variable called arg1 to make the generation easier.
This might suggest this solution misunderstands a more complex case.
Also, there is a problem where destructor_action is always true for me, even when not requesting %extend as above.
So this code doesn't actually quite work as I expect. The end result is that the code still works because
destructor_action calls free like the original template. The one caveat is the string in destructor_action casts to char* which is weird.
I think there is a deeper underlying SWIG issue because I don't think it should be char*. However, it doesn't really matter for free.
Maybe the fix for the destructor_action always true problem is that this is supposed to be embedded in the if(Extend) block above.
But I don't fully understand the conditions of any of these things, and since it works for the moment, I don't want to break more stuff.
*/
if (destructor_action) {
Template t_dtor = getTemplate("js_dtoroverride");
state.clazz(DTOR, wrap_name);
t_dtor.replace("${classname_mangled}", state.clazz(NAME_MANGLED))
.replace("$jswrapper", wrap_name)
.replace("$jsfree", jsfree)
.replace("$jstype", ctype);
t_dtor.replace("${destructor_action}", destructor_action);
Wrapper_pretty_print(t_dtor.str(), f_wrappers);
} else {
Template t_dtor = getTemplate("js_dtor");
state.clazz(DTOR, wrap_name);
t_dtor.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jswrapper", wrap_name)
.replace("$jsfree", jsfree)
.replace("$jstype", ctype)
.pretty_print(f_wrappers);
}
Delete(p_classtype);
Delete(ctype);
Delete(jsfree);
return SWIG_OK;
}
int JSEmitter::emitGetter(Node *n, bool is_member, bool is_static) {
Wrapper *wrapper = NewWrapper();
Template t_getter(getTemplate("js_getter"));
// prepare wrapper name
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
Setattr(n, "wrap:name", wrap_name);
state.variable(GETTER, wrap_name);
// prepare local variables
ParmList *params = Getattr(n, "parms");
emit_parameter_variables(params, wrapper);
emit_attach_parmmaps(params, wrapper);
// prepare code part
String *action = emit_action(n);
marshalInputArgs(n, params, wrapper, Getter, is_member, is_static);
marshalOutput(n, params, wrapper, action);
emitCleanupCode(n, wrapper, params);
t_getter.replace("$jswrapper", wrap_name)
.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code)
.pretty_print(f_wrappers);
DelWrapper(wrapper);
return SWIG_OK;
}
int JSEmitter::emitSetter(Node *n, bool is_member, bool is_static) {
// skip variables that are immutable
if (State::IsSet(state.variable(IS_IMMUTABLE))) {
return SWIG_OK;
}
Wrapper *wrapper = NewWrapper();
Template t_setter(getTemplate("js_setter"));
// prepare wrapper name
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
Setattr(n, "wrap:name", wrap_name);
state.variable(SETTER, wrap_name);
// prepare local variables
ParmList *params = Getattr(n, "parms");
emit_parameter_variables(params, wrapper);
emit_attach_parmmaps(params, wrapper);
// prepare code part
String *action = emit_action(n);
marshalInputArgs(n, params, wrapper, Setter, is_member, is_static);
Append(wrapper->code, action);
emitCleanupCode(n, wrapper, params);
t_setter.replace("$jswrapper", wrap_name)
.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code)
.pretty_print(f_wrappers);
DelWrapper(wrapper);
return SWIG_OK;
}
/* -----------------------------------------------------------------------------
* JSEmitter::emitConstant() : triggers code generation for constants
* ----------------------------------------------------------------------------- */
int JSEmitter::emitConstant(Node *n) {
// HACK: somehow it happened under Mac OS X that before everything started
// a lot of SWIG internal constants were emitted
// This didn't happen on other platforms yet...
// we ignore those premature definitions
if (!State::IsSet(state.globals(HAS_TEMPLATES))) {
return SWIG_ERROR;
}
Wrapper *wrapper = NewWrapper();
SwigType *type = Getattr(n, "type");
String *iname = Getattr(n, "sym:name");
String *wname = Swig_name_get(Getattr(current_namespace, NAME_MANGLED), iname);
String *rawval = Getattr(n, "rawval");
String *value = rawval ? rawval : Getattr(n, "value");
// HACK: forcing usage of cppvalue for v8 (which turned out to fix typedef_struct.i, et. al)
if (State::IsSet(state.globals(FORCE_CPP)) && Getattr(n, "cppvalue") != NULL) {
value = Getattr(n, "cppvalue");
}
Template t_getter(getTemplate("js_getter"));
// call the variable methods as a constants are
// registered in same way
enterVariable(n);
state.variable(GETTER, wname);
// TODO: why do we need this?
Setattr(n, "wrap:name", wname);
// special treatment of member pointers
if (SwigType_type(type) == T_MPOINTER) {
// TODO: this could go into a code-template
String *mpointer_wname = NewString("");
Printf(mpointer_wname, "_wrapConstant_%s", iname);
Setattr(n, "memberpointer:constant:wrap:name", mpointer_wname);
String *str = SwigType_str(type, mpointer_wname);
Printf(f_wrappers, "static %s = %s;\n", str, value);
Delete(str);
value = mpointer_wname;
}
marshalOutput(n, 0, wrapper, NewString(""), value, false);
t_getter.replace("$jswrapper", wname)
.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code)
.pretty_print(f_wrappers);
exitVariable(n);
DelWrapper(wrapper);
return SWIG_OK;
}
int JSEmitter::emitFunction(Node *n, bool is_member, bool is_static) {
Wrapper *wrapper = NewWrapper();
Template t_function(getTemplate("js_function"));
bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
// prepare the function wrapper name
String *iname = Getattr(n, "sym:name");
String *wrap_name = Swig_name_wrapper(iname);
if (is_overloaded) {
t_function = getTemplate("js_overloaded_function");
Append(wrap_name, Getattr(n, "sym:overname"));
}
Setattr(n, "wrap:name", wrap_name);
state.function(WRAPPER_NAME, wrap_name);
// prepare local variables
ParmList *params = Getattr(n, "parms");
emit_parameter_variables(params, wrapper);
emit_attach_parmmaps(params, wrapper);
// HACK: in test-case `ignore_parameter` emit_attach_parmmaps generates an extra line of applied typemap.
// Deleting wrapper->code here fixes the problem, and seems to have no side effect elsewhere
Delete(wrapper->code);
wrapper->code = NewString("");
marshalInputArgs(n, params, wrapper, Function, is_member, is_static);
String *action = emit_action(n);
marshalOutput(n, params, wrapper, action);
emitCleanupCode(n, wrapper, params);
Replaceall(wrapper->code, "$symname", iname);
t_function.replace("$jswrapper", wrap_name)
.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code)
.replace("$jsargcount", Getattr(n, ARGCOUNT))
.pretty_print(f_wrappers);
DelWrapper(wrapper);
return SWIG_OK;
}
int JSEmitter::emitFunctionDispatcher(Node *n, bool /*is_member */ ) {
Wrapper *wrapper = NewWrapper();
// Generate call list, go to first node
Node *sibl = n;
while (Getattr(sibl, "sym:previousSibling"))
sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
do {
String *siblname = Getattr(sibl, "wrap:name");
if (siblname) {
// handle function overloading
Template t_dispatch_case = getTemplate("js_function_dispatch_case");
t_dispatch_case.replace("$jswrapper", siblname)
.replace("$jsargcount", Getattr(sibl, ARGCOUNT));
Append(wrapper->code, t_dispatch_case.str());
}
} while ((sibl = Getattr(sibl, "sym:nextSibling")));
Template t_function(getTemplate("js_function_dispatcher"));
// Note: this dispatcher function gets called after the last overloaded function has been created.
// At this time, n.wrap:name contains the name of the last wrapper function.
// To get a valid function name for the dispatcher function we take the last wrapper name and
// subtract the extension "sym:overname",
String *wrap_name = NewString(Getattr(n, "wrap:name"));
String *overname = Getattr(n, "sym:overname");
Node *methodclass = Swig_methodclass(n);
String *class_name = Getattr(methodclass, "sym:name");
int l1 = Len(wrap_name);
int l2 = Len(overname);
Delslice(wrap_name, l1 - l2, l1);
String *new_string = NewStringf("%s_%s", class_name, wrap_name);
String *final_wrap_name = Swig_name_wrapper(new_string);
Setattr(n, "wrap:name", final_wrap_name);
state.function(WRAPPER_NAME, final_wrap_name);
t_function.replace("$jslocals", wrapper->locals)
.replace("$jscode", wrapper->code);
// call this here, to replace all variables
t_function.replace("$jswrapper", final_wrap_name)
.replace("$jsname", state.function(NAME))
.pretty_print(f_wrappers);
// Delete the state variable
DelWrapper(wrapper);
return SWIG_OK;
}
String *JSEmitter::emitInputTypemap(Node *n, Parm *p, Wrapper *wrapper, String *arg) {
// Get input typemap for current param
String *tm = Getattr(p, "tmap:in");
SwigType *type = Getattr(p, "type");
if (tm != NULL) {
Replaceall(tm, "$input", arg);
Setattr(p, "emit:input", arg);
// do replacements for built-in variables
if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
} else {
Replaceall(tm, "$disown", "0");
}
Replaceall(tm, "$symname", Getattr(n, "sym:name"));
Printf(wrapper->code, "%s\n", tm);
} else {
Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(type, 0));
}
return tm;
}
String *JSEmitter::emitCheckTypemap(Node *, Parm *p, Wrapper *wrapper, String *arg) {
String *tm = Getattr(p, "tmap:check");
if (tm != nullptr) {
Replaceall(tm, "$input", arg);
Printf(wrapper->code, "%s\n", tm);
}
return tm;
}
void JSEmitter::marshalOutput(Node *n, ParmList *params, Wrapper *wrapper, String *actioncode, const String *cresult, bool emitReturnVariable) {
SwigType *type = Getattr(n, "type");
String *tm;
Parm *p;
// adds a declaration for the result variable
if (emitReturnVariable)
emit_return_variable(n, type, wrapper);
// if not given, use default result identifier ('result') for output typemap
if (cresult == 0)
cresult = defaultResultName;
tm = Swig_typemap_lookup_out("out", n, cresult, wrapper, actioncode);
bool should_own = GetFlag(n, "feature:new") != 0;
if (tm) {
Replaceall(tm, "$objecttype", Swig_scopename_last(SwigType_str(SwigType_strip_qualifiers(type), 0)));
if (should_own) {
Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
} else {
Replaceall(tm, "$owner", "0");
}
Append(wrapper->code, tm);
if (Len(tm) > 0) {
Printf(wrapper->code, "\n");
}
} else {
Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(type, 0), Getattr(n, "name"));
}
if (params) {
for (p = params; p;) {
if ((tm = Getattr(p, "tmap:argout"))) {
Replaceall(tm, "$input", Getattr(p, "emit:input"));
Printv(wrapper->code, tm, "\n", NIL);
p = Getattr(p, "tmap:argout:next");
} else {
p = nextSibling(p);
}
}
}
Replaceall(wrapper->code, "$result", "jsresult");
}
void JSEmitter::emitCleanupCode(Node *n, Wrapper *wrapper, ParmList *params) {
Parm *p;
String *tm;
for (p = params; p;) {
if ((tm = Getattr(p, "tmap:freearg"))) {
//addThrows(n, "tmap:freearg", p);
Replaceall(tm, "$input", Getattr(p, "emit:input"));
Printv(wrapper->code, tm, "\n", NIL);
p = Getattr(p, "tmap:freearg:next");
} else {
p = nextSibling(p);
}
}
if (GetFlag(n, "feature:new")) {
tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0);
if (tm != NIL) {
//addThrows(throws_hash, "newfree", n);
Printv(wrapper->code, tm, "\n", NIL);
}
}
/* See if there is any return cleanup code */
if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
Printf(wrapper->code, "%s\n", tm);
Delete(tm);
}
}
int JSEmitter::switchNamespace(Node *n) {
// HACK: somehow this gets called for member functions.
// We can safely ignore them, as members are not associated to a namespace (only their class)
if (GetFlag(n, "ismember")) {
return SWIG_OK;
}
// if nspace is deactivated, everything goes into the global scope
if (!GetFlag(n, "feature:nspace")) {
current_namespace = Getattr(namespaces, "::");
return SWIG_OK;
}
// EXPERIMENTAL: we want to use Language::getNSpace() here
// However, it is not working yet.
// For namespace functions Language::getNSpace() does not give a valid result
#if 0
JAVASCRIPT *lang = static_cast<JAVASCRIPT*>(Language::instance());
String *_nspace = lang->getNSpace();
if (!Equal(nspace, _nspace)) {
Printf(stdout, "##### Custom vs Language::getNSpace(): %s | %s\n", nspace, _nspace);
}
#endif
String *nspace = Getattr(n, "sym:nspace");
if (nspace == NULL) {
// It seems that only classes have 'sym:nspace' set.
// We try to get the namespace from the qualified name (i.e., everything before the last '::')
nspace = Swig_scopename_prefix(Getattr(n, "name"));
}
// If there is not even a scopename prefix then it must be global scope
if (nspace == NULL) {
current_namespace = Getattr(namespaces, "::");
return SWIG_OK;
}
String *scope = NewString(nspace);
// replace "." with "::" that we can use Swig_scopename_last
Replaceall(scope, ".", "::");
// if the scope is not yet registered
// create (parent) namespaces recursively
if (!Getattr(namespaces, scope)) {
createNamespace(scope);
}
current_namespace = Getattr(namespaces, scope);
return SWIG_OK;
}
int JSEmitter::createNamespace(String *scope) {
String *parent_scope = Swig_scopename_prefix(scope);
Hash *parent_namespace;
if (parent_scope == 0) {
parent_namespace = Getattr(namespaces, "::");
} else if (!Getattr(namespaces, parent_scope)) {
createNamespace(parent_scope);
parent_namespace = Getattr(namespaces, parent_scope);
} else {
parent_namespace = Getattr(namespaces, parent_scope);
}
assert(parent_namespace != 0);
Hash *new_namespace = createNamespaceEntry(Char(scope), Char(Getattr(parent_namespace, "name")), Char(Getattr(parent_namespace, "name_mangled")));
Setattr(namespaces, scope, new_namespace);
Delete(parent_scope);
return SWIG_OK;
}
Hash *JSEmitter::createNamespaceEntry(const char *_name, const char *parent, const char *parent_mangled) {
Hash *entry = NewHash();
String *name = NewString(_name);
Setattr(entry, NAME, Swig_scopename_last(name));
Setattr(entry, NAME_MANGLED, Swig_name_mangle_string(name));
Setattr(entry, PARENT, NewString(parent));
Setattr(entry, PARENT_MANGLED, NewString(parent_mangled));
Delete(name);
return entry;
}
/**********************************************************************
* JavascriptCore: JSEmitter implementation for JavascriptCore engine
**********************************************************************/
class JSCEmitter:public JSEmitter {
public:
JSCEmitter();
virtual ~ JSCEmitter();
virtual int initialize(Node *n);
virtual int dump(Node *n);
virtual int close();
protected:
virtual int enterVariable(Node *n);
virtual int exitVariable(Node *n);
virtual int enterFunction(Node *n);
virtual int exitFunction(Node *n);
virtual int enterClass(Node *n);
virtual int exitClass(Node *n);
virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static);
virtual Hash *createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled);
virtual int emitNamespaces();
private:
String *NULL_STR;
String *VETO_SET;
// output file and major code parts
File *f_wrap_cpp;
File *f_runtime;
File *f_header;
File *f_init;
};
JSCEmitter::JSCEmitter()
: JSEmitter(JSEmitter::JavascriptCore), NULL_STR(NewString("NULL")), VETO_SET(NewString("JS_veto_set_variable")), f_wrap_cpp(NULL), f_runtime(NULL), f_header(NULL), f_init(NULL) {
}
JSCEmitter::~JSCEmitter() {
Delete(NULL_STR);
Delete(VETO_SET);
}
/* ---------------------------------------------------------------------
* marshalInputArgs()
*
* Process all of the arguments passed into the argv array
* and convert them into C/C++ function arguments using the
* supplied typemaps.
* --------------------------------------------------------------------- */
void JSCEmitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) {
Parm *p;
String *tm;
// determine an offset index, as members have an extra 'this' argument
// except: static members and ctors.
int startIdx = 0;
if (is_member && !is_static && mode != Ctor) {
startIdx = 1;
}
// store number of arguments for argument checks
int num_args = emit_num_arguments(parms) - startIdx;
String *argcount = NewString("");
Printf(argcount, "%d", num_args);
Setattr(n, ARGCOUNT, argcount);
// process arguments
int i = 0;
for (p = parms; p;) {
String *arg = NewString("");
String *type = Getattr(p, "type");
// ignore varargs
if (SwigType_isvarargs(type))
break;
switch (mode) {
case Getter:
case Function:
if (is_member && !is_static && i == 0) {
Printv(arg, "thisObject", 0);
i++;
} else {
Printf(arg, "argv[%d]", i - startIdx);
i += GetInt(p, "tmap:in:numinputs");
}
break;
case Setter:
if (is_member && !is_static && i == 0) {
Printv(arg, "thisObject", 0);
i++;
} else {
Printv(arg, "value", 0);
i++;
}
break;
case Ctor:
Printf(arg, "argv[%d]", i);
i += GetInt(p, "tmap:in:numinputs");
break;
default:
Printf(stderr, "Illegal MarshallingMode.");
Exit(EXIT_FAILURE);
}
tm = emitInputTypemap(n, p, wrapper, arg);
Delete(arg);
if (tm) {
p = Getattr(p, "tmap:in:next");
} else {
p = nextSibling(p);
}
}
for (p = parms; p;) {
tm = emitCheckTypemap(n, p, wrapper, Getattr(p, "emit:input"));
if (tm) {
p = Getattr(p, "tmap:check:next");
} else {
p = nextSibling(p);
}
}
}
int JSCEmitter::initialize(Node *n) {
JSEmitter::initialize(n);
/* Get the output file name */
String *outfile = Getattr(n, "outfile");
/* Initialize I/O */
f_wrap_cpp = NewFile(outfile, "w", SWIG_output_files());
if (!f_wrap_cpp) {
FileErrorDisplay(outfile);
Exit(EXIT_FAILURE);
}
/* Initialization of members */
f_runtime = NewString("");
f_init = NewString("");
f_header = NewString("");
state.globals(CREATE_NAMESPACES, NewString(""));
state.globals(REGISTER_NAMESPACES, NewString(""));
state.globals(INITIALIZER, NewString(""));
/* Register file targets with the SWIG file handler */
Swig_register_filebyname("begin", f_wrap_cpp);
Swig_register_filebyname("header", f_header);
Swig_register_filebyname("wrapper", f_wrappers);
Swig_register_filebyname("runtime", f_runtime);
Swig_register_filebyname("init", f_init);
Swig_banner(f_wrap_cpp);
Swig_obligatory_macros(f_runtime, "JAVASCRIPT");
return SWIG_OK;
}
int JSCEmitter::dump(Node *n) {
/* Get the module name */
String *module = Getattr(n, "name");
Template initializer_define(getTemplate("js_initializer_define"));
initializer_define.replace("$jsname", module).pretty_print(f_header);
SwigType_emit_type_table(f_runtime, f_wrappers);
Printv(f_wrap_cpp, f_runtime, "\n", 0);
Printv(f_wrap_cpp, f_header, "\n", 0);
Printv(f_wrap_cpp, f_wrappers, "\n", 0);
emitNamespaces();
// compose the initializer function using a template
Template initializer(getTemplate("js_initializer"));
initializer.replace("$jsname", module)
.replace("$jsregisterclasses", state.globals(INITIALIZER))
.replace("$jscreatenamespaces", state.globals(CREATE_NAMESPACES))
.replace("$jsregisternamespaces", state.globals(REGISTER_NAMESPACES))
.pretty_print(f_init);
Printv(f_wrap_cpp, f_init, 0);
return SWIG_OK;
}
int JSCEmitter::close() {
Delete(f_runtime);
Delete(f_header);
Delete(f_wrappers);
Delete(f_init);
Delete(namespaces);
Delete(f_wrap_cpp);
return SWIG_OK;
}
int JSCEmitter::enterFunction(Node *n) {
JSEmitter::enterFunction(n);
return SWIG_OK;
}
int JSCEmitter::exitFunction(Node *n) {
Template t_function = getTemplate("jsc_function_declaration");
bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
// handle overloaded functions
if (is_overloaded) {
if (!Getattr(n, "sym:nextSibling")) {
//state.function(WRAPPER_NAME, Swig_name_wrapper(Getattr(n, "name")));
// create dispatcher
emitFunctionDispatcher(n, is_member);
} else {
//don't register wrappers of overloaded functions in function tables
return SWIG_OK;
}
}
t_function.replace("$jsname", state.function(NAME))
.replace("$jswrapper", state.function(WRAPPER_NAME));
if (is_member) {
if (GetFlag(state.function(), IS_STATIC)) {
t_function.pretty_print(state.clazz(STATIC_FUNCTIONS));
} else {
t_function.pretty_print(state.clazz(MEMBER_FUNCTIONS));
}
} else {
t_function.pretty_print(Getattr(current_namespace, "functions"));
}
return SWIG_OK;
}
int JSCEmitter::enterVariable(Node *n) {
JSEmitter::enterVariable(n);
state.variable(GETTER, NULL_STR);
state.variable(SETTER, VETO_SET);
return SWIG_OK;
}
int JSCEmitter::exitVariable(Node *n) {
Template t_variable(getTemplate("jsc_variable_declaration"));
t_variable.replace("$jsname", state.variable(NAME))
.replace("$jsgetter", state.variable(GETTER))
.replace("$jssetter", state.variable(SETTER));
if (GetFlag(n, "ismember")) {
if (GetFlag(state.variable(), IS_STATIC)
|| Equal(Getattr(n, "nodeType"), "enumitem")) {
t_variable.pretty_print(state.clazz(STATIC_VARIABLES));
} else {
t_variable.pretty_print(state.clazz(MEMBER_VARIABLES));
}
} else {
t_variable.pretty_print(Getattr(current_namespace, "values"));
}
return SWIG_OK;
}
int JSCEmitter::enterClass(Node *n) {
JSEmitter::enterClass(n);
state.clazz(MEMBER_VARIABLES, NewString(""));
state.clazz(MEMBER_FUNCTIONS, NewString(""));
state.clazz(STATIC_VARIABLES, NewString(""));
state.clazz(STATIC_FUNCTIONS, NewString(""));
Template t_class_decl = getTemplate("jsc_class_declaration");
t_class_decl.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.pretty_print(f_wrappers);
return SWIG_OK;
}
int JSCEmitter::exitClass(Node *n) {
Template t_class_tables(getTemplate("jsc_class_tables"));
t_class_tables.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsclassvariables", state.clazz(MEMBER_VARIABLES))
.replace("$jsclassfunctions", state.clazz(MEMBER_FUNCTIONS))
.replace("$jsstaticclassfunctions", state.clazz(STATIC_FUNCTIONS))
.replace("$jsstaticclassvariables", state.clazz(STATIC_VARIABLES))
.pretty_print(f_wrappers);
/* adds the ctor wrappers at this position */
// Note: this is necessary to avoid extra forward declarations.
//Append(f_wrappers, state.clazz(CTOR_WRAPPERS));
// for abstract classes add a vetoing ctor
if (GetFlag(state.clazz(), IS_ABSTRACT)) {
Template t_veto_ctor(getTemplate("js_veto_ctor"));
t_veto_ctor.replace("$jswrapper", state.clazz(CTOR))
.replace("$jsname", state.clazz(NAME))
.pretty_print(f_wrappers);
}
/* adds a class template statement to initializer function */
Template t_classtemplate(getTemplate("jsc_class_definition"));
/* prepare registration of base class */
String *jsclass_inheritance = NewString("");
Node *base_class = getBaseClass(n);
if (base_class != NULL) {
Template t_inherit(getTemplate("jsc_class_inherit"));
t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsbaseclassmangled", SwigType_manglestr(Getattr(base_class, "name")))
.pretty_print(jsclass_inheritance);
} else {
Template t_inherit(getTemplate("jsc_class_noinherit"));
t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.pretty_print(jsclass_inheritance);
}
t_classtemplate.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
.replace("$jsclass_inheritance", jsclass_inheritance)
.replace("$jsctor", state.clazz(CTOR))
.replace("$jsdtor", state.clazz(DTOR))
.pretty_print(state.globals(INITIALIZER));
Delete(jsclass_inheritance);
/* Note: this makes sure that there is a swig_type added for this class */
SwigType_remember_clientdata(state.clazz(TYPE_MANGLED), NewString("0"));
/* adds a class registration statement to initializer function */
Template t_registerclass(getTemplate("jsc_class_registration"));
t_registerclass.replace("$jsname", state.clazz(NAME))
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsnspace", Getattr(state.clazz("nspace"), NAME_MANGLED))
.pretty_print(state.globals(INITIALIZER));
return SWIG_OK;
}
Hash *JSCEmitter::createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled) {
Hash *entry = JSEmitter::createNamespaceEntry(name, parent, parent_mangled);
Setattr(entry, "functions", NewString(""));
Setattr(entry, "values", NewString(""));
return entry;
}
int JSCEmitter::emitNamespaces() {
Iterator it;
for (it = First(namespaces); it.item; it = Next(it)) {
Hash *entry = it.item;
String *name = Getattr(entry, NAME);
String *name_mangled = Getattr(entry, NAME_MANGLED);
String *parent_mangled = Getattr(entry, PARENT_MANGLED);
String *functions = Getattr(entry, "functions");
String *variables = Getattr(entry, "values");
// skip the global namespace which is given by the application
Template namespace_definition(getTemplate("jsc_nspace_declaration"));
namespace_definition.replace("$jsglobalvariables", variables)
.replace("$jsglobalfunctions", functions)
.replace("$jsnspace", name_mangled)
.replace("$jsmangledname", name_mangled)
.pretty_print(f_wrap_cpp);
Template t_createNamespace(getTemplate("jsc_nspace_definition"));
t_createNamespace.replace("$jsmangledname", name_mangled);
Append(state.globals(CREATE_NAMESPACES), t_createNamespace.str());
// Don't register 'exports' as namespace. It is return to the application.
if (!Equal("exports", name)) {
Template t_registerNamespace(getTemplate("jsc_nspace_registration"));
t_registerNamespace.replace("$jsmangledname", name_mangled)
.replace("$jsname", name)
.replace("$jsparent", parent_mangled);
Append(state.globals(REGISTER_NAMESPACES), t_registerNamespace.str());
}
}
return SWIG_OK;
}
JSEmitter *swig_javascript_create_JSCEmitter() {
return new JSCEmitter();
}
/**********************************************************************
* V8: JSEmitter implementation for V8 engine
**********************************************************************/
class V8Emitter:public JSEmitter {
public:
V8Emitter();
virtual ~ V8Emitter();
virtual int initialize(Node *n);
virtual int dump(Node *n);
virtual int close();
virtual int enterClass(Node *n);
virtual int exitClass(Node *n);
virtual int enterVariable(Node *n);
virtual int exitVariable(Node *n);
virtual int exitFunction(Node *n);
protected:
virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static);
virtual int emitNamespaces();
protected:
/* built-in parts */
String *f_runtime;
String *f_header;
String *f_init;
String *f_post_init;
/* part for class templates */
String *f_class_templates;
/* parts for initilizer */
String *f_init_namespaces;
String *f_init_class_templates;
String *f_init_wrappers;
String *f_init_inheritance;
String *f_init_class_instances;
String *f_init_static_wrappers;
String *f_init_register_classes;
String *f_init_register_namespaces;
// the output cpp file
File *f_wrap_cpp;
String *NULL_STR;
String *VETO_SET;
String *moduleName;
};
V8Emitter::V8Emitter()
: JSEmitter(JSEmitter::V8), NULL_STR(NewString("0")), VETO_SET(NewString("JS_veto_set_variable")) {
}
V8Emitter::~V8Emitter() {
Delete(NULL_STR);
Delete(VETO_SET);
}
int V8Emitter::initialize(Node *n) {
JSEmitter::initialize(n);
moduleName = Getattr(n, "name");
// Get the output file name
String *outfile = Getattr(n, "outfile");
f_wrap_cpp = NewFile(outfile, "w", SWIG_output_files());
if (!f_wrap_cpp) {
FileErrorDisplay(outfile);
Exit(EXIT_FAILURE);
}
f_runtime = NewString("");
f_header = NewString("");
f_class_templates = NewString("");
f_init = NewString("");
f_post_init = NewString("");
f_init_namespaces = NewString("");
f_init_class_templates = NewString("");
f_init_wrappers = NewString("");
f_init_inheritance = NewString("");
f_init_class_instances = NewString("");
f_init_static_wrappers = NewString("");
f_init_register_classes = NewString("");
f_init_register_namespaces = NewString("");
// note: this is necessary for built-in generation of SWIG runtime code
Swig_register_filebyname("begin", f_wrap_cpp);
Swig_register_filebyname("runtime", f_runtime);
Swig_register_filebyname("header", f_header);
Swig_register_filebyname("wrapper", f_wrappers);
Swig_register_filebyname("init", f_init);
Swig_register_filebyname("post-init", f_post_init);
state.globals(FORCE_CPP, NewString("1"));
Swig_banner(f_wrap_cpp);
Swig_obligatory_macros(f_runtime, "JAVASCRIPT");
return SWIG_OK;
}
int V8Emitter::dump(Node *n) {
/* Get the module name */
String *module = Getattr(n, "name");
Template initializer_define(getTemplate("js_initializer_define"));
initializer_define.replace("$jsname", module).pretty_print(f_header);
SwigType_emit_type_table(f_runtime, f_wrappers);
Printv(f_wrap_cpp, f_runtime, "\n", 0);
Printv(f_wrap_cpp, f_header, "\n", 0);
Printv(f_wrap_cpp, f_class_templates, "\n", 0);
Printv(f_wrap_cpp, f_wrappers, "\n", 0);
emitNamespaces();
// compose the initializer function using a template
// filled with sub-parts
Template initializer(getTemplate("js_initializer"));
initializer.replace("$jsname", moduleName)
.replace("$jsv8nspaces", f_init_namespaces)
.replace("$jsv8classtemplates", f_init_class_templates)
.replace("$jsv8wrappers", f_init_wrappers)
.replace("$jsv8inheritance", f_init_inheritance)
.replace("$jsv8classinstances", f_init_class_instances)
.replace("$jsv8staticwrappers", f_init_static_wrappers)
.replace("$jsv8registerclasses", f_init_register_classes)
.replace("$jsv8registernspaces", f_init_register_namespaces);
Printv(f_init, initializer.str(), 0);
Printv(f_wrap_cpp, f_init, 0);
Printv(f_wrap_cpp, f_post_init, 0);
return SWIG_OK;
}
int V8Emitter::close() {
Delete(f_runtime);
Delete(f_header);
Delete(f_class_templates);
Delete(f_init_namespaces);
Delete(f_init_class_templates);
Delete(f_init_wrappers);
Delete(f_init_inheritance);
Delete(f_init_class_instances);
Delete(f_init_static_wrappers);
Delete(f_init_register_classes);
Delete(f_init_register_namespaces);
Delete(f_init);
Delete(f_post_init);
Delete(f_wrap_cpp);
return SWIG_OK;
}
int V8Emitter::enterClass(Node *n) {
JSEmitter::enterClass(n);
// emit declaration of a v8 class template
Template t_decl_class(getTemplate("jsv8_declare_class_template"));
t_decl_class.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.trim()
.pretty_print(f_class_templates);
return SWIG_OK;
}
int V8Emitter::exitClass(Node *n) {
if (GetFlag(state.clazz(), IS_ABSTRACT)) {
Template t_veto_ctor(getTemplate("js_veto_ctor"));
t_veto_ctor.replace("$jswrapper", state.clazz(CTOR))
.replace("$jsname", state.clazz(NAME))
.pretty_print(f_wrappers);
}
/* Note: this makes sure that there is a swig_type added for this class */
String *clientData = NewString("");
Printf(clientData, "&%s_clientData", state.clazz(NAME_MANGLED));
/* Note: this makes sure that there is a swig_type added for this class */
SwigType_remember_clientdata(state.clazz(TYPE_MANGLED), NewString("0"));
// emit definition of v8 class template
Template t_def_class = getTemplate("jsv8_define_class_template");
t_def_class.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsname", state.clazz(NAME))
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
.replace("$jsdtor", state.clazz(DTOR))
.trim()
.pretty_print(f_init_class_templates);
Template t_class_instance = getTemplate("jsv8_create_class_instance");
t_class_instance.replace("$jsname", state.clazz(NAME))
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsctor", state.clazz(CTOR))
.trim()
.pretty_print(f_init_class_instances);
// emit inheritance setup
Node *baseClass = getBaseClass(n);
if (baseClass) {
String *base_name = Getattr(baseClass, "name");
Template t_inherit = getTemplate("jsv8_inherit");
String *base_name_mangled = SwigType_manglestr(base_name);
t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsbaseclass", base_name_mangled)
.trim()
.pretty_print(f_init_inheritance);
Delete(base_name_mangled);
}
// emit registration of class template
Template t_register = getTemplate("jsv8_register_class");
t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsname", state.clazz(NAME))
.replace("$jsparent", Getattr(state.clazz("nspace"), NAME_MANGLED))
.trim()
.pretty_print(f_init_register_classes);
return SWIG_OK;
}
int V8Emitter::enterVariable(Node *n) {
JSEmitter::enterVariable(n);
state.variable(GETTER, NULL_STR);
state.variable(SETTER, VETO_SET);
return SWIG_OK;
}
int V8Emitter::exitVariable(Node *n) {
if (GetFlag(n, "ismember")) {
if (GetFlag(state.variable(), IS_STATIC) || Equal(Getattr(n, "nodeType"), "enumitem")) {
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()
.pretty_print(f_init_static_wrappers);
} else {
Template t_register = getTemplate("jsv8_register_member_variable");
t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsname", state.variable(NAME))
.replace("$jsgetter", state.variable(GETTER))
.replace("$jssetter", state.variable(SETTER))
.trim()
.pretty_print(f_init_wrappers);
}
} else {
// Note: a global variable is treated like a static variable
// with the parent being a nspace object (instead of class object)
Template t_register = getTemplate("jsv8_register_static_variable");
t_register.replace("$jsparent", Getattr(current_namespace, NAME_MANGLED))
.replace("$jsname", state.variable(NAME))
.replace("$jsgetter", state.variable(GETTER))
.replace("$jssetter", state.variable(SETTER))
.trim()
.pretty_print(f_init_wrappers);
}
return SWIG_OK;
}
int V8Emitter::exitFunction(Node *n) {
bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
// create a dispatcher for overloaded functions
bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
if (is_overloaded) {
if (!Getattr(n, "sym:nextSibling")) {
//state.function(WRAPPER_NAME, Swig_name_wrapper(Getattr(n, "name")));
emitFunctionDispatcher(n, is_member);
} else {
//don't register wrappers of overloaded functions in function tables
return SWIG_OK;
}
}
// register the function at the specific context
if (is_member) {
if (GetFlag(state.function(), IS_STATIC)) {
Template t_register = getTemplate("jsv8_register_static_function");
t_register.replace("$jsparent", state.clazz(NAME_MANGLED))
.replace("$jsname", state.function(NAME))
.replace("$jswrapper", state.function(WRAPPER_NAME))
.trim()
.pretty_print(f_init_static_wrappers);
} else {
Template t_register = getTemplate("jsv8_register_member_function");
t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
.replace("$jsname", state.function(NAME))
.replace("$jswrapper", state.function(WRAPPER_NAME))
.trim()
.pretty_print(f_init_wrappers);
}
} else {
// Note: a global function is treated like a static function
// with the parent being a nspace object instead of class object
Template t_register = getTemplate("jsv8_register_static_function");
t_register.replace("$jsparent", Getattr(current_namespace, NAME_MANGLED))
.replace("$jsname", state.function(NAME))
.replace("$jswrapper", state.function(WRAPPER_NAME))
.trim()
.pretty_print(f_init_static_wrappers);
}
return SWIG_OK;
}
void V8Emitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) {
Parm *p;
String *tm;
int startIdx = 0;
if (is_member && !is_static && mode != Ctor) {
startIdx = 1;
}
// store number of arguments for argument checks
int num_args = emit_num_arguments(parms) - startIdx;
String *argcount = NewString("");
Printf(argcount, "%d", num_args);
Setattr(n, ARGCOUNT, argcount);
int i = 0;
for (p = parms; p;) {
String *arg = NewString("");
String *type = Getattr(p, "type");
// ignore varargs
if (SwigType_isvarargs(type))
break;
switch (mode) {
case Getter:
if (is_member && !is_static && i == 0) {
Printv(arg, "info.Holder()", 0);
i++;
} else {
Printf(arg, "args[%d]", i - startIdx);
i += GetInt(p, "tmap:in:numinputs");
}
break;
case Function:
if (is_member && !is_static && i == 0) {
Printv(arg, "args.Holder()", 0);
i++;
} else {
Printf(arg, "args[%d]", i - startIdx);
i += GetInt(p, "tmap:in:numinputs");
}
break;
case Setter:
if (is_member && !is_static && i == 0) {
Printv(arg, "info.Holder()", 0);
i++;
} else {
Printv(arg, "value", 0);
i++;
}
break;
case Ctor:
Printf(arg, "args[%d]", i);
i += GetInt(p, "tmap:in:numinputs");
break;
default:
Printf(stderr, "Illegal MarshallingMode.");
Exit(EXIT_FAILURE);
}
tm = emitInputTypemap(n, p, wrapper, arg);
Delete(arg);
if (tm) {
p = Getattr(p, "tmap:in:next");
} else {
p = nextSibling(p);
}
}
for (p = parms; p;) {
tm = emitCheckTypemap(n, p, wrapper, Getattr(p, "emit:input"));
if (tm) {
p = Getattr(p, "tmap:check:next");
} else {
p = nextSibling(p);
}
}
}
int V8Emitter::emitNamespaces() {
Iterator it;
for (it = First(namespaces); it.item; it = Next(it)) {
Hash *entry = it.item;
String *name = Getattr(entry, NAME);
String *name_mangled = Getattr(entry, NAME_MANGLED);
String *parent = Getattr(entry, PARENT);
String *parent_mangled = Getattr(entry, PARENT_MANGLED);
bool do_create = true;
bool do_register = true;
if (Equal(parent, "")) {
do_register = false;
}
// Note: 'exports' is by convention the name of the object where
// globals are stored into
if (Equal(name, "exports")) {
do_create = false;
}
if (do_create) {
// create namespace object and register it to the parent scope
Template t_create_ns = getTemplate("jsv8_create_namespace");
t_create_ns.replace("$jsmangledname", name_mangled)
.trim()
.pretty_print(f_init_namespaces);
}
if (do_register) {
Template t_register_ns = getTemplate("jsv8_register_namespace");
t_register_ns.replace("$jsmangledname", name_mangled)
.replace("$jsname", name)
.replace("$jsparent", parent_mangled)
.trim();
// prepend in order to achieve reversed order of registration statements
String *tmp_register_stmt = NewString("");
t_register_ns.pretty_print(tmp_register_stmt);
Insert(f_init_register_namespaces, 0, tmp_register_stmt);
Delete(tmp_register_stmt);
}
}
return SWIG_OK;
}
JSEmitter *swig_javascript_create_V8Emitter() {
return new V8Emitter();
}
/**********************************************************************
* Helper implementations
**********************************************************************/
JSEmitterState::JSEmitterState()
: globalHash(NewHash()) {
// initialize sub-hashes
Setattr(globalHash, "class", NewHash());
Setattr(globalHash, "function", NewHash());
Setattr(globalHash, "variable", NewHash());
}
JSEmitterState::~JSEmitterState() {
Delete(globalHash);
}
DOH *JSEmitterState::getState(const char *key, bool new_key) {
if (new_key) {
Hash *hash = NewHash();
Setattr(globalHash, key, hash);
}
return Getattr(globalHash, key);
}
DOH *JSEmitterState::globals() {
return globalHash;
}
DOH *JSEmitterState::globals(const char *key, DOH *initial) {
if (initial != 0) {
Setattr(globalHash, key, initial);
}
return Getattr(globalHash, key);
}
DOH *JSEmitterState::clazz(bool new_key) {
return getState("class", new_key);
}
DOH *JSEmitterState::clazz(const char *key, DOH *initial) {
DOH *c = clazz();
if (initial != 0) {
Setattr(c, key, initial);
}
return Getattr(c, key);
}
DOH *JSEmitterState::function(bool new_key) {
return getState("function", new_key);
}
DOH *JSEmitterState::function(const char *key, DOH *initial) {
DOH *f = function();
if (initial != 0) {
Setattr(f, key, initial);
}
return Getattr(f, key);
}
DOH *JSEmitterState::variable(bool new_key) {
return getState("variable", new_key);
}
DOH *JSEmitterState::variable(const char *key, DOH *initial) {
DOH *v = variable();
if (initial != 0) {
Setattr(v, key, initial);
}
return Getattr(v, key);
}
/*static*/
int JSEmitterState::IsSet(DOH *val) {
if (!val) {
return 0;
} else {
const char *cval = Char(val);
if (!cval)
return 0;
return (strcmp(cval, "0") != 0) ? 1 : 0;
}
}
/* -----------------------------------------------------------------------------
* Template::Template() : creates a Template class for given template code
* ----------------------------------------------------------------------------- */
Template::Template(const String *code_) {
if (!code_) {
Printf(stdout, "Template code was null. Illegal input for template.");
Exit(EXIT_FAILURE);
}
code = NewString(code_);
templateName = NewString("");
}
Template::Template(const String *code_, const String *templateName_) {
if (!code_) {
Printf(stdout, "Template code was null. Illegal input for template.");
Exit(EXIT_FAILURE);
}
code = NewString(code_);
templateName = NewString(templateName_);
}
/* -----------------------------------------------------------------------------
* Template::~Template() : cleans up of Template.
* ----------------------------------------------------------------------------- */
Template::~Template() {
Delete(code);
Delete(templateName);
}
/* -----------------------------------------------------------------------------
* String* Template::str() : retrieves the current content of the template.
* ----------------------------------------------------------------------------- */
String *Template::str() {
if (js_template_enable_debug) {
String *pre_code = NewString("");
String *post_code = NewString("");
String *debug_code = NewString("");
Printf(pre_code, "/* begin fragment(\"%s\") */", templateName);
Printf(post_code, "/* end fragment(\"%s\") */", templateName);
Printf(debug_code, "%s\n%s\n%s\n", pre_code, code, post_code);
Delete(code);
Delete(pre_code);
Delete(post_code);
code = debug_code;
}
return code;
}
Template & Template::trim() {
const char *str = Char(code);
if (str == 0)
return *this;
int length = Len(code);
if (length == 0)
return *this;
int idx;
for (idx = 0; idx < length; ++idx) {
if (str[idx] != ' ' && str[idx] != '\t' && str[idx] != '\r' && str[idx] != '\n')
break;
}
int start_pos = idx;
for (idx = length - 1; idx >= start_pos; --idx) {
if (str[idx] != ' ' && str[idx] != '\t' && str[idx] != '\r' && str[idx] != '\n')
break;
}
int end_pos = idx;
int new_length = end_pos - start_pos + 1;
char *newstr = new char[new_length + 1];
memcpy(newstr, str + start_pos, new_length);
newstr[new_length] = 0;
Delete(code);
code = NewString(newstr);
delete[]newstr;
return *this;
}
/* -----------------------------------------------------------------------------
* Template& Template::replace(const String* pattern, const String* repl) :
*
* replaces all occurrences of a given pattern with a given replacement.
*
* - pattern: the pattern to be replaced
* - repl: the replacement string
* - returns a reference to the Template to allow chaining of methods.
* ----------------------------------------------------------------------------- */
Template & Template::replace(const String *pattern, const String *repl) {
Replaceall(code, pattern, repl);
return *this;
}
Template & Template::print(DOH *doh) {
Printv(doh, str(), 0);
return *this;
}
Template & Template::pretty_print(DOH *doh) {
Wrapper_pretty_print(str(), doh);
return *this;
}
Template::Template(const Template & t) {
code = NewString(t.code);
templateName = NewString(t.templateName);
}
void Template::operator=(const Template & t) {
Delete(code);
Delete(templateName);
code = NewString(t.code);
templateName = NewString(t.templateName);
}