| /* ----------------------------------------------------------------------------- | |
| * 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: | |
| /* | |
| * Helper function for detecting if the constructor Node does not use a name that matches | |
| * the expected name for a constructor. Occurs when %rename is used for just a constructor | |
| * or %template instantiates a templated constructor with a different name to the class. | |
| */ | |
| bool isRenamedConstructor(Node *n); | |
| /* | |
| * 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 = "\ | |
| 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\ | |
| \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); | |
| // 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"); | |
| String *view = Getattr(n, "view"); | |
| if (kind) { | |
| if (Equal(kind, "function") | |
| // HACK: functions declared through a function typedef (see sneaky.ctest) keep kind 'variable', so detect them via 'view' | |
| || (Equal(kind, "variable") && (Equal(view, "globalfunctionHandler") || Equal(view, "memberfunctionHandler")))) { | |
| 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 { | |
| 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_immutable(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"; | |
| } | |
| bool JSEmitter::isRenamedConstructor(Node *n) { | |
| Node *cls = parentNode(n); | |
| if (!Equal(nodeType(cls), "class")) { | |
| cls = parentNode(cls); | |
| assert(Equal(nodeType(cls), "class")); | |
| } | |
| return !Equal(Getattr(n, "constructorHandler:sym:name"), Getattr(cls, "sym:name")); | |
| } | |
| int JSEmitter::emitCtor(Node *n) { | |
| // Constructor renaming does not work in JavaScript. | |
| // This allows us to slip past the unit tests which are broken for all JavaScript backends. | |
| // TODO: fix by correcting the bad constructor name - this is the approach used in the Java module. | |
| if (isRenamedConstructor(n)) | |
| return SWIG_ERROR; | |
| Wrapper *wrapper = NewWrapper(); | |
| bool is_overloaded = GetFlag(n, "sym:overloaded") != 0; | |
| Template t_ctor(getTemplate("js_ctor")); | |
| // prepare the function wrapper name | |
| String *iname = Getattr(n, "sym:name"); | |
| SwigType *returntype = Getattr(n, "type"); | |
| String *wrap_name = Swig_name_wrapper(iname); | |
| 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_parm_typemaps(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); | |
| bool isvoid = !Cmp(returntype, "void"); | |
| emit_isvoid_special_variables(n, wrapper->code, isvoid); | |
| Replaceall(wrapper->code, "$symname", iname); | |
| 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_parm_typemaps(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_parm_typemaps(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 *value = 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"); | |
| SwigType *returntype = Getattr(n, "type"); | |
| 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_parm_typemaps(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); | |
| bool isvoid = !Cmp(returntype, "void"); | |
| emit_isvoid_special_variables(n, wrapper->code, isvoid); | |
| 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")); | |
| Printf(code, "%s\n", tm); | |
| } else { | |
| Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(type, 0)); | |
| return NULL; | |
| } | |
| 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 != NULL) { | |
| 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;) { | |
| p = emit_ignored_in_typemaps(p, wrapper->code); | |
| if (!p) | |
| break; | |
| 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); | |
| } | |
| 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;) { | |
| p = emit_ignored_in_typemaps(p, wrapper->code); | |
| if (!p) | |
| break; | |
| 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, "SWIGV8_INFO_HOLDER(info)", 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, "SWIGV8_ARGS_THIS(args)", 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, "SWIGV8_INFO_HOLDER(info)", 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); | |
| } | |
| 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;) { | |
| p = emit_ignored_in_typemaps(p, wrapper->code); | |
| if (!p) | |
| break; | |
| 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:check: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 = 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); | |
| } |