diff --git a/Lib/javascript/javascript.swg b/Lib/javascript/javascript.swg new file mode 100644 index 000000000..fb01a63bb --- /dev/null +++ b/Lib/javascript/javascript.swg @@ -0,0 +1,15 @@ +/* ----------------------------------------------------------------------------- + * javascript.swg + * + * Javascript typemaps + * ----------------------------------------------------------------------------- */ + +%include + +%include + +%include + +%include + +%include diff --git a/Lib/javascript/javascriptkw.swg b/Lib/javascript/javascriptkw.swg new file mode 100644 index 000000000..c3c118391 --- /dev/null +++ b/Lib/javascript/javascriptkw.swg @@ -0,0 +1,40 @@ +#ifndef JAVASCRIPT_JAVASCRIPTKW_SWG_ +#define JAVASCRIPT_JAVASCRIPTKW_SWG_ + +/* Warnings for Java keywords */ +#define JAVASCRIPTKW(x) %keywordwarn("'" `x` "' is a javascript keyword, renaming to '_"`x`"'",rename="_%s") `x` + +/* Taken from https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Reserved_Words */ + +JAVASCRIPTKW(break); +JAVASCRIPTKW(case); +JAVASCRIPTKW(catch); +JAVASCRIPTKW(continue); +JAVASCRIPTKW(default); +JAVASCRIPTKW(delete); +JAVASCRIPTKW(do); +JAVASCRIPTKW(else); +JAVASCRIPTKW(finally); +JAVASCRIPTKW(for); +JAVASCRIPTKW(function); +JAVASCRIPTKW(if); +JAVASCRIPTKW(in); +JAVASCRIPTKW(instanceof); +JAVASCRIPTKW(new); +JAVASCRIPTKW(return); +JAVASCRIPTKW(switch); +JAVASCRIPTKW(this); +JAVASCRIPTKW(throw); +JAVASCRIPTKW(try); +JAVASCRIPTKW(typeof); +JAVASCRIPTKW(var); +JAVASCRIPTKW(void); +JAVASCRIPTKW(while); +JAVASCRIPTKW(with); + +/* others bad names if any*/ +// for example %namewarn("321:clone() is a javascript bad method name") *::clone(); + +#undef JAVASCRIPTKW + +#endif //JAVASCRIPT_JAVASCRIPTKW_SWG_ diff --git a/Source/Makefile.am b/Source/Makefile.am index 984b9c268..fa11bfdcb 100644 --- a/Source/Makefile.am +++ b/Source/Makefile.am @@ -49,6 +49,8 @@ eswig_SOURCES = CParse/cscanner.c \ Modules/guile.cxx \ Modules/java.cxx \ Modules/lang.cxx \ + Modules/javascript.cxx \ + Modules/javascript_emitter.cxx \ Modules/lua.cxx \ Modules/main.cxx \ Modules/modula3.cxx \ diff --git a/Source/Modules/javascript.cxx b/Source/Modules/javascript.cxx new file mode 100644 index 000000000..e1336d82a --- /dev/null +++ b/Source/Modules/javascript.cxx @@ -0,0 +1,246 @@ +#include "swigmod.h" + +#include +#include +#include + +#include "javascript_emitter.h" + +/* ******************************************************************** + * JAVASCRIPT + * ********************************************************************/ + +class JAVASCRIPT : public Language { + +public: + JAVASCRIPT() {} + ~JAVASCRIPT() {} + + virtual int functionHandler(Node *n); + virtual int globalfunctionHandler(Node *n); + virtual int variableHandler(Node *n); + virtual int globalvariableHandler(Node *n); + virtual int classHandler(Node *n); + virtual int functionWrapper(Node *n); + + /** + * Registers all %fragments assigned to section "templates" with the Emitter. + **/ + virtual int fragmentDirective(Node *n); + + virtual void main(int argc, char *argv[]); + virtual int top(Node *n); + +private: + + JSEmitter* emitter; +}; + +/* --------------------------------------------------------------------- + * functionWrapper() + * + * Low level code generator for functions + * --------------------------------------------------------------------- */ + +int JAVASCRIPT::functionWrapper(Node *n) { + + Language::functionWrapper(n); + + emitter->EmitWrapperFunction(n); + + return SWIG_OK; +} + +/* --------------------------------------------------------------------- + * functionHandler() + * + * Function handler for generating wrappers for functions + * --------------------------------------------------------------------- */ + +int JAVASCRIPT::functionHandler(Node *n) { + + 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->SwitchContext(n); + + Language::globalfunctionHandler(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->SwitchContext(n); + + Language::globalvariableHandler(n); + return SWIG_OK; +} + +/* --------------------------------------------------------------------- + * classHandler() + * + * Function handler for generating wrappers for class + * --------------------------------------------------------------------- */ + +int JAVASCRIPT::classHandler(Node *n) { + emitter->SwitchContext(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(Cmp(section, "templates") == 0) { + emitter->RegisterTemplate(Getattr(n, "value"), Getattr(n, "code")); + } else { + Swig_fragment_register(n); + } + + return SWIG_OK; +} + +/* --------------------------------------------------------------------- + * 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(); + + delete emitter; + + return SWIG_OK; +} + +/* --------------------------------------------------------------------- + * main() + * + * Entry point for the JAVASCRIPT module + * --------------------------------------------------------------------- */ + +void JAVASCRIPT::main(int argc, char *argv[]) { + + const char* lib_dir; + + // Set javascript subdirectory in SWIG library + SWIG_library_directory("javascript"); + + int mode = -1; + + for (int i = 1; i < argc; i++) { + if (argv[i]) { + if (strcmp(argv[i], "-v8") == 0) { + Swig_mark_arg(i); + mode = JSEmitter::V8; + SWIG_library_directory("javascript/v8"); + } else if (strcmp(argv[i], "-jsc") == 0) { + Swig_mark_arg(i); + mode = JSEmitter::JavascriptCore; + SWIG_library_directory("javascript/jsc"); + } else if (strcmp(argv[i], "-qt") == 0) { + Swig_mark_arg(i); + mode = JSEmitter::QtScript; + SWIG_library_directory("javascript/qt"); + } + } + } + + switch(mode) { + case JSEmitter::V8: + { + // TODO: emitter = create_v8_emitter(); + break; + } + case JSEmitter::JavascriptCore: + { + // TODO: emitter = create_jsc_emitter(); + break; + } + case JSEmitter::QtScript: + { + // TODO: emitter = create_qtscript_emitter(); + break; + } + default: + { + Printf(stderr, "Unknown emitter type."); + SWIG_exit(-1); + } + } + + + // Add a symbol to the parser for conditional compilation + Preprocessor_define("SWIGJAVASCRIPT 1", 0); + + // Add typemap definitions + SWIG_typemap_lang("javascript"); + + // Set configuration file + SWIG_config_file("javascript.swg"); + + allow_overloading(); +} + +/* ----------------------------------------------------------------------------- + * swig_JAVASCRIPT() - Instantiate module + * ----------------------------------------------------------------------------- */ + +static Language *new_swig_javascript() { + return new JAVASCRIPT(); +} + +extern "C" Language *swig_javascript(void) { + return new_swig_javascript(); +} diff --git a/Source/Modules/javascript_emitter.cxx b/Source/Modules/javascript_emitter.cxx new file mode 100644 index 000000000..7b49aaf5d --- /dev/null +++ b/Source/Modules/javascript_emitter.cxx @@ -0,0 +1,198 @@ +#include "javascript_emitter.h" + +#include "swigmod.h" + +/* ----------------------------------------------------------------------------- + * JSEmitter() + * ----------------------------------------------------------------------------- */ + +JSEmitter::JSEmitter() + : empty_string(NewString("")) +{ + templates = NewHash(); +} + +/* ----------------------------------------------------------------------------- + * ~JSEmitter() + * ----------------------------------------------------------------------------- */ + +JSEmitter::~JSEmitter() +{ + Delete(empty_string); + Delete(templates); +} + +/* ----------------------------------------------------------------------------- + * JSEmitter::RegisterTemplate() : Registers a code template + * ----------------------------------------------------------------------------- */ + +int JSEmitter::RegisterTemplate(const String *name, const String *code) +{ + return Setattr(templates, name, code); +} + +/* ----------------------------------------------------------------------------- + * JSEmitter::GetTemplate() : Retrieves a registered a code template + * ----------------------------------------------------------------------------- */ + +const String* JSEmitter::GetTemplate(const String *name) +{ + String* templ = Getattr(templates, name); + + if(!templ) { + Printf(stderr, "Could not find template %s\n.", name); + SWIG_exit(EXIT_FAILURE); + } + + return templ; +} + +/* ----------------------------------------------------------------------------- + * JSEmitter::typemapLookup() + * n - for input only and must contain info for Getfile(n) and Getline(n) to work + * tmap_method - typemap method name + * type - typemap type to lookup + * warning - warning number to issue if no typemaps found + * typemap_attributes - the typemap attributes are attached to this node and will + * also be used for temporary storage if non null + * return is never NULL, unlike Swig_typemap_lookup() + * ----------------------------------------------------------------------------- */ + +const String *JSEmitter::typemapLookup(Node *n, const_String_or_char_ptr tmap_method, + SwigType *type, int warning, Node *typemap_attributes) { + Node *node = !typemap_attributes ? NewHash() : typemap_attributes; + Setattr(node, "type", type); + Setfile(node, Getfile(n)); + Setline(node, Getline(n)); + const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0); + if (!tm) { + tm = empty_string; + if (warning != WARN_NONE) { + Swig_warning(warning, Getfile(n), Getline(n), + "No %s typemap defined for %s\n", tmap_method, SwigType_str(type, 0)); + } + } + if (!typemap_attributes) { + Delete(node); + } + return tm; +} + +/* ----------------------------------------------------------------------------- + * JSEmitter::GetBaseClass() : the node of the base class or NULL + * ----------------------------------------------------------------------------- */ + +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 + * ----------------------------------------------------------------------------- */ + +int JSEmitter::EmitWrapperFunction(Node* n) +{ + int ret = SWIG_OK; + + current_wrapper = NewWrapper(); + Setattr(n, "wrap:name", Getattr(n, "sym:name")); + + String* kind = Getattr(n, "kind"); + + if(kind) { + bool is_member = GetFlag(n, "ismember"); + if( Cmp(kind, "function") == 0 ) { + ret = EmitFunction(n, is_member); + } else if (Cmp(kind, "variable") == 0) { + if(IsSetterMethod(n)) { + ret = EmitSetter(n, is_member); + } else { + ret = EmitGetter(n, is_member); + } + } else { + Printf(stderr, "Warning: unsupported wrapper function type\n"); + Swig_print_node(n); + } + } else { + String *view = Getattr(n, "view"); + + if( Cmp(view, "constructorHandler") == 0 ) { + ret = EmitCtor(n); + + } else if( Cmp(view, "destructorHandler") == 0 ) { + ret = EmitDtor(n); + + } else { + Printf(stderr, "Warning: unsupported wrapper function type"); + Swig_print_node(n); + } + } + + DelWrapper(current_wrapper); + current_wrapper = 0; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * str_ends_with() : c string helper to check suffix match + * ----------------------------------------------------------------------------- */ + +// TODO: shift this to DOH string API +int str_ends_with(const char * str, const char * suffix) { + + if( str == NULL || suffix == NULL ) + return 0; + + size_t str_len = strlen(str); + size_t suffix_len = strlen(suffix); + + if(suffix_len > str_len) + return 0; + + return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len ); +} + +/* ----------------------------------------------------------------------------- + * JSEmitter::IsSetterMethod() : helper to check if a method is a setter function + * ----------------------------------------------------------------------------- */ + +bool JSEmitter::IsSetterMethod(Node *n) { + String* symname = Getattr(n, "sym:name"); + return ( str_ends_with( (char*) Data(symname), "_set") != 0 ); +} + +Template::Template(const String* code) +{ + if(!code) { + Printf(stdout, "Template code was null. Illegal input for template."); + SWIG_exit(EXIT_FAILURE); + } + + m_code = NewString(code); +} + +Template::~Template() { + Delete(m_code); +} + +String* Template::str() { + return m_code; +} + +Template& Template::Replace(const String* pattern, const String* repl) { + ::Replaceall(m_code, pattern, repl); + return *this; +} diff --git a/Source/Modules/javascript_emitter.h b/Source/Modules/javascript_emitter.h new file mode 100644 index 000000000..b73bb55b4 --- /dev/null +++ b/Source/Modules/javascript_emitter.h @@ -0,0 +1,139 @@ +#ifndef JAVASCRIPT_EMITTER_H +#define JAVASCRIPT_EMITTER_H + +#include "swigmod.h" + +/** + * A class that wraps a code snippet used as template for code generation. + */ +class Template { + +public: + Template(const String* code); + + ~Template(); + + String* str(); + + Template& Replace(const String* pattern, const String* repl); + +private: + String* m_code; +}; + +class JSEmitter { + +public: + + enum JSEmitterType { + JavascriptCore, + V8, + QtScript + }; + + JSEmitter(); + + virtual ~JSEmitter(); + + /** + * Opens output files and temporary output DOHs. + */ + virtual int Initialize(Node *n) = 0; + + /** + * 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 SwitchContext(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the beginning of the classHandler. + */ + virtual int EnterClass(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the end of the classHandler. + */ + virtual int ExitClass(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the beginning of the variableHandler. + */ + virtual int EnterVariable(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the end of the variableHandler. + */ + virtual int ExitVariable(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the beginning of the functionHandler. + */ + virtual int EnterFunction(Node *n) { return SWIG_OK; }; + + /** + * Invoked at the end of the functionHandler. + */ + virtual int ExitFunction(Node *n) { return SWIG_OK; }; + + /** + * Invoked by functionWrapper callback after call to Language::functionWrapper. + */ + virtual int EmitWrapperFunction(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. + */ + const String* GetTemplate(const String *name); + +protected: + + virtual int EmitCtor(Node *n) = 0; + + virtual int EmitDtor(Node *n) = 0; + + virtual int EmitFunction(Node *n, bool is_member) = 0; + + virtual int EmitGetter(Node *n, bool is_member) = 0; + + virtual int EmitSetter(Node *n, bool is_member) = 0; + + bool IsSetterMethod(Node *n); + + Node* GetBaseClass(Node *n); + + const String* typemapLookup(Node *n, const_String_or_char_ptr tmap_method, + SwigType *type, int warning, Node *typemap_attributes = 0); + +protected: + + // empty string used at different places in the code + String *empty_string; + + Hash *templates; + + Wrapper* current_wrapper; +}; + +#endif // JAVASCRIPT_EMITTER_H diff --git a/Source/Modules/swigmain.cxx b/Source/Modules/swigmain.cxx index 01ab1b79f..f4c86addc 100644 --- a/Source/Modules/swigmain.cxx +++ b/Source/Modules/swigmain.cxx @@ -53,6 +53,7 @@ extern "C" { Language *swig_r(void); Language *swig_go(void); Language *swig_d(void); + Language *swig_javascript(void); } struct swig_module { @@ -94,6 +95,7 @@ static swig_module modules[] = { {"-tcl8", swig_tcl, 0}, {"-uffi", swig_uffi, "Common Lisp / UFFI"}, {"-xml", swig_xml, "XML"}, + {"-javascript", swig_javascript, "Javascript"}, {NULL, NULL, NULL} };