Merge remote-tracking branch 'upstream/master'
This commit is contained in:
Коммит
8ffd5c52ca
|
@ -28,9 +28,9 @@
|
|||
%\maketitle
|
||||
|
||||
\begin{abstract}
|
||||
We present Emscripten, an LLVM-to-JavaScript compiler. Emscripten compiles
|
||||
LLVM (Low Level Virtual Machine) assembly code into standard JavaScript, which opens up two avenues for running code written
|
||||
in languages other than JavaScript on the web: (1) Compile code directly into LLVM bitcode, and
|
||||
We present Emscripten, a compiler from LLVM (Low Level Virtual Machine) assembly to JavaScript. This
|
||||
opens up two avenues for running code written
|
||||
in languages other than JavaScript on the web: (1) Compile code directly into LLVM assemby, and
|
||||
then compile that into JavaScript using Emscripten, or (2) Compile
|
||||
a language's entire runtime into LLVM and then JavaScript, as in the previous
|
||||
approach, and then use the compiled runtime to run code written in that language. For example, the
|
||||
|
@ -107,8 +107,8 @@ not Python, so for example division of integers can yield unexpected results
|
|||
but in JavaScript and in Pyjamas a floating-point number can be generated).
|
||||
|
||||
In this paper we present another project along those lines: \textbf{Emscripten},
|
||||
which compiles LLVM assembly code into JavaScript. LLVM (the Low Level Virtual
|
||||
Machine\footnote{\url{http://llvm.org/}}) is a compiler project primarily focused on C, C++ and
|
||||
which compiles LLVM (Low Level Virtual Machine\footnote{\url{http://llvm.org/}}) assembly into JavaScript.
|
||||
LLVM is a compiler project primarily focused on C, C++ and
|
||||
Objective-C. It compiles those languages through a \emph{frontend} (the
|
||||
main ones of which are Clang and LLVM-GCC) into the
|
||||
LLVM intermediary representation (which can be machine-readable
|
||||
|
|
|
@ -681,7 +681,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
|
|||
+ makeFunctionCall(item.ident, item.params, item.funcData) + ' '
|
||||
+ '} catch(e) { '
|
||||
+ 'if (ABORT) throw e; __THREW__ = true; '
|
||||
+ (EXCEPTION_DEBUG ? 'print("Exception: " + e + " : " + e.stack + ", currently at: " + (new Error().stack)); ' : '')
|
||||
+ (EXCEPTION_DEBUG ? 'print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '')
|
||||
+ 'return null } })(); if (!__THREW__) { ' + makeBranch(item.toLabel, item.currLabelId)
|
||||
+ ' } else { ' + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
|
||||
return ret;
|
||||
|
|
|
@ -482,7 +482,7 @@ function __shutdownRuntime__() {
|
|||
if (typeof func === 'number') {
|
||||
func = FUNCTION_TABLE[func];
|
||||
}
|
||||
func(atexit.arg);
|
||||
func(atexit.arg || null);
|
||||
}
|
||||
|
||||
// allow browser to GC, set heaps to null?
|
||||
|
|
128
tests/runner.py
128
tests/runner.py
|
@ -2426,8 +2426,9 @@ if 'benchmark' not in sys.argv:
|
|||
'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000',
|
||||
args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47'''])
|
||||
|
||||
### Test cases in separate files
|
||||
|
||||
# Test cases in separate files. Note that these files may contain invalid .ll!
|
||||
# They are only valid enough for us to read for test purposes, not for llvm-as
|
||||
# to process.
|
||||
def test_cases(self):
|
||||
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
||||
if LLVM_OPTS: return self.skip() # Our code is not exactly 'normal' llvm assembly
|
||||
|
@ -2505,6 +2506,7 @@ if 'benchmark' not in sys.argv:
|
|||
### Integration tests
|
||||
|
||||
def test_scriptaclass(self):
|
||||
header_filename = os.path.join(self.get_dir(), 'header.h')
|
||||
header = '''
|
||||
struct ScriptMe {
|
||||
int value;
|
||||
|
@ -2515,8 +2517,9 @@ if 'benchmark' not in sys.argv:
|
|||
void mulVal(int mul);
|
||||
};
|
||||
'''
|
||||
header_filename = os.path.join(self.get_dir(), 'header.h')
|
||||
open(header_filename, 'w').write(header)
|
||||
h = open(header_filename, 'w')
|
||||
h.write(header)
|
||||
h.close()
|
||||
|
||||
src = '''
|
||||
#include "header.h"
|
||||
|
@ -2548,23 +2551,100 @@ if 'benchmark' not in sys.argv:
|
|||
|
||||
# Way 2: use CppHeaderParser
|
||||
|
||||
header = '''
|
||||
#include <stdio.h>
|
||||
|
||||
class Parent {
|
||||
protected:
|
||||
int value;
|
||||
public:
|
||||
Parent(int val);
|
||||
int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before
|
||||
void mulVal(int mul);
|
||||
};
|
||||
|
||||
class Child1 : public Parent {
|
||||
public:
|
||||
Child1() : Parent(7) { printf("Child1:%d\\n", value); };
|
||||
Child1(int val) : Parent(val*2) { value -= 1; printf("Child1:%d\\n", value); };
|
||||
int getValSqr() { return value*value; }
|
||||
int getValSqr(int more) { return value*value*more; }
|
||||
};
|
||||
|
||||
class Child2 : Parent {
|
||||
public:
|
||||
Child2() : Parent(9) { printf("Child2:%d\\n", value); };
|
||||
int getValCube() { return value*value*value; }
|
||||
private:
|
||||
void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this
|
||||
};
|
||||
'''
|
||||
open(header_filename, 'w').write(header)
|
||||
|
||||
basename = os.path.join(self.get_dir(), 'bindingtest')
|
||||
Popen(['python', BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
||||
output = Popen(['python', BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
||||
assert 'Traceback' not in output, 'Failure in binding generation: ' + output
|
||||
|
||||
src = '''
|
||||
#include "header.h"
|
||||
|
||||
ScriptMe::ScriptMe(int val) : value(val) { }
|
||||
int ScriptMe::getVal() { return value; }
|
||||
void ScriptMe::mulVal(int mul) { value *= mul; }
|
||||
Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); }
|
||||
void Parent::mulVal(int mul) { value *= mul; }
|
||||
|
||||
#include "bindingtest.c"
|
||||
'''
|
||||
|
||||
script_src_2 = '''
|
||||
var sme = new ScriptMe(83);
|
||||
var sme = new Parent(42);
|
||||
sme.mulVal(2);
|
||||
print('*' + sme.getVal() + '*');
|
||||
print('*')
|
||||
print(sme.getVal());
|
||||
|
||||
print('c1');
|
||||
|
||||
var c1 = new Child1();
|
||||
print(c1.getVal());
|
||||
c1.mulVal(2);
|
||||
print(c1.getVal());
|
||||
print(c1.getValSqr());
|
||||
print(c1.getValSqr_2(3));
|
||||
|
||||
print('c1 v2');
|
||||
|
||||
c1 = new Child1_2(8);
|
||||
print(c1.getVal());
|
||||
c1.mulVal(2);
|
||||
print(c1.getVal());
|
||||
print(c1.getValSqr());
|
||||
print(c1.getValSqr_2(3));
|
||||
|
||||
print('c2')
|
||||
|
||||
var c2 = new Child2();
|
||||
print(c2.getVal());
|
||||
c2.mulVal(2);
|
||||
print(c2.getVal());
|
||||
print(c2.getValCube());
|
||||
var succeeded;
|
||||
try {
|
||||
succeeded = 0;
|
||||
print(c2.doSomethingSecret()); // should fail since private
|
||||
succeeded = 1;
|
||||
} catch(e) {}
|
||||
print(succeeded);
|
||||
try {
|
||||
succeeded = 0;
|
||||
print(c2.getValSqr()); // function from the other class
|
||||
succeeded = 1;
|
||||
} catch(e) {}
|
||||
print(succeeded);
|
||||
try {
|
||||
succeeded = 0;
|
||||
c2.getValCube(); // sanity
|
||||
succeeded = 1;
|
||||
} catch(e) {}
|
||||
print(succeeded);
|
||||
|
||||
print('*ok*');
|
||||
'''
|
||||
|
||||
|
@ -2575,7 +2655,33 @@ if 'benchmark' not in sys.argv:
|
|||
'// {{MODULE_ADDITIONS}'
|
||||
)
|
||||
open(filename, 'w').write(src)
|
||||
self.do_test(src, '*166*\n*ok*', post_build=post2)
|
||||
self.do_test(src, '''*
|
||||
84
|
||||
c1
|
||||
Parent:7
|
||||
Child1:7
|
||||
7
|
||||
14
|
||||
196
|
||||
588
|
||||
c1 v2
|
||||
Parent:16
|
||||
Child1:15
|
||||
15
|
||||
30
|
||||
900
|
||||
2700
|
||||
c2
|
||||
Parent:9
|
||||
Child2:9
|
||||
9
|
||||
18
|
||||
5832
|
||||
0
|
||||
0
|
||||
1
|
||||
*ok*
|
||||
''', post_build=post2)
|
||||
|
||||
### Tests for tools
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,669 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Author: Jashua R. Cloutier (contact via sourceforge username:senexcanis)
|
||||
#
|
||||
# Copyright (C) 2010, Jashua R. Cloutier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# * Neither the name of Jashua R. Cloutier nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
#
|
||||
# The CppHeaderParser.py script is written in Python 2.4 and released to
|
||||
# the open source community for continuous improvements under the BSD
|
||||
# 2.0 new license, which can be found at:
|
||||
#
|
||||
# http://www.opensource.org/licenses/bsd-license.php
|
||||
#
|
||||
"""Parse C++ header files and generate a data structure
|
||||
representing the class
|
||||
"""
|
||||
|
||||
import ply.lex as lex
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
import inspect
|
||||
|
||||
def lineno():
|
||||
"""Returns the current line number in our program."""
|
||||
return inspect.currentframe().f_back.f_lineno
|
||||
|
||||
|
||||
__version__ = "1.9"
|
||||
version = "1.9"
|
||||
tokens = [
|
||||
'NUMBER',
|
||||
'NAME',
|
||||
'OPEN_PAREN',
|
||||
'CLOSE_PAREN',
|
||||
'OPEN_BRACE',
|
||||
'CLOSE_BRACE',
|
||||
'COLON',
|
||||
'SEMI_COLON',
|
||||
'COMMA',
|
||||
'COMMENT_SINGLELINE',
|
||||
'COMMENT_MULTILINE',
|
||||
'PRECOMP_MACRO',
|
||||
'PRECOMP_MACRO_CONT',
|
||||
'ASTERISK',
|
||||
'AMPERSTAND',
|
||||
'EQUALS',
|
||||
'MINUS',
|
||||
'PLUS',
|
||||
'DIVIDE',
|
||||
'CHAR_LITERAL',
|
||||
'STRING_LITERAL',
|
||||
'OPERATOR_DIVIDE_OVERLOAD',
|
||||
'NEW_LINE',
|
||||
]
|
||||
|
||||
t_ignore = " \t\r[].|!?%@"
|
||||
t_NUMBER = r'[0-9][0-9XxA-Fa-f]*'
|
||||
t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*'
|
||||
t_OPERATOR_DIVIDE_OVERLOAD = r'/='
|
||||
t_OPEN_PAREN = r'\('
|
||||
t_CLOSE_PAREN = r'\)'
|
||||
t_OPEN_BRACE = r'{'
|
||||
t_CLOSE_BRACE = r'}'
|
||||
t_SEMI_COLON = r';'
|
||||
t_COLON = r':'
|
||||
t_COMMA = r','
|
||||
t_PRECOMP_MACRO = r'\#.*'
|
||||
t_PRECOMP_MACRO_CONT = r'.*\\\n'
|
||||
def t_COMMENT_SINGLELINE(t):
|
||||
r'\/\/.*\n'
|
||||
global doxygenCommentCache
|
||||
if t.value.startswith("///") or t.value.startswith("//!"):
|
||||
if doxygenCommentCache:
|
||||
doxygenCommentCache += "\n"
|
||||
if t.value.endswith("\n"):
|
||||
doxygenCommentCache += t.value[:-1]
|
||||
else:
|
||||
doxygenCommentCache += t.value
|
||||
t_ASTERISK = r'\*'
|
||||
t_MINUS = r'\-'
|
||||
t_PLUS = r'\+'
|
||||
t_DIVIDE = r'/[^/]'
|
||||
t_AMPERSTAND = r'&'
|
||||
t_EQUALS = r'='
|
||||
t_CHAR_LITERAL = "'.'"
|
||||
#found at http://wordaligned.org/articles/string-literals-and-regular-expressions
|
||||
#TODO: This does not work with the string "bla \" bla"
|
||||
t_STRING_LITERAL = r'"([^"\\]|\\.)*"'
|
||||
#Found at http://ostermiller.org/findcomment.html
|
||||
def t_COMMENT_MULTILINE(t):
|
||||
r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/'
|
||||
global doxygenCommentCache
|
||||
if t.value.startswith("/**") or t.value.startswith("/*!"):
|
||||
#not sure why, but get double new lines
|
||||
v = t.value.replace("\n\n", "\n")
|
||||
#strip prefixing whitespace
|
||||
v = re.sub("\n[\s]+\*", "\n*", v)
|
||||
doxygenCommentCache += v
|
||||
def t_NEWLINE(t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += len(t.value)
|
||||
|
||||
def t_error(v):
|
||||
print("Lex error: ", v)
|
||||
|
||||
lex.lex()
|
||||
debug = 0
|
||||
|
||||
supportedAccessSpecifier = [
|
||||
'public',
|
||||
'protected',
|
||||
'private'
|
||||
]
|
||||
|
||||
doxygenCommentCache = ""
|
||||
|
||||
def is_namespace(nameStack):
|
||||
"""Determines if a namespace is being specified"""
|
||||
if len(nameStack) == 0:
|
||||
return False
|
||||
if nameStack[0] == "namespace":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_enum_namestack(nameStack):
|
||||
"""Determines if a namestack is an enum namestack"""
|
||||
if len(nameStack) == 0:
|
||||
return False
|
||||
if nameStack[0] == "enum":
|
||||
return True
|
||||
if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum":
|
||||
return True
|
||||
return False
|
||||
|
||||
class CppParseError(Exception): pass
|
||||
|
||||
class CppClass(dict):
|
||||
"""Takes a name stack and turns it into a class
|
||||
|
||||
Contains the following Keys:
|
||||
self['name'] - Name of the class
|
||||
self['doxygen'] - Doxygen comments associated with the class if they exist
|
||||
self['inherits'] - List of Classes that this one inherits where the values
|
||||
are of the form {"access": Anything in supportedAccessSpecifier
|
||||
"class": Name of the class
|
||||
self['methods'] - Dictionary where keys are from supportedAccessSpecifier
|
||||
and values are a lists of CppMethod's
|
||||
self['properties'] - Dictionary where keys are from supportedAccessSpecifier
|
||||
and values are lists of CppVariable's
|
||||
self['enums'] - Dictionary where keys are from supportedAccessSpecifier and
|
||||
values are lists of CppEnum's
|
||||
|
||||
An example of how this could look is as follows:
|
||||
#self =
|
||||
{
|
||||
'name': ""
|
||||
'inherits':[]
|
||||
'methods':
|
||||
{
|
||||
'public':[],
|
||||
'protected':[],
|
||||
'private':[]
|
||||
},
|
||||
'properties':
|
||||
{
|
||||
'public':[],
|
||||
'protected':[],
|
||||
'private':[]
|
||||
},
|
||||
'enums':
|
||||
{
|
||||
'public':[],
|
||||
'protected':[],
|
||||
'private':[]
|
||||
}
|
||||
}
|
||||
"""
|
||||
def __init__(self, nameStack):
|
||||
if (debug): print("Class: ", nameStack)
|
||||
if (len(nameStack) < 2):
|
||||
print("Error detecting class")
|
||||
return
|
||||
global doxygenCommentCache
|
||||
if len(doxygenCommentCache):
|
||||
self["doxygen"] = doxygenCommentCache
|
||||
doxygenCommentCache = ""
|
||||
self["name"] = nameStack[1]
|
||||
inheritList = []
|
||||
if ":" in nameStack:
|
||||
nameStack = nameStack[nameStack.index(":") + 1:]
|
||||
while len(nameStack):
|
||||
tmpStack = []
|
||||
tmpInheritClass = {"access":"private"}
|
||||
if "," in nameStack:
|
||||
tmpStack = nameStack[:nameStack.index(",")]
|
||||
nameStack = nameStack[nameStack.index(",") + 1:]
|
||||
else:
|
||||
tmpStack = nameStack
|
||||
nameStack = []
|
||||
if len(tmpStack) == 0:
|
||||
break;
|
||||
elif len(tmpStack) == 1:
|
||||
tmpInheritClass["class"] = tmpStack[0]
|
||||
elif len(tmpStack) == 2:
|
||||
tmpInheritClass["access"] = tmpStack[0]
|
||||
tmpInheritClass["class"] = tmpStack[1]
|
||||
else:
|
||||
print("Warning: Cant figure out class inheriting %s\n"%(" ".join(tmpStack)))
|
||||
continue
|
||||
inheritList.append(tmpInheritClass)
|
||||
methodAccessSpecificList = {}
|
||||
propertyAccessSpecificList = {}
|
||||
enumAccessSpecificList = {}
|
||||
|
||||
for accessSpecifier in supportedAccessSpecifier:
|
||||
methodAccessSpecificList[accessSpecifier] = []
|
||||
propertyAccessSpecificList[accessSpecifier] = []
|
||||
enumAccessSpecificList[accessSpecifier] = []
|
||||
self['inherits'] = inheritList
|
||||
self['methods'] = methodAccessSpecificList
|
||||
self['properties'] = propertyAccessSpecificList
|
||||
self['enums'] = enumAccessSpecificList
|
||||
self['namespace'] = ""
|
||||
|
||||
def __repr__(self):
|
||||
"""Convert class to a string"""
|
||||
namespace_prefix = ""
|
||||
if self["namespace"]: namespace_prefix = self["namespace"] + "::"
|
||||
rtn = "class %s\n"%(namespace_prefix + self["name"])
|
||||
try:
|
||||
print(self["doxygen"], end=' ')
|
||||
except: pass
|
||||
if "inherits" in list(self.keys()):
|
||||
rtn += "Inherits: "
|
||||
for inheritClass in self["inherits"]:
|
||||
rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"])
|
||||
rtn += "\n"
|
||||
rtn += "{\n"
|
||||
for accessSpecifier in supportedAccessSpecifier:
|
||||
rtn += "%s\n"%(accessSpecifier)
|
||||
#Enums
|
||||
if (len(self["enums"][accessSpecifier])):
|
||||
rtn += " // Enums\n"
|
||||
for enum in self["enums"][accessSpecifier]:
|
||||
rtn += " %s\n"%(repr(enum))
|
||||
#Properties
|
||||
if (len(self["properties"][accessSpecifier])):
|
||||
rtn += " // Properties\n"
|
||||
for property in self["properties"][accessSpecifier]:
|
||||
rtn += " %s\n"%(repr(property))
|
||||
#Methods
|
||||
if (len(self["methods"][accessSpecifier])):
|
||||
rtn += " // Method\n"
|
||||
for method in self["methods"][accessSpecifier]:
|
||||
rtn += " %s\n"%(repr(method))
|
||||
rtn += "}\n"
|
||||
return rtn
|
||||
|
||||
class CppMethod(dict):
|
||||
"""Takes a name stack and turns it into a method
|
||||
|
||||
Contains the following Keys:
|
||||
self['rtnType'] - Return type of the method (ex. "int")
|
||||
self['name'] - Name of the method (ex. "getSize")
|
||||
self['doxygen'] - Doxygen comments associated with the method if they exist
|
||||
self['parameters'] - List of CppVariables
|
||||
"""
|
||||
def __init__(self, nameStack, curClass):
|
||||
if (debug): print("Method: ", nameStack)
|
||||
global doxygenCommentCache
|
||||
if len(doxygenCommentCache):
|
||||
self["doxygen"] = doxygenCommentCache
|
||||
doxygenCommentCache = ""
|
||||
if "operator" in nameStack:
|
||||
self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')])
|
||||
self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')])
|
||||
else:
|
||||
self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1])
|
||||
self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')])
|
||||
if len(self["rtnType"]) == 0 or self["name"] == curClass:
|
||||
self["rtnType"] = "void"
|
||||
paramsStack = nameStack[nameStack.index('(') + 1: ]
|
||||
#Remove things from the stack till we hit the last paren, this helps handle abstract and normal methods
|
||||
while (paramsStack[-1] != ")"):
|
||||
paramsStack.pop()
|
||||
paramsStack.pop()
|
||||
params = []
|
||||
#See if there is a doxygen comment for the variable
|
||||
doxyVarDesc = {}
|
||||
#TODO: Put this into a class
|
||||
if "doxygen" in self:
|
||||
doxyLines = self["doxygen"].split("\n")
|
||||
lastParamDesc = ""
|
||||
for doxyLine in doxyLines:
|
||||
if " @param " in doxyLine or " \param " in doxyLine:
|
||||
try:
|
||||
#Strip out the param
|
||||
doxyLine = doxyLine[doxyLine.find("param ") + 6:]
|
||||
(var, desc) = doxyLine.split(" ", 1)
|
||||
doxyVarDesc[var] = desc.strip()
|
||||
lastParamDesc = var
|
||||
except: pass
|
||||
elif " @return " in doxyLine or " \return " in doxyLine:
|
||||
lastParamDesc = ""
|
||||
# not handled for now
|
||||
elif lastParamDesc:
|
||||
try:
|
||||
doxyLine = doxyLine.strip()
|
||||
if " " not in doxyLine:
|
||||
lastParamDesc = ""
|
||||
continue
|
||||
doxyLine = doxyLine[doxyLine.find(" ") + 1:]
|
||||
doxyVarDesc[lastParamDesc] += " " + doxyLine
|
||||
except: pass
|
||||
|
||||
#Create the variable now
|
||||
while (len(paramsStack)):
|
||||
if (',' in paramsStack):
|
||||
params.append(CppVariable(paramsStack[0:paramsStack.index(',')], doxyVarDesc=doxyVarDesc))
|
||||
paramsStack = paramsStack[paramsStack.index(',') + 1:]
|
||||
else:
|
||||
param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc)
|
||||
if len(list(param.keys())):
|
||||
params.append(param)
|
||||
break
|
||||
self["parameters"] = params
|
||||
|
||||
|
||||
class CppVariable(dict):
|
||||
"""Takes a name stack and turns it into a method
|
||||
|
||||
Contains the following Keys:
|
||||
self['type'] - Type for the variable (ex. "const string &")
|
||||
self['name'] - Name of the variable (ex. "numItems")
|
||||
self['namespace'] - Namespace containing the enum
|
||||
self['desc'] - Description of the variable if part of a method (optional)
|
||||
self['doxygen'] - Doxygen comments associated with the method if they exist
|
||||
self['defaltValue'] - Default value of the variable, this key will only
|
||||
exist if there is a default value
|
||||
"""
|
||||
def __init__(self, nameStack, **kwargs):
|
||||
if (debug): print("Variable: ", nameStack)
|
||||
if (len(nameStack) < 2):
|
||||
return
|
||||
global doxygenCommentCache
|
||||
if len(doxygenCommentCache):
|
||||
self["doxygen"] = doxygenCommentCache
|
||||
doxygenCommentCache = ""
|
||||
if ("=" in nameStack):
|
||||
self["type"] = " ".join(nameStack[:nameStack.index("=") - 1])
|
||||
self["name"] = nameStack[nameStack.index("=") - 1]
|
||||
self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:])
|
||||
else:
|
||||
self["type"] = " ".join(nameStack[:-1])
|
||||
self["name"] = nameStack[-1]
|
||||
self["type"] = self["type"].replace(" :",":")
|
||||
self["type"] = self["type"].replace(": ",":")
|
||||
self["type"] = self["type"].replace(" <","<")
|
||||
self["type"] = self["type"].replace(" >",">")
|
||||
#Optional doxygen description
|
||||
try:
|
||||
self["desc"] = kwargs["doxyVarDesc"][self["name"]]
|
||||
except: pass
|
||||
|
||||
class CppEnum(dict):
|
||||
"""Takes a name stack and turns it into an Enum
|
||||
|
||||
Contains the following Keys:
|
||||
self['name'] - Name of the enum (ex. "ItemState")
|
||||
self['namespace'] - Namespace containing the enum
|
||||
self['values'] - List of values where the values are a dictionary of the
|
||||
form {"name": name of the key (ex. "PARSING_HEADER"),
|
||||
"value": Specified value of the enum, this key will only exist
|
||||
if a value for a given enum value was defined
|
||||
}
|
||||
"""
|
||||
def __init__(self, nameStack):
|
||||
if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack:
|
||||
#Not enough stuff for an enum
|
||||
return
|
||||
global doxygenCommentCache
|
||||
if len(doxygenCommentCache):
|
||||
self["doxygen"] = doxygenCommentCache
|
||||
doxygenCommentCache = ""
|
||||
valueList = []
|
||||
#Figure out what values it has
|
||||
valueStack = nameStack[nameStack.index('{') + 1: nameStack.index('}')]
|
||||
while len(valueStack):
|
||||
tmpStack = []
|
||||
if "," in valueStack:
|
||||
tmpStack = valueStack[:valueStack.index(",")]
|
||||
valueStack = valueStack[valueStack.index(",") + 1:]
|
||||
else:
|
||||
tmpStack = valueStack
|
||||
valueStack = []
|
||||
if len(tmpStack) == 1:
|
||||
valueList.append({"name": tmpStack[0]})
|
||||
elif len(tmpStack) >= 3 and tmpStack[1] == "=":
|
||||
valueList.append({"name": tmpStack[0], "value": " ".join(tmpStack[2:])})
|
||||
elif len(tmpStack) == 2 and tmpStack[1] == "=":
|
||||
if (debug): print("Missed value for %s"%tmpStack[0])
|
||||
valueList.append({"name": tmpStack[0]})
|
||||
if len(valueList):
|
||||
self["values"] = valueList
|
||||
else:
|
||||
#An enum without any values is useless, dont bother existing
|
||||
return
|
||||
#Figure out if it has a name
|
||||
preBraceStack = nameStack[:nameStack.index("{")]
|
||||
postBraceStack = nameStack[nameStack.index("}") + 1:]
|
||||
if (len(preBraceStack) == 2 and "typedef" not in nameStack):
|
||||
self["name"] = preBraceStack[1]
|
||||
elif len(postBraceStack) and "typedef" in nameStack:
|
||||
self["name"] = " ".join(postBraceStack)
|
||||
#See if there are instances of this
|
||||
if "typedef" not in nameStack and len(postBraceStack):
|
||||
self["instances"] = []
|
||||
for var in postBraceStack:
|
||||
if "," in var:
|
||||
continue
|
||||
self["instances"].append(var)
|
||||
self["namespace"] = ""
|
||||
|
||||
class CppHeader:
|
||||
"""Parsed C++ class header
|
||||
|
||||
Variables produced:
|
||||
self.classes - Dictionary of classes found in a given header file where the
|
||||
key is the name of the class
|
||||
"""
|
||||
def __init__(self, headerFileName, argType = "file"):
|
||||
if (argType == "file"):
|
||||
self.headerFileName = os.path.expandvars(headerFileName)
|
||||
self.mainClass = os.path.split(self.headerFileName)[1][:-2]
|
||||
headerFileStr = ""
|
||||
# if headerFileName[-2:] != ".h":
|
||||
# raise Exception("file must be a header file and end with .h")
|
||||
elif argType == "string":
|
||||
self.headerFileName = ""
|
||||
self.mainClass = "???"
|
||||
headerFileStr = headerFileName
|
||||
else:
|
||||
raise Exception("Arg type must be either file or string")
|
||||
self.curClass = ""
|
||||
self.classes = {}
|
||||
self.enums = []
|
||||
self.nameStack = []
|
||||
self.nameSpaces = []
|
||||
self.curAccessSpecifier = 'private'
|
||||
|
||||
if (len(self.headerFileName)):
|
||||
headerFileStr = "\n".join(open(self.headerFileName).readlines())
|
||||
self.braceDepth = 0
|
||||
lex.input(headerFileStr)
|
||||
curLine = 0
|
||||
curChar = 0
|
||||
try:
|
||||
while True:
|
||||
tok = lex.token()
|
||||
# Example: LexToken(COLON,';',1,373)
|
||||
# where (tok.name, tok.value, ?, ?)
|
||||
if not tok:
|
||||
break
|
||||
curLine = tok.lineno
|
||||
curChar = tok.lexpos
|
||||
if (tok.type == 'OPEN_BRACE'):
|
||||
if len(self.nameStack) and is_namespace(self.nameStack):
|
||||
self.nameSpaces.append(self.nameStack[1])
|
||||
if len(self.nameStack) and not is_enum_namestack(self.nameStack):
|
||||
self.evaluate_stack()
|
||||
else:
|
||||
self.nameStack.append(tok.value)
|
||||
self.braceDepth += 1
|
||||
elif (tok.type == 'CLOSE_BRACE'):
|
||||
if self.braceDepth == 0:
|
||||
continue
|
||||
if (self.braceDepth == len(self.nameSpaces)):
|
||||
tmp = self.nameSpaces.pop()
|
||||
if len(self.nameStack) and is_enum_namestack(self.nameStack):
|
||||
self.nameStack.append(tok.value)
|
||||
elif self.braceDepth < 10:
|
||||
self.evaluate_stack()
|
||||
else:
|
||||
self.nameStack = []
|
||||
self.braceDepth -= 1
|
||||
if (self.braceDepth == 0):
|
||||
self.curClass = ""
|
||||
|
||||
if (tok.type == 'OPEN_PAREN'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'CLOSE_PAREN'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'EQUALS'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'COMMA'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'NUMBER'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'MINUS'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'PLUS'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'STRING_LITERAL'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK'):
|
||||
if (tok.value == 'class'):
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.value in supportedAccessSpecifier and self.braceDepth == len(self.nameSpaces) + 1):
|
||||
self.curAccessSpecifier = tok.value
|
||||
else:
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'COLON'):
|
||||
#Dont want colon to be first in stack
|
||||
if len(self.nameStack) == 0:
|
||||
continue
|
||||
self.nameStack.append(tok.value)
|
||||
elif (tok.type == 'SEMI_COLON'):
|
||||
if (self.braceDepth < 10):
|
||||
self.evaluate_stack()
|
||||
except:
|
||||
raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s"
|
||||
% (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)))
|
||||
|
||||
def evaluate_stack(self):
|
||||
"""Evaluates the current name stack"""
|
||||
global doxygenCommentCache
|
||||
if (debug): print("Evaluating stack %s at..."%self.nameStack)
|
||||
if (len(self.curClass)):
|
||||
if (debug): print("%s (%s) "%(self.curClass, self.curAccessSpecifier), end=' ')
|
||||
if (len(self.nameStack) == 0):
|
||||
if (debug): print("line ",lineno())
|
||||
if (debug): print("(Empty Stack)")
|
||||
return
|
||||
elif (self.nameStack[0] == "namespace"):
|
||||
#Taken care of outside of here
|
||||
pass
|
||||
elif (self.nameStack[0] == "class"):
|
||||
if (debug): print("line ",lineno())
|
||||
self.evaluate_class_stack()
|
||||
elif (self.nameStack[0] == "struct"):
|
||||
if (debug): print("line ",lineno())
|
||||
self.curAccessSpecifier = "public"
|
||||
self.evaluate_class_stack()
|
||||
elif (len(self.curClass) == 0):
|
||||
if (debug): print("line ",lineno())
|
||||
if is_enum_namestack(self.nameStack):
|
||||
self.evaluate_enum_stack()
|
||||
self.nameStack = []
|
||||
doxygenCommentCache = ""
|
||||
return
|
||||
elif (self.braceDepth < 1):
|
||||
if (debug): print("line ",lineno())
|
||||
#Ignore global stuff for now
|
||||
if (debug): print("Global stuff: ", self.nameStack)
|
||||
self.nameStack = []
|
||||
doxygenCommentCache = ""
|
||||
return
|
||||
elif (self.braceDepth > len(self.nameSpaces) + 1):
|
||||
if (debug): print("line ",lineno())
|
||||
self.nameStack = []
|
||||
doxygenCommentCache = ""
|
||||
return
|
||||
elif is_enum_namestack(self.nameStack):
|
||||
if (debug): print("line ",lineno())
|
||||
#elif self.nameStack[0] == "enum":
|
||||
self.evaluate_enum_stack()
|
||||
elif ('(' in self.nameStack):
|
||||
if (debug): print("line ",lineno())
|
||||
self.evaluate_method_stack()
|
||||
else:
|
||||
if (debug): print("line ",lineno())
|
||||
self.evaluate_property_stack()
|
||||
self.nameStack = []
|
||||
doxygenCommentCache = ""
|
||||
|
||||
def evaluate_class_stack(self):
|
||||
"""Create a Class out of the name stack (but not its parts)"""
|
||||
#dont support sub classes today
|
||||
if self.braceDepth != len(self.nameSpaces):
|
||||
return
|
||||
newClass = CppClass(self.nameStack)
|
||||
if len(list(newClass.keys())):
|
||||
self.curClass = newClass["name"]
|
||||
self.classes[self.curClass] = newClass
|
||||
else:
|
||||
self.curClass = ""
|
||||
newClass["namespace"] = self.cur_namespace()
|
||||
|
||||
def evaluate_method_stack(self):
|
||||
"""Create a method out of the name stack"""
|
||||
newMethod = CppMethod(self.nameStack, self.curClass)
|
||||
if len(list(newMethod.keys())):
|
||||
self.classes[self.curClass]["methods"][self.curAccessSpecifier].append(newMethod)
|
||||
|
||||
def evaluate_property_stack(self):
|
||||
"""Create a Property out of the name stack"""
|
||||
newVar = CppVariable(self.nameStack)
|
||||
if len(list(newVar.keys())):
|
||||
self.classes[self.curClass]["properties"][self.curAccessSpecifier].append(newVar)
|
||||
|
||||
def evaluate_enum_stack(self):
|
||||
"""Create an Enum out of the name stack"""
|
||||
newEnum = CppEnum(self.nameStack)
|
||||
if len(list(newEnum.keys())):
|
||||
if len(self.curClass):
|
||||
newEnum["namespace"] = self.cur_namespace()
|
||||
self.classes[self.curClass]["enums"][self.curAccessSpecifier].append(newEnum)
|
||||
else:
|
||||
newEnum["namespace"] = self.cur_namespace()
|
||||
# print "Adding global enum"
|
||||
self.enums.append(newEnum)
|
||||
#This enum has instances, turn them into properties
|
||||
if "instances" in newEnum:
|
||||
instanceType = "enum"
|
||||
if "name" in newEnum:
|
||||
instanceType = newEnum["name"]
|
||||
for instance in newEnum["instances"]:
|
||||
self.nameStack = [instanceType, instance]
|
||||
self.evaluate_property_stack()
|
||||
del newEnum["instances"]
|
||||
|
||||
def cur_namespace(self, add_double_colon = False):
|
||||
rtn = ""
|
||||
i = 0
|
||||
while i < len(self.nameSpaces):
|
||||
rtn += self.nameSpaces[i]
|
||||
if add_double_colon or i < len(self.nameSpaces) - 1:
|
||||
rtn += "::"
|
||||
i+=1
|
||||
return rtn
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
rtn = ""
|
||||
for className in list(self.classes.keys()):
|
||||
rtn += repr(self.classes[className])
|
||||
return rtn
|
|
@ -1,9 +1,4 @@
|
|||
# CppHeaderParser package
|
||||
# Author: Jashua Cloutier (contact via sourceforge username:senexcanis)
|
||||
import sys
|
||||
if sys.version_info[0] == 2:
|
||||
from CppHeaderParser import *
|
||||
else:
|
||||
from CppHeaderParser3 import *
|
||||
from CppHeaderParser import *
|
||||
|
||||
#__all__ = ['CppHeaderParser']
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
typedef char* string;
|
||||
|
||||
using namespace std;
|
||||
class SampleClass
|
||||
{
|
||||
|
@ -32,7 +35,7 @@ public:
|
|||
*********************************/
|
||||
unsigned int meth4();
|
||||
private:
|
||||
void * meth5(){return NULL};
|
||||
void * meth5(){return NULL}; // invalid c++, fixing parser anyways.
|
||||
|
||||
/// prop1 description
|
||||
string prop1;
|
||||
|
@ -62,3 +65,83 @@ namespace Alpha
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
// tests by hart //
|
||||
namespace Gamma {
|
||||
typedef std::string mystring;
|
||||
}
|
||||
typedef std::string _StringBase;
|
||||
typedef _StringBase String;
|
||||
|
||||
namespace Theta {
|
||||
class ThetaClass {
|
||||
public:
|
||||
ThetaClass();
|
||||
~ThetaClass();
|
||||
protected:
|
||||
struct mystruct {
|
||||
bool xxx;
|
||||
};
|
||||
void this_method_protected();
|
||||
}
|
||||
|
||||
struct ThetaStruct {
|
||||
bool test1;
|
||||
};
|
||||
|
||||
class ThetaClass2 {
|
||||
public:
|
||||
ThetaClass2();
|
||||
static inline void static_inlined_method();
|
||||
inline friend void myfriendmethod();
|
||||
static std::vector<float> returns_std_vector();
|
||||
float operator + ();
|
||||
template<typename AAA> std::vector<float> template_method( const AAA & aaa );
|
||||
|
||||
}
|
||||
|
||||
inline int ThetaClass::method_defined_outside() {
|
||||
return 1;
|
||||
}
|
||||
inline ThetaClass::operator ThetaClass() const
|
||||
{
|
||||
return ThetaClass();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct FreeStruct {
|
||||
bool freestructs_boolvar;
|
||||
float freestructs_floatvar;
|
||||
};
|
||||
|
||||
namespace A {
|
||||
|
||||
class FixMe {
|
||||
virtual ~__forced_unwind() throw();
|
||||
virtual void purevirt() = 0;
|
||||
int realMethod() {
|
||||
return 1
|
||||
}
|
||||
}; // legal to end class with }; ?
|
||||
|
||||
class SubClassFixMe : public FixMe {
|
||||
public:
|
||||
void purevirt();
|
||||
}
|
||||
|
||||
namespace B {
|
||||
|
||||
}
|
||||
class StillAbstract : public FixMe {
|
||||
public:
|
||||
void somemethod();
|
||||
pointer operator->() const { return &(operator*()); } // example of operator used in method def
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
namespace C __attribute__ ((__visibility__ ("default"))) {
|
||||
}
|
||||
|
||||
|
|
|
@ -48,4 +48,27 @@ print "\nNamespace for OmegaClass is:"
|
|||
print cppHeader.classes["OmegaClass"]["namespace"]
|
||||
|
||||
print "\nType for omegaString is:"
|
||||
print cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"]
|
||||
print cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"]
|
||||
|
||||
# by hart
|
||||
print 'global typedefs:'
|
||||
print cppHeader.typedefs
|
||||
|
||||
print 'order of classes:'
|
||||
for cls in cppHeader.classes_order:
|
||||
print '\t', cls['name']
|
||||
|
||||
print 'order of typedefs:'
|
||||
for name in cppHeader.typedefs_order:
|
||||
print '\t', name
|
||||
|
||||
print cppHeader.classes['ThetaClass']
|
||||
print '_'*80
|
||||
print cppHeader.classes['ThetaClass2']
|
||||
|
||||
print cppHeader.structs
|
||||
|
||||
print cppHeader.namespaces
|
||||
|
||||
print cppHeader.classes['FixMe']
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@ We generate the following:
|
|||
|
||||
* BASENAME.js: JavaScript bindings file, with generated JavaScript wrapper
|
||||
objects. This is a high-level wrapping, using native JS classes.
|
||||
|
||||
The C bindings file is basically a tiny C wrapper around the C++ code.
|
||||
It's only purpose is to make it easy to access the C++ code in the JS
|
||||
bindings, and to prevent DFE from removing the code we care about. The
|
||||
JS bindings do more serious work, creating class structures in JS and
|
||||
linking them to the C bindings.
|
||||
'''
|
||||
|
||||
import os, sys, glob
|
||||
|
@ -50,62 +56,89 @@ for header in sys.argv[2:]:
|
|||
classes[cname] = clazz
|
||||
|
||||
# Second pass - generate bindings
|
||||
# TODO: Bind virtual functions using dynamic binding in the C binding code
|
||||
|
||||
funcs = []
|
||||
funcs = {} # name -> # of copies in the original, and originalname in a copy
|
||||
|
||||
gen_c = open(basename + '.c', 'w')
|
||||
gen_js = open(basename + '.js', 'w')
|
||||
|
||||
gen_c.write('extern "C" {\n')
|
||||
|
||||
for cname, clazz in classes.iteritems():
|
||||
print 'Generating', cname
|
||||
# TODO: Generate all parent class (recursively) data too
|
||||
|
||||
constructor_counter = 0
|
||||
def generate_class(generating_cname, cname, clazz):
|
||||
inherited = generating_cname != cname
|
||||
|
||||
for method in clazz['methods']['public']:
|
||||
#print ' ', method['name'], method
|
||||
print ' ', method['name'], method
|
||||
|
||||
mname = method['name']
|
||||
args = method['parameters']
|
||||
constructor = mname == cname
|
||||
|
||||
if constructor and inherited: continue
|
||||
|
||||
# C
|
||||
|
||||
ret = (cname + ' *') if constructor else method['rtnType']
|
||||
callprefix = 'new ' if constructor else 'self->'
|
||||
typedargs = ', '.join( ([] if constructor else [cname + ' * self']) + map(lambda arg: arg['type'] + ' ' + arg['name'], args) )
|
||||
justargs = ', '.join(map(lambda arg: arg['name'], args))
|
||||
fullname = cname + '__' + mname
|
||||
fullname = 'emscripten_bind_' + generating_cname + '__' + mname
|
||||
generating_cname_suffixed = generating_cname
|
||||
mname_suffixed = mname
|
||||
count = funcs.setdefault(fullname, 0)
|
||||
funcs[fullname] += 1
|
||||
|
||||
# handle overloading
|
||||
if count > 0:
|
||||
suffix = '_' + str(count+1)
|
||||
funcs[fullname + suffix] = fullname # this should never change
|
||||
fullname += suffix
|
||||
mname_suffixed += suffix
|
||||
if constructor:
|
||||
generating_cname_suffixed += suffix
|
||||
|
||||
gen_c.write('''
|
||||
%s emscripten_bind_%s(%s) {
|
||||
%s %s(%s) {
|
||||
return %s%s(%s);
|
||||
}
|
||||
''' % (ret, fullname, typedargs, callprefix, mname, justargs))
|
||||
|
||||
funcs.append('emscripten_bind_' + fullname)
|
||||
|
||||
# JS
|
||||
|
||||
if constructor:
|
||||
gen_js.write('''
|
||||
dupe = type(funcs[fullname]) is str
|
||||
if not dupe:
|
||||
gen_js.write('''
|
||||
function %s(%s) {
|
||||
this.ptr = _emscripten_bind_%s(%s);
|
||||
this.ptr = _%s(%s);
|
||||
}
|
||||
''' % (cname + (str(constructor_counter) if constructor_counter > 0 else ''), justargs, fullname, justargs))
|
||||
constructor_counter += 1
|
||||
else: # TODO: handle case of multiple constructors
|
||||
''' % (generating_cname_suffixed, justargs, fullname, justargs))
|
||||
else:
|
||||
gen_js.write('''
|
||||
function %s(%s) {
|
||||
this.ptr = _%s(%s);
|
||||
}
|
||||
%s.prototype = %s.prototype;
|
||||
''' % (generating_cname_suffixed, justargs, fullname, justargs, generating_cname_suffixed, cname))
|
||||
else:
|
||||
gen_js.write('''
|
||||
%s.prototype.%s = function(%s) {
|
||||
%s_emscripten_bind_%s(this.ptr%s);
|
||||
%s_%s(this.ptr%s);
|
||||
}
|
||||
''' % (cname, mname, justargs, 'return ' if ret != 'void' else '', fullname, (', ' if len(justargs) > 0 else '') + justargs))
|
||||
''' % (generating_cname, mname_suffixed, justargs, 'return ' if ret != 'void' else '', fullname, (', ' if len(justargs) > 0 else '') + justargs))
|
||||
|
||||
for cname, clazz in classes.iteritems():
|
||||
generate_class(cname, cname, clazz)
|
||||
|
||||
# In addition, generate all methods of parent classes. We do not inherit in JS (how would we do multiple inheritance etc.?)
|
||||
for parent in clazz['inherits']:
|
||||
generate_class(cname, parent['class'], classes[parent['class']])
|
||||
|
||||
# Finish up
|
||||
|
||||
funcs = funcs.keys()
|
||||
|
||||
gen_c.write('''
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче