[mapguide-commits] r7635 - sandbox/jng/node/Oem/SWIGEx/Source/Modules

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Thu Jun 20 08:00:57 PDT 2013


Author: jng
Date: 2013-06-20 08:00:57 -0700 (Thu, 20 Jun 2013)
New Revision: 7635

Modified:
   sandbox/jng/node/Oem/SWIGEx/Source/Modules/nodejs.cxx
Log:
This submission includes:
 * Codegen for "wrap" constructors, which are constructors that allow us to attach native pointers to. This is needed to support APIs that return JS proxies for our MapGuide C++ classes
 * Codegen for proper polymorphism support, for any API that returns a Mg object, we use the class id mechanism to resolve the appropriate "wrap" constructor to invoke. Otherwise APIs like MgSiteConnection.CreateService will return a JS object with the MgService prototype (and not the prototype of the most derived class).
 * Constructor codegen to guard against calls without the JS new operator


Modified: sandbox/jng/node/Oem/SWIGEx/Source/Modules/nodejs.cxx
===================================================================
--- sandbox/jng/node/Oem/SWIGEx/Source/Modules/nodejs.cxx	2013-06-20 05:04:57 UTC (rev 7634)
+++ sandbox/jng/node/Oem/SWIGEx/Source/Modules/nodejs.cxx	2013-06-20 15:00:57 UTC (rev 7635)
@@ -4,13 +4,15 @@
 #include <sstream>
 
 #define EXPORTS_VAR "exports"
-#define CLASS_EXPORT_VAR "tpl"
+#define CLASS_EXPORT_VAR "constructor_tpl"
 #define CLASS_PROXY_CTOR_NAME "New"
 #define BASE_CLASS_FUNC_TEMPLATE_VAR "baseCls"
 #define WRAPPED_PTR_VAR "wrappedPtr"
 #define FUNC_RET_VAR "retVal"
 #define ARGS_VAR "args"
 
+extern std::map<std::string, int> clsIds;
+
 class Indenter
 {
 private:
@@ -237,6 +239,7 @@
         writeToFile("#include <node.h>\n\n\n");
 
         writeToFile("//TODO: Should learn how to process SWIG %module directive instead of hard-coding headers\n");
+        writeToFile("#include <map>\n");
         writeToFile("#include \"MapGuideCommon.h\"\n");
         writeToFile("#include \"WebApp.h\"\n");
         writeToFile("#include \"WebSupport.h\"\n");
@@ -250,6 +253,18 @@
         Language::top(n);
 
         writeToFile("//=====================================\n");
+        writeToFile("//Polymorphism support\n");
+        writeToFile("//=====================================\n");
+        writeToFile("class JsProxyFactory\n");
+        writeToFile("{\n");
+        writeToFile("public:\n");
+        {
+            Indenter ind(m_indentationLevels);
+            writeToFile("static v8::Handle<v8::Value> CreateJsProxy(MgObject* obj);\n");
+        }
+        writeToFile("};\n\n");
+
+        writeToFile("//=====================================\n");
         writeToFile("//Proxy class definitions\n");
         writeToFile("//=====================================\n");
         for (StringMap::iterator it = m_classDecls.begin(); it != m_classDecls.end(); it++)
@@ -259,7 +274,41 @@
             writeToFile("\n");
         }
 
+        // Add main polymorphism support code
         writeToFile("//=====================================\n");
+        writeToFile("//Polymorphism support implementation\n");
+        writeToFile("//=====================================\n");
+        writeToFile("v8::Handle<v8::Value> JsProxyFactory::CreateJsProxy(MgObject* obj)\n");
+        writeToFile("{\n");
+        {
+            Indenter ind(m_indentationLevels);
+            writeToFile("CHECKARGUMENTNULL(obj, L\"JsProxyFactory::CreateJsProxy\");\n");
+
+            writeToFile("INT32 clsId = obj->GetClassId();\n");
+            writeToFile("switch(clsId)\n");
+            writeToFile("{\n");
+            {
+                Indenter ind2(m_indentationLevels);
+                for(std::map<std::string, int>::const_iterator it = clsIds.begin(); it != clsIds.end(); it++)
+                {
+                    std::string clsName = it->first;
+                    //Skip exception classes
+                    if (clsName.find("Exception") != std::string::npos)
+                        continue;
+
+                    int clsId = it->second;
+
+                    std::string proxyClsName;
+                    setProxyClassName(clsName.c_str(), proxyClsName);
+                    writeToFile(NewStringf("case %d: return %s::CreateJsProxy(obj);\n", clsId, proxyClsName.c_str()));
+                }
+                writeToFile("default: return v8::ThrowException(v8::Exception::Error(v8::String::New(\"Could not find JavaScript proxy factory for unmanaged pointer\")));\n");
+            }
+            writeToFile("}\n");
+        }
+        writeToFile("}\n");
+
+        writeToFile("//=====================================\n");
         writeToFile("//v8 persistent Proxy class constructors\n");
         writeToFile("//=====================================\n");
         for (StringMap::iterator it = m_classDecls.begin(); it != m_classDecls.end(); it++)
@@ -304,6 +353,27 @@
                         mbMethodName += "::";
                         mbMethodName += CLASS_PROXY_CTOR_NAME;
                         
+                        codeWriter.writeToFragment(NewStringf("if (!%s.IsConstructCall())\n", ARGS_VAR));
+                        codeWriter.writeToFragment("{\n");
+                        {
+                            Indenter ind2(m_indentationLevels);
+                            codeWriter.writeToFragment(NewStringf("return v8::ThrowException(v8::Exception::TypeError(v8::String::New(\"Use the new operator to create instances of %s\")));\n", clsName.c_str()));
+                        }
+                        codeWriter.writeToFragment("}\n");
+
+                        //wrap constructor
+                        codeWriter.writeToFragment("//Check if this is a C++ class wrap constructor call\n");
+                        codeWriter.writeToFragment(NewStringf("if (%s.Length() == 1 && %s[0]->IsExternal())\n", ARGS_VAR, ARGS_VAR));
+                        codeWriter.writeToFragment("{\n");
+                        {
+                            Indenter ind2(m_indentationLevels);
+                            codeWriter.writeToFragment(NewStringf("%s* obj = (%s*)v8::External::Unwrap(%s[0]);\n", proxyClsName.c_str(), proxyClsName.c_str(), ARGS_VAR));
+                            codeWriter.writeToFragment("//Wrap to \"this\"\n");
+                            codeWriter.writeToFragment(NewStringf("obj->Wrap(%s.This());\n", ARGS_VAR));
+                            codeWriter.writeToFragment(NewStringf("return %s.This();\n", ARGS_VAR));
+                        }
+                        codeWriter.writeToFragment("}\n");
+
                         codeWriter.writeToFragment(NewStringf("Ptr<%s> %s;\n", clsName.c_str(), WRAPPED_PTR_VAR));
                         ExceptionCodeWriter writer(codeWriter, mbMethodName);
                         codeWriter.writeToFragment(m_ctorSetupCode[clsName]);
@@ -315,7 +385,20 @@
             }
             else
             {
-                writeToFile(NewStringf("\treturn v8::ThrowException(v8::Exception::Error(v8::String::New(\"Class %s has no public constructors\")));\n", clsName.c_str()));
+                Indenter ind(m_indentationLevels);
+                //wrap constructor
+                writeToFile("//Check if this is a C++ class wrap constructor call\n");
+                writeToFile(NewStringf("if (%s.Length() == 1 && %s[0]->IsExternal())\n", ARGS_VAR, ARGS_VAR));
+                writeToFile("{\n");
+                {
+                    Indenter ind2(m_indentationLevels);
+                    writeToFile(NewStringf("%s* obj = (%s*)v8::External::Unwrap(%s[0]);\n", proxyClsName.c_str(), proxyClsName.c_str(), ARGS_VAR));
+                    writeToFile("//Wrap to \"this\"\n");
+                    writeToFile(NewStringf("obj->Wrap(%s.This());\n", ARGS_VAR));
+                    writeToFile(NewStringf("return %s.This();\n", ARGS_VAR));
+                }
+                writeToFile("}\n");
+                writeToFile(NewStringf("return v8::ThrowException(v8::Exception::Error(v8::String::New(\"Class %s has no public constructors\")));\n", clsName.c_str()));
             }
             writeToFile("}\n\n");
         }
@@ -581,10 +664,12 @@
             m_currentExportFragment += EXPORTS_VAR;
             m_currentExportFragment += ");";
 
+            /*
             m_currentInheritFragment += proxyClsName;
             m_currentInheritFragment += "::ApplyInheritanceChain(";
             m_currentInheritFragment += EXPORTS_VAR;
             m_currentInheritFragment += ");";
+            */
 
             //Unmanaged pointer member
             cls.writeToFragment(clsHead);
@@ -638,10 +723,10 @@
             //Stubs to be implemented further down
             {
                 Indenter ind(m_indentationLevels);
-                cls.writeToFragment("static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);\n");
-                cls.writeToFragment(NewStringf("static v8::Handle<v8::Value> CreateJsProxy(%s* ptr);\n", clsName.c_str())); 
+                //cls.writeToFragment("static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);\n");
+                cls.writeToFragment("static v8::Handle<v8::Value> CreateJsProxy(MgObject* ptr);\n"); 
                 cls.writeToFragment("static void Export(v8::Handle<v8::Object> exports);\n");
-                cls.writeToFragment("static void ApplyInheritanceChain(v8::Handle<v8::Object> exports);\n");
+                //cls.writeToFragment("static void ApplyInheritanceChain(v8::Handle<v8::Object> exports);\n");
             }
 
             //Class body
@@ -681,6 +766,7 @@
                 cls.writeToFragment(NewStringf("static v8::Persistent<v8::Function> constructor;\n"));
             }
 
+            /*
             //node/v8 public factory
             {
                 Indenter ind(m_indentationLevels);
@@ -703,18 +789,25 @@
                 }
                 clsSetup.writeToFragment("}\n");
             }
+            */
 
             //node/v8 wrap mg pointer factory method
             {
                 Indenter ind(m_indentationLevels);
-                clsSetup.writeToFragment(NewStringf("v8::Handle<v8::Value> %s::CreateJsProxy(%s* ptr)\n", proxyClsName.c_str(), m_currentClassName));
+                clsSetup.writeToFragment(NewStringf("v8::Handle<v8::Value> %s::CreateJsProxy(MgObject* ptr)\n", proxyClsName.c_str()));
                 clsSetup.writeToFragment("{\n");
                 {
                     Indenter ind2(m_indentationLevels);
                     clsSetup.writeToFragment("v8::HandleScope scope;\n");
-                    clsSetup.writeToFragment("v8::Local<v8::Object> instance = constructor->NewInstance();\n");
-                    clsSetup.writeToFragment(NewStringf("%s* proxyVal = new %s(ptr);\n", proxyClsName.c_str(), proxyClsName.c_str()));
-                    clsSetup.writeToFragment("proxyVal->Wrap(instance);\n");
+                    //clsSetup.writeToFragment("v8::Handle<v8::Value> argv[1] = { v8::Undefined() };\n");
+                    //clsSetup.writeToFragment("v8::Local<v8::Object> instance = constructor->NewInstance();\n");
+                    clsSetup.writeToFragment(NewStringf("%s* proxyVal = new %s((%s*)ptr);\n", proxyClsName.c_str(), proxyClsName.c_str(), m_currentClassName));
+                    clsSetup.writeToFragment("//Invoke the \"wrap\" constructor\n");
+                    clsSetup.writeToFragment("v8::Handle<v8::Value> argv[1] = { v8::External::New(proxyVal) };\n");
+                    clsSetup.writeToFragment("v8::Local<v8::Object> instance = constructor->NewInstance(1, argv);\n");
+                    //clsSetup.writeToFragment("//Bug? Or are we doing something wrong? Internal field count is 0 and blows up the next line\n");
+                    //clsSetup.writeToFragment("proxyVal->Wrap(instance);\n");
+                    //clsSetup.writeToFragment("instance->SetInternalField(0, v8::External::New(proxyVal));\n");
                     clsSetup.writeToFragment("return scope.Close(instance);\n");
                 }
                 clsSetup.writeToFragment("}\n");
@@ -727,11 +820,22 @@
                 clsSetup.writeToFragment("{\n");
                 {
                     Indenter ind2(m_indentationLevels);
-                    clsSetup.writeToFragment(NewStringf("v8::Local<v8::FunctionTemplate> %s = v8::FunctionTemplate::New(%s::%s);\n", CLASS_EXPORT_VAR, proxyClsName.c_str(), CLASS_PROXY_CTOR_NAME));
-                    clsSetup.writeToFragment(NewStringf("constructor_tpl = v8::Persistent<v8::FunctionTemplate>::New(%s);\n", CLASS_EXPORT_VAR));
-                    clsSetup.writeToFragment(NewStringf("%s->SetClassName(v8::String::NewSymbol(\"%s\"));\n", CLASS_EXPORT_VAR, m_currentClassName));
+                    clsSetup.writeToFragment(NewStringf("v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(%s::%s);\n", proxyClsName.c_str(), CLASS_PROXY_CTOR_NAME));
+                    clsSetup.writeToFragment(NewStringf("%s = v8::Persistent<v8::FunctionTemplate>::New(tpl);\n", CLASS_EXPORT_VAR));
+
+                    //This is ok because export calls are ordered with base classes first, so we're assured to be
+                    //inheriting from an initialized base class function template
+                    if (Strcmp("", baseClassName) != 0)
+                    {
+                        std::string proxyBaseClsName;
+                        setProxyClassName(baseClassName, proxyBaseClsName);
+                        clsSetup.writeToFragment("//Inherit from base class function template\n");
+                        clsSetup.writeToFragment(NewStringf("constructor_tpl->Inherit(%s::constructor_tpl);\n", proxyBaseClsName.c_str()));
+                    }
+
                     clsSetup.writeToFragment("//For the internal pointer\n");
                     clsSetup.writeToFragment(NewStringf("%s->InstanceTemplate()->SetInternalFieldCount(1);\n", CLASS_EXPORT_VAR));
+                    clsSetup.writeToFragment(NewStringf("%s->SetClassName(v8::String::NewSymbol(\"%s\"));\n", CLASS_EXPORT_VAR, m_currentClassName));
 
                     std::string ctorReg;
                     ctorReg += proxyClsName;
@@ -759,6 +863,7 @@
                 clsSetup.writeToFragment("}\n");
             }
 
+            /*
             //node/v8 export function
             {
                 Indenter ind(m_indentationLevels);
@@ -780,6 +885,7 @@
                 }
                 clsSetup.writeToFragment("}\n");
             }
+            */
             cls.writeToFragment("};");
 
             Delete(m_currentClassName);
@@ -787,7 +893,7 @@
 
             m_classDecls[clsName] = m_currentFragment;
             m_classExportCalls.push_back(m_currentExportFragment);
-            m_classInheritCalls.push_back(m_currentInheritFragment);
+            //m_classInheritCalls.push_back(m_currentInheritFragment);
             m_classSetupCalls.push_back(m_currentClassSetupFragment);
 
         }
@@ -914,7 +1020,7 @@
                 if (isMgPointer) //Stub
                 {
                     meth.writeToFragment("v8::HandleScope scope;\n");
-                    meth.writeToFragment(NewStringf("v8::Handle<v8::Value> instance = Proxy%s::CreateJsProxy(%s);\n", retTypeUnprefixed, FUNC_RET_VAR));
+                    meth.writeToFragment(NewStringf("v8::Handle<v8::Value> instance = JsProxyFactory::CreateJsProxy(%s);\n", FUNC_RET_VAR));
                     meth.writeToFragment("return scope.Close(instance);\n");
                 }
                 else



More information about the mapguide-commits mailing list