mirror of https://github.com/swig/swig
3251 lines
98 KiB
C++
3251 lines
98 KiB
C++
/* -----------------------------------------------------------------------------
|
|
* 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 INDEX "idx"
|
|
#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 IS_WRAPPED "is_wrapped"
|
|
#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 ARGREQUIRED "wrap:argmin"
|
|
#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,
|
|
NAPI
|
|
};
|
|
|
|
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;
|
|
|
|
virtual const char *getFunctionTemplate(bool);
|
|
|
|
virtual const char *getFunctionDispatcherTemplate(bool);
|
|
|
|
virtual const char *getOverloadedFunctionTemplate(bool);
|
|
|
|
virtual const char *getSetterTemplate(bool);
|
|
|
|
virtual const char *getGetterTemplate(bool);
|
|
|
|
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();
|
|
JSEmitter *swig_javascript_create_NAPIEmitter();
|
|
|
|
/**********************************************************************
|
|
* 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\
|
|
-napi - creates a NAPI 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], "-napi") == 0) {
|
|
if (engine != -1) {
|
|
Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
Swig_mark_arg(i);
|
|
engine = JSEmitter::NAPI;
|
|
} 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;
|
|
}
|
|
case JSEmitter::NAPI:
|
|
{
|
|
emitter = swig_javascript_create_NAPIEmitter();
|
|
Preprocessor_define("SWIG_JAVASCRIPT_NAPI 1", 0);
|
|
SWIG_library_directory("javascript/napi");
|
|
Preprocessor_define("BUILDING_NODE_EXTENSION 1", 0);
|
|
if (!cparse_cplusplus) {
|
|
Swig_cparse_cplusplusout(1);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Printf(stderr, "SWIG Javascript: Unknown engine. Please specify one of '-jsc', '-v8', '-node' or '-napi'.\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;
|
|
}
|
|
|
|
const char *JSEmitter::getFunctionTemplate(bool) {
|
|
return "js_function";
|
|
}
|
|
|
|
const char *JSEmitter::getFunctionDispatcherTemplate(bool) {
|
|
return "js_function_dispatcher";
|
|
}
|
|
|
|
const char *JSEmitter::getOverloadedFunctionTemplate(bool) {
|
|
return "js_overloaded_function";
|
|
}
|
|
|
|
const char *JSEmitter::getGetterTemplate(bool) {
|
|
return "js_getter";
|
|
}
|
|
|
|
const char *JSEmitter::getSetterTemplate(bool) {
|
|
return "js_setter";
|
|
}
|
|
|
|
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);
|
|
|
|
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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", wrap_name)
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jslocals", wrapper->locals)
|
|
.replace("$jscode", wrapper->code)
|
|
.replace("$jsargcount", Getattr(n, ARGCOUNT))
|
|
.replace("$jsparent", state.clazz(PARENT_MANGLED))
|
|
.replace("$jsargrequired", Getattr(n, ARGREQUIRED))
|
|
.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))
|
|
.replace("$jsargrequired", Getattr(n, ARGREQUIRED));
|
|
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("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsdispatchcases", state.clazz(CTOR_DISPATCHERS))
|
|
.replace("$jsparent", state.clazz(PARENT_MANGLED))
|
|
.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(getGetterTemplate(is_member)));
|
|
|
|
// 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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.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(getSetterTemplate(is_member)));
|
|
|
|
// 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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.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");
|
|
}
|
|
|
|
bool is_member = GetFlag(n, "ismember");
|
|
Template t_getter(getTemplate(getGetterTemplate(is_member)));
|
|
|
|
// 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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", wname)
|
|
.replace("$jslocals", wrapper->locals)
|
|
.replace("$jscode", wrapper->code)
|
|
.pretty_print(f_wrappers);
|
|
|
|
exitVariable(n);
|
|
|
|
// This is the counterpart to the "constant" test in
|
|
// exitVariable, it prevents double setting of
|
|
// symbols that are both constants and variables
|
|
SetFlag(n, "constant");
|
|
|
|
DelWrapper(wrapper);
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int JSEmitter::emitFunction(Node *n, bool is_member, bool is_static) {
|
|
Wrapper *wrapper = NewWrapper();
|
|
Template t_function(getTemplate(getFunctionTemplate(is_member)));
|
|
|
|
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(getOverloadedFunctionTemplate(is_member));
|
|
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);
|
|
|
|
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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", wrap_name)
|
|
.replace("$jslocals", wrapper->locals)
|
|
.replace("$jscode", wrapper->code)
|
|
.replace("$jsargcount", Getattr(n, ARGCOUNT))
|
|
.replace("$jsargrequired", Getattr(n, ARGREQUIRED))
|
|
.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))
|
|
.replace("$jsargrequired", Getattr(sibl, ARGREQUIRED));
|
|
|
|
Append(wrapper->code, t_dispatch_case.str());
|
|
}
|
|
|
|
} while ((sibl = Getattr(sibl, "sym:nextSibling")));
|
|
|
|
Template t_function(getTemplate(getFunctionDispatcherTemplate(is_member)));
|
|
|
|
// 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("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.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) {
|
|
String *code = NewString("");
|
|
// Get input typemap for current param
|
|
String *tm = Getattr(p, "tmap:in");
|
|
String *tm_def = Getattr(p, "tmap:default");
|
|
SwigType *type = Getattr(p, "type");
|
|
int argmin = -1;
|
|
int argidx = -1;
|
|
bool is_optional = false;
|
|
|
|
if (Getattr(n, ARGREQUIRED)) {
|
|
argmin = GetInt(n, ARGREQUIRED);
|
|
}
|
|
if (Getattr(p, INDEX)) {
|
|
argidx = GetInt(p, INDEX);
|
|
}
|
|
|
|
if (tm_def != NULL) {
|
|
is_optional = true;
|
|
}
|
|
if (argmin >= 0 && argidx >= 0 && argidx >= argmin) {
|
|
is_optional = true;
|
|
}
|
|
if (is_optional && Getattr(p, INDEX) == NULL) {
|
|
Printf(stderr, "Argument %s in %s cannot be a default argument\n",
|
|
Getattr(p, NAME), state.function(NAME));
|
|
return SWIG_ERROR;
|
|
}
|
|
|
|
if (is_optional) {
|
|
Template t_check_default(getTemplate("js_check_arg"));
|
|
|
|
t_check_default.replace("$jsarg", Getattr(p, INDEX)).pretty_print(code);
|
|
Printf(code, "{\n");
|
|
}
|
|
|
|
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"));
|
|
if (!checkAttribute(p, "tmap:in:noblock", "1")) {
|
|
Printf(code, "{\n");
|
|
}
|
|
Printf(code, "%s", tm);
|
|
if (!checkAttribute(p, "tmap:in:noblock", "1")) {
|
|
Printf(code, "\n}\n");
|
|
}
|
|
} 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));
|
|
}
|
|
|
|
if (is_optional) {
|
|
Printf(code, "}\n");
|
|
}
|
|
|
|
Append(wrapper->code, code);
|
|
return code;
|
|
}
|
|
|
|
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 = NULL;
|
|
|
|
// 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);
|
|
int num_required = emit_num_required(parms) - startIdx;
|
|
SetInt(n, ARGREQUIRED, num_required);
|
|
|
|
// 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);
|
|
SetInt(p, INDEX, 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);
|
|
SetInt(p, INDEX, i);
|
|
i += GetInt(p, "tmap:in:numinputs");
|
|
break;
|
|
default:
|
|
Printf(stderr, "Illegal MarshallingMode.");
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// numinputs=0 typemaps are emitted by the legacy code in
|
|
// emit_attach_parmmaps() in emit.cxx, check the comment there
|
|
if (!checkAttribute(p, "tmap:in:numinputs", "0"))
|
|
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 = NULL;
|
|
|
|
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 num_required = emit_num_required(parms) - startIdx;
|
|
SetInt(n, ARGREQUIRED, num_required);
|
|
|
|
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);
|
|
SetInt(p, INDEX, 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);
|
|
SetInt(p, INDEX, 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);
|
|
SetInt(p, INDEX, i);
|
|
i += GetInt(p, "tmap:in:numinputs");
|
|
break;
|
|
default:
|
|
Printf(stderr, "Illegal MarshallingMode.");
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// numinputs=0 typemaps are emitted by the legacy code in
|
|
// emit_attach_parmmaps() in emit.cxx, check the comment there
|
|
// All language backends work around this
|
|
if (!checkAttribute(p, "tmap:in:numinputs", "0"))
|
|
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;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* NAPI: JSEmitter implementation for N-API
|
|
**********************************************************************/
|
|
|
|
class NAPIEmitter : public JSEmitter {
|
|
public:
|
|
NAPIEmitter();
|
|
|
|
virtual ~NAPIEmitter();
|
|
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();
|
|
virtual int emitCtor(Node *);
|
|
virtual int emitDtor(Node *);
|
|
virtual int emitClassMethodDeclaration(Node *);
|
|
|
|
virtual const char *getFunctionTemplate(bool is_member);
|
|
virtual const char *getFunctionDispatcherTemplate(bool is_member);
|
|
virtual const char *getOverloadedFunctionTemplate(bool is_member);
|
|
virtual const char *getSetterTemplate(bool is_member);
|
|
virtual const char *getGetterTemplate(bool is_member);
|
|
|
|
protected:
|
|
/* built-in parts */
|
|
String *f_runtime;
|
|
String *f_header;
|
|
String *f_init;
|
|
String *f_post_init;
|
|
|
|
/* class declarations */
|
|
String *f_class_declarations;
|
|
|
|
/* parts for initilizer */
|
|
String *f_init_namespaces;
|
|
String *f_init_wrappers;
|
|
String *f_init_inheritance;
|
|
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;
|
|
|
|
// the current index in the class table
|
|
size_t class_idx;
|
|
};
|
|
|
|
NAPIEmitter::NAPIEmitter()
|
|
: JSEmitter(JSEmitter::NAPI), NULL_STR(NewString("0")),
|
|
VETO_SET(NewString("JS_veto_set_variable")), class_idx(0) {}
|
|
|
|
NAPIEmitter::~NAPIEmitter() {
|
|
Delete(NULL_STR);
|
|
Delete(VETO_SET);
|
|
}
|
|
|
|
int NAPIEmitter::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_init = NewString("");
|
|
f_post_init = NewString("");
|
|
|
|
f_class_declarations = NewString("");
|
|
|
|
f_init_namespaces = NewString("");
|
|
f_init_wrappers = NewString("");
|
|
f_init_inheritance = 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 NAPIEmitter::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_declarations, "\n", 0);
|
|
Printv(f_wrap_cpp, f_wrappers, "\n", 0);
|
|
|
|
emitNamespaces();
|
|
|
|
String *inheritance = NewStringEmpty();
|
|
if (Len(f_init_inheritance) > 0) {
|
|
Template t_inheritance(getTemplate("js_init_inheritance"));
|
|
t_inheritance.pretty_print(inheritance);
|
|
}
|
|
|
|
// compose the initializer function using a template
|
|
// filled with sub-parts
|
|
Template initializer(getTemplate("js_initializer"));
|
|
initializer.replace("$jsname", moduleName)
|
|
.replace("$jsnapinspaces", f_init_namespaces)
|
|
.replace("$jsnapipreinheritance", inheritance)
|
|
.replace("$jsnapiinitinheritance", f_init_inheritance)
|
|
.replace("$jsnapiregisterclasses", f_init_register_classes)
|
|
.replace("$jsnapiregisternspaces", 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);
|
|
|
|
Delete(inheritance);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::close() {
|
|
Delete(f_runtime);
|
|
Delete(f_header);
|
|
Delete(f_class_declarations);
|
|
Delete(f_init_namespaces);
|
|
Delete(f_init_wrappers);
|
|
Delete(f_init_inheritance);
|
|
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;
|
|
}
|
|
|
|
const char *NAPIEmitter::getFunctionTemplate(bool is_member) {
|
|
return is_member ? "js_function" : "js_global_function";
|
|
}
|
|
|
|
const char *NAPIEmitter::getFunctionDispatcherTemplate(bool is_member) {
|
|
return is_member ? "js_function_dispatcher" : "js_global_function_dispatcher";
|
|
}
|
|
|
|
const char *NAPIEmitter::getOverloadedFunctionTemplate(bool is_member) {
|
|
return is_member ? "js_overloaded_function" : "js_global_overloaded_function";
|
|
}
|
|
|
|
const char *NAPIEmitter::getGetterTemplate(bool is_member) {
|
|
return is_member ? "js_getter" : "js_global_getter";
|
|
}
|
|
|
|
const char *NAPIEmitter::getSetterTemplate(bool is_member) {
|
|
return is_member ? "js_setter" : "js_global_setter";
|
|
}
|
|
|
|
int NAPIEmitter::enterClass(Node *n) {
|
|
JSEmitter::enterClass(n);
|
|
|
|
// emit registration of class template
|
|
String *idx = NewString("");
|
|
Printf(idx, "%d", class_idx++);
|
|
Template t_register = getTemplate("jsnapi_registerclass");
|
|
t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsparent", Getattr(state.clazz("nspace"), NAME_MANGLED))
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jsclassidx", idx)
|
|
.trim()
|
|
.pretty_print(f_init_register_classes);
|
|
Delete(idx);
|
|
|
|
// emit inheritance
|
|
String *baseMangled;
|
|
Node *baseClass = getBaseClass(n);
|
|
SetFlag(n, IS_WRAPPED);
|
|
if (baseClass && GetFlag(baseClass, IS_WRAPPED)) {
|
|
String *jsName = NewString("");
|
|
String *nspace = Getattr(baseClass, "sym:nspace");
|
|
if (Len(nspace) == 0) nspace = Getattr(current_namespace, NAME_MANGLED);
|
|
Printf(jsName, "%s_%s", nspace, Getattr(baseClass, "sym:name"));
|
|
baseMangled = SwigType_manglestr(jsName);
|
|
Delete(jsName);
|
|
|
|
f_init_wrappers = Copy(Getattr(baseClass, MEMBER_FUNCTIONS));
|
|
f_init_static_wrappers = Copy(Getattr(baseClass, STATIC_FUNCTIONS));
|
|
} else {
|
|
baseMangled = NewString("SWIG_NAPI_ObjectWrap");
|
|
f_init_wrappers = NewString("");
|
|
f_init_static_wrappers = NewString("");
|
|
}
|
|
state.clazz(PARENT_MANGLED, baseMangled);
|
|
|
|
Template t_setup_inheritance(getTemplate("jsnapi_setup_inheritance"));
|
|
t_setup_inheritance.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", state.clazz(CTOR))
|
|
.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsparent", baseMangled)
|
|
.pretty_print(f_init_inheritance);
|
|
|
|
// emit declaration of a NAPI class template
|
|
Template t_decl_class(getTemplate("jsnapi_class_prologue_template"));
|
|
t_decl_class.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsparent", baseMangled)
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
|
|
Delete(baseMangled);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::exitClass(Node *n) {
|
|
if (GetFlag(state.clazz(), IS_ABSTRACT)) {
|
|
Template t_veto_ctor(getTemplate("js_veto_ctor"));
|
|
t_veto_ctor.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", state.clazz(CTOR))
|
|
.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsparent", state.clazz(PARENT_MANGLED))
|
|
.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 NAPI class template
|
|
Template t_def_class = getTemplate("jsnapi_class_epilogue_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_class_declarations);
|
|
|
|
Template t_class_instance = getTemplate("jsnapi_declare_class_instance");
|
|
t_class_instance.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
|
|
Template t_class_template = getTemplate("jsnapi_getclass");
|
|
t_class_template.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsnapiwrappers", f_init_wrappers)
|
|
.replace("$jsnapistaticwrappers", f_init_static_wrappers)
|
|
.replace("$jsparent", state.clazz(PARENT_MANGLED))
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
|
|
/* Save these to be reused in the child classes */
|
|
Setattr(n, MEMBER_FUNCTIONS, f_init_wrappers);
|
|
Setattr(n, STATIC_FUNCTIONS, f_init_static_wrappers);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::enterVariable(Node *n) {
|
|
// Somehow, this is not always reset
|
|
// (some constructs like smart pointers reuse Nodes)
|
|
UnsetFlag(n, "constant");
|
|
|
|
JSEmitter::enterVariable(n);
|
|
|
|
state.variable(GETTER, VETO_SET);
|
|
state.variable(SETTER, VETO_SET);
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::exitVariable(Node *n) {
|
|
// Due to special handling of C++ "static const" member variables
|
|
// (refer to the comment in lang.cxx:Language::staticmembervariableHandler)
|
|
// a static const member variable may get transformed into a constant
|
|
// and be emitted by emitConstant which will result calling exitVariable twice
|
|
if (GetFlag(n, "constant")) {
|
|
return SWIG_OK;
|
|
}
|
|
|
|
if (GetFlag(n, "ismember")) {
|
|
String *modifier = NewStringEmpty();
|
|
if (GetFlag(state.variable(), IS_STATIC) ||
|
|
Equal(Getattr(n, "nodeType"), "enumitem")) {
|
|
Template t_register = getTemplate("jsnapi_register_static_variable");
|
|
t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsname", state.variable(NAME))
|
|
.replace("$jsgetter", state.variable(GETTER))
|
|
.replace("$jssetter", state.variable(SETTER) != VETO_SET
|
|
? state.variable(SETTER)
|
|
: "JS_veto_set_static_variable")
|
|
.trim()
|
|
.pretty_print(f_init_static_wrappers);
|
|
Append(modifier, "static");
|
|
} else {
|
|
Template t_register = getTemplate("jsnapi_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);
|
|
}
|
|
|
|
// emit declaration of a class member function
|
|
Template t_getter = getTemplate("jsnapi_class_method_declaration");
|
|
t_getter.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jsdtor", state.clazz(DTOR))
|
|
.replace("$jswrapper", state.variable(GETTER))
|
|
.replace("$jsstatic", modifier)
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
if (state.variable(SETTER) != VETO_SET) {
|
|
Template t_setter = getTemplate("jsnapi_class_setter_declaration");
|
|
t_setter.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jsname", state.clazz(NAME))
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jsdtor", state.clazz(DTOR))
|
|
.replace("$jswrapper", state.variable(SETTER))
|
|
.replace("$jsstatic", modifier)
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
}
|
|
Delete(modifier);
|
|
} else {
|
|
Template t_register = getTemplate("jsnapi_register_global_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_register_namespaces);
|
|
}
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::emitClassMethodDeclaration(Node *) {
|
|
// emit declaration of a class member function
|
|
Template t_def_class = getTemplate("jsnapi_class_method_declaration");
|
|
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))
|
|
.replace("$jswrapper", state.function(WRAPPER_NAME))
|
|
.replace("$jsstatic", GetFlag(state.function(), IS_STATIC) ? "static" : "")
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::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) {
|
|
emitClassMethodDeclaration(n);
|
|
if (!Getattr(n, "sym:nextSibling")) {
|
|
emitFunctionDispatcher(n, is_member);
|
|
} else {
|
|
return SWIG_OK;
|
|
}
|
|
}
|
|
// register the function at the specific context
|
|
if (is_member) {
|
|
if (GetFlag(state.function(), IS_STATIC)) {
|
|
Template t_register = getTemplate("jsnapi_register_static_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_static_wrappers);
|
|
} else {
|
|
Template t_register = getTemplate("jsnapi_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);
|
|
}
|
|
|
|
emitClassMethodDeclaration(n);
|
|
} 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("jsnapi_register_global_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_register_namespaces);
|
|
}
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
void NAPIEmitter::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 num_required = emit_num_required(parms) - startIdx;
|
|
SetInt(n, ARGREQUIRED, num_required);
|
|
|
|
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.This()", 0);
|
|
i++;
|
|
} else {
|
|
Printf(arg, "info[%d]", i - startIdx);
|
|
SetInt(p, INDEX, i - startIdx);
|
|
i += GetInt(p, "tmap:in:numinputs");
|
|
}
|
|
break;
|
|
case Function:
|
|
if (is_member && !is_static && i == 0) {
|
|
Printv(arg, "info.This()", 0);
|
|
i++;
|
|
} else {
|
|
Printf(arg, "info[%d]", i - startIdx);
|
|
SetInt(p, INDEX, i - startIdx);
|
|
i += GetInt(p, "tmap:in:numinputs");
|
|
}
|
|
break;
|
|
case Setter:
|
|
if (is_member && !is_static && i == 0) {
|
|
Printv(arg, "info.This()", 0);
|
|
i++;
|
|
} else {
|
|
Printv(arg, "value", 0);
|
|
i++;
|
|
}
|
|
break;
|
|
case Ctor:
|
|
Printf(arg, "info[%d]", i);
|
|
SetInt(p, INDEX, i - startIdx);
|
|
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:in:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
int NAPIEmitter::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("jsnapi_create_namespace");
|
|
t_create_ns.replace("$jsmangledname", name_mangled)
|
|
.trim()
|
|
.pretty_print(f_init_namespaces);
|
|
}
|
|
|
|
if (do_register) {
|
|
Template t_register_ns = getTemplate("jsnapi_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;
|
|
}
|
|
|
|
int NAPIEmitter::emitCtor(Node *n) {
|
|
int r;
|
|
|
|
// Constructor renaming does not work in JavaScript
|
|
// This allows us to slip past the unit test which
|
|
// is broken for all JavaScript backends
|
|
if (GetFlag(n, "sym:overloaded")) {
|
|
if (!Getattr(n, "sym:nextSibling")) {
|
|
if (GetFlag(state.clazz(), "ctor:dispatcher:emitted")) {
|
|
return SWIG_OK;
|
|
}
|
|
SetFlag(state.clazz(), "ctor:dispatcher:emitted");
|
|
}
|
|
}
|
|
|
|
r = JSEmitter::emitCtor(n);
|
|
if (r != SWIG_OK)
|
|
return r;
|
|
|
|
Template t_getter = getTemplate("jsnapi_class_method_declaration");
|
|
t_getter.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.replace("$jswrapper", Getattr(n, "wrap:name"))
|
|
.replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
|
|
.replace("$jsstatic", "")
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
int NAPIEmitter::emitDtor(Node *n) {
|
|
// NAPI destructors must have a class declaration
|
|
Template t_getter = getTemplate("jsnapi_class_dtor_declaration");
|
|
t_getter.replace("$jsmangledname", state.clazz(NAME_MANGLED))
|
|
.trim()
|
|
.pretty_print(f_class_declarations);
|
|
return JSEmitter::emitDtor(n);
|
|
}
|
|
|
|
JSEmitter *swig_javascript_create_V8Emitter() {
|
|
return new V8Emitter();
|
|
}
|
|
|
|
JSEmitter *swig_javascript_create_NAPIEmitter() {
|
|
return new NAPIEmitter();
|
|
}
|
|
|
|
/**********************************************************************
|
|
* 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);
|
|
}
|