348 lines
12 KiB
Python
348 lines
12 KiB
Python
import sys
|
|
import re
|
|
import os
|
|
import argparse
|
|
|
|
class MethodInfo:
|
|
def __init__(self, name, args):
|
|
self.name = name
|
|
self.args = args
|
|
|
|
class MethodList:
|
|
def __init__(self, ifdef, methods):
|
|
self.ifdef = ifdef
|
|
self.methods = methods
|
|
|
|
def makeDir(dirname):
|
|
if not os.path.exists(dirname):
|
|
os.mkdir(dirname)
|
|
|
|
def getMethods(content):
|
|
methods = []
|
|
# Extract name and argument list of the methods that return JSSmart<CJSValue>
|
|
src_methods = re.findall(r'JSSmart\<CJSValue\>\s+(\w+)\s*\(([^\)]*)\)\s*;', content)
|
|
# Get method names and argument lists
|
|
for src_method in src_methods:
|
|
args = re.findall(r'JSSmart\<CJSValue\>\s+(\w+)', src_method[1])
|
|
|
|
method = MethodInfo(src_method[0], args)
|
|
methods.append(method)
|
|
|
|
return methods
|
|
|
|
def parseHeader(header_file):
|
|
class_name = ''
|
|
method_lists = []
|
|
with open(header_file, 'r') as file:
|
|
content = file.read()
|
|
|
|
# Extract the class name
|
|
match = re.search(r'class\s+(JS_DECL\s+)?(\w+)\s*:\s*public\s+CJSEmbedObject', content)
|
|
if match:
|
|
class_name = match.group(2)
|
|
else:
|
|
print("No class derived from CJSEmbedObject was found")
|
|
sys.exit(1)
|
|
|
|
# Remove all functions which start with "/*[noexport]*/" comment
|
|
content = re.sub(r'/\*\[noexport\]\*/\s*JSSmart\<CJSValue\>\s+\w+\s*\([^\)]*\)\s*;', '', content)
|
|
|
|
# Handle methods inside of ifdef blocks
|
|
ifdef_blocks = re.findall(r'#ifdef\s+([\w\d_]+)\s+(.*?)#endif', content, re.DOTALL)
|
|
for ifdef_block in ifdef_blocks:
|
|
methods = getMethods(ifdef_block[1])
|
|
if len(methods) > 0:
|
|
method_lists.append(MethodList(ifdef_block[0], methods))
|
|
content = content.replace(ifdef_block[1], '')
|
|
|
|
# Add all other methods
|
|
method_lists.append(MethodList(None, getMethods(content)))
|
|
|
|
return class_name, method_lists
|
|
|
|
def generateCommonCode(class_name):
|
|
code = "std::string " + class_name + "::getName() { return \"" + class_name + "\"; }\n"
|
|
code += "\n"
|
|
code += "CJSEmbedObject* " + class_name + "::getCreator()\n"
|
|
code += "{\n"
|
|
code += " return new " + class_name + "();\n"
|
|
code += "}\n"
|
|
return code
|
|
|
|
def generateV8InternalCode(class_name, method_lists, header_file):
|
|
code = "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT CHANGE IT!\n"
|
|
code += "// IF YOU NEED TO UPDATE THIS CODE, JUST RERUN PYTHON SCRIPT WITH \"--internal\" OPTION.\n\n"
|
|
code += "#include \"../" + header_file + "\"\n"
|
|
code += "#include \"../../js_internal/v8/v8_base.h\"\n\n"
|
|
namespace_name = "NS" + header_file[:-2]
|
|
code += "namespace " + namespace_name + "\n"
|
|
code += "{\n"
|
|
code += "#define CURRENTWRAPPER " + class_name + "\n\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += " FUNCTION_WRAPPER_V8_" + str(len(method.args)) + "(_" + method.name + ", " + method.name + ")\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += "\n"
|
|
code += " v8::Handle<v8::ObjectTemplate> CreateTemplate(v8::Isolate* isolate)\n"
|
|
code += " {\n"
|
|
code += " v8::EscapableHandleScope handle_scope(isolate);\n"
|
|
code += " v8::Local<v8::ObjectTemplate> result = v8::ObjectTemplate::New(isolate);\n"
|
|
code += " result->SetInternalFieldCount(1);\n"
|
|
code += "\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += " NSV8Objects::Template_Set(result, \"" + method.name + "\", _" + method.name + ");\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += "\n"
|
|
code += " return handle_scope.Escape(result);\n"
|
|
code += " }\n"
|
|
code += "}\n"
|
|
code += "\n"
|
|
adapter_name = class_name + "Adapter"
|
|
code += "class " + adapter_name + " : public CJSEmbedObjectAdapterV8Template\n"
|
|
code += "{\n"
|
|
code += "public:\n"
|
|
code += " virtual v8::Local<v8::ObjectTemplate> getTemplate(v8::Isolate* isolate) override\n"
|
|
code += " {\n"
|
|
code += " v8::EscapableHandleScope handle_scope(isolate);\n"
|
|
code += " v8::Local<v8::ObjectTemplate> templ = " + namespace_name + "::CreateTemplate(isolate);\n"
|
|
code += " return handle_scope.Escape(templ);\n"
|
|
code += " }\n"
|
|
code += "};\n"
|
|
code += "\n"
|
|
code += "CJSEmbedObjectAdapterBase* " + class_name + "::getAdapter()\n"
|
|
code += "{\n"
|
|
code += " if (m_pAdapter == nullptr)\n"
|
|
code += " m_pAdapter = new " + adapter_name + "();\n"
|
|
code += " return m_pAdapter;\n"
|
|
code += "}\n\n"
|
|
return code
|
|
|
|
def generateJSCInternalCode(class_name, method_lists, header_file):
|
|
code = "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT CHANGE IT!\n"
|
|
code += "// IF YOU NEED TO UPDATE THIS CODE, JUST RERUN PYTHON SCRIPT WITH \"--internal\" OPTION.\n\n"
|
|
code += "#include \"../" + header_file + "\"\n"
|
|
code += "#include \"../../js_internal/jsc/jsc_base.h\"\n\n"
|
|
objc_protocol_name = "IJS" + class_name
|
|
code += "@protocol " + objc_protocol_name + " <JSExport>\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += "-(JSValue*) " + method.name
|
|
for arg in method.args:
|
|
code += " : (JSValue*)" + arg
|
|
code += ";\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += "@end\n\n"
|
|
objc_class_name = "CJS" + class_name
|
|
code += "@interface " + objc_class_name + " : NSObject<" + objc_protocol_name + ", JSEmbedObjectProtocol>\n"
|
|
code += "{\n"
|
|
code += "@public\n"
|
|
code += " " + class_name + "* m_internal;\n"
|
|
code += "}\n"
|
|
code += "@end\n\n"
|
|
code += "@implementation " + objc_class_name + "\n"
|
|
code += "EMBED_OBJECT_WRAPPER_METHODS(" + class_name + ");\n"
|
|
code += "\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += "FUNCTION_WRAPPER_JS_" + str(len(method.args)) + "(" + method.name + ", " + method.name + ")\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += "@end\n"
|
|
code += "\n"
|
|
adapter_name = class_name + "Adapter"
|
|
code += "class " + adapter_name + " : public CJSEmbedObjectAdapterJSC\n"
|
|
code += "{\n"
|
|
code += "public:\n"
|
|
code += " virtual id getExportedObject(CJSEmbedObject* pNative) override\n"
|
|
code += " {\n"
|
|
code += " return [[" + objc_class_name + " alloc] init:(" + class_name + "*)pNative];\n"
|
|
code += " }\n"
|
|
code += "};\n"
|
|
code += "\n"
|
|
code += "CJSEmbedObjectAdapterBase* " + class_name + "::getAdapter()\n"
|
|
code += "{\n"
|
|
code += " if (m_pAdapter == nullptr)\n"
|
|
code += " m_pAdapter = new " + adapter_name + "();\n"
|
|
code += " return m_pAdapter;\n"
|
|
code += "}\n\n"
|
|
return code
|
|
|
|
def generateV8ExternalCode(class_name, method_lists, header_file):
|
|
code = "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT CHANGE IT!\n"
|
|
code += "// IF YOU NEED TO UPDATE THIS CODE, JUST RERUN PYTHON SCRIPT.\n\n"
|
|
code += "#include \"../" + header_file + "\"\n"
|
|
code += "#include \"js_embed.h\"\n\n"
|
|
adapter_name = class_name + "Adapter"
|
|
code += "class " + adapter_name + " : public CJSEmbedObjectAdapterV8\n"
|
|
code += "{\n"
|
|
code += "public:\n"
|
|
code += " virtual std::vector<std::string> getMethodNames() override\n"
|
|
code += " {\n"
|
|
code += " return std::vector<std::string> {\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += " \"" + method.name + "\",\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += " };\n"
|
|
code += " }\n"
|
|
code += "\n"
|
|
code += " virtual void initFunctions(CJSEmbedObject* pNativeObjBase) override\n"
|
|
code += " {\n"
|
|
code += " " + class_name + "* pNativeObj = static_cast<" + class_name + "*>(pNativeObjBase);\n"
|
|
code += " m_functions = std::vector<EmbedFunctionType> {\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += " [pNativeObj](CJSFunctionArguments* args) { return pNativeObj->" + method.name + "("
|
|
for i in range(len(method.args)):
|
|
code += "args->Get(" + str(i) + ")"
|
|
if i != len(method.args) - 1:
|
|
code += ", "
|
|
code += "); },\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += " };\n"
|
|
code += " }\n"
|
|
code += "};\n"
|
|
code += "\n"
|
|
code += "CJSEmbedObjectAdapterBase* " + class_name + "::getAdapter()\n"
|
|
code += "{\n"
|
|
code += " if (m_pAdapter == nullptr)\n"
|
|
code += " m_pAdapter = new " + adapter_name + "();\n"
|
|
code += " return m_pAdapter;\n"
|
|
code += "}\n\n"
|
|
return code
|
|
|
|
def generateJSCExternalCode(class_name, method_lists, header_file):
|
|
code = "// THIS FILE WAS GENERATED AUTOMATICALLY. DO NOT CHANGE IT!\n"
|
|
code += "// IF YOU NEED TO UPDATE THIS CODE, JUST RERUN PYTHON SCRIPT.\n\n"
|
|
code += "#include \"../" + header_file + "\"\n"
|
|
code += "#import \"js_embed.h\"\n\n"
|
|
objc_protocol_name = "IJS" + class_name
|
|
code += "@protocol " + objc_protocol_name + " <JSExport>\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += "-(JSValue*) " + method.name
|
|
for arg in method.args:
|
|
code += " : (JSValue*)" + arg
|
|
code += ";\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n"
|
|
code += "@end\n"
|
|
code += "\n"
|
|
objc_class_name = "CJS" + class_name
|
|
code += "@interface " + objc_class_name + " : NSObject<" + objc_protocol_name + ", JSEmbedObjectProtocol>\n"
|
|
code += "{\n"
|
|
code += "@public\n"
|
|
code += " " + class_name + "* m_internal;\n"
|
|
code += "}\n"
|
|
code += "@end\n"
|
|
code += "\n"
|
|
code += "@implementation " + objc_class_name + "\n"
|
|
code += "EMBED_OBJECT_WRAPPER_METHODS(" + class_name + ");\n"
|
|
code += "\n"
|
|
for method_list in method_lists:
|
|
if method_list.ifdef:
|
|
code += "#ifdef " + method_list.ifdef + "\n"
|
|
for method in method_list.methods:
|
|
code += "-(JSValue*) " + method.name
|
|
for arg in method.args:
|
|
code += " : (JSValue*)" + arg
|
|
code += "\n{\n"
|
|
code += " JSSmart<CJSValue> ret = m_internal->" + method.name + "("
|
|
for arg in method.args:
|
|
code += "CJSEmbedObjectAdapterJSC::Native2Value(" + arg + ")"
|
|
if arg != method.args[-1]:
|
|
code += ", "
|
|
code += ");\n"
|
|
code += " return CJSEmbedObjectAdapterJSC::Value2Native(ret);\n"
|
|
code += "}\n"
|
|
if method_list.ifdef:
|
|
code += "#endif\n\n"
|
|
code += "@end\n\n"
|
|
adapter_name = class_name + "Adapter"
|
|
code += "class " + adapter_name + " : public CJSEmbedObjectAdapterJSC\n"
|
|
code += "{\n"
|
|
code += "public:\n"
|
|
code += " virtual id getExportedObject(CJSEmbedObject* pNative) override\n"
|
|
code += " {\n"
|
|
code += " return [[" + objc_class_name + " alloc] init:(" + class_name + "*)pNative];\n"
|
|
code += " }\n"
|
|
code += "};\n"
|
|
code += "\n"
|
|
code += "CJSEmbedObjectAdapterBase* " + class_name + "::getAdapter()\n"
|
|
code += "{\n"
|
|
code += " if (m_pAdapter == nullptr)\n"
|
|
code += " m_pAdapter = new " + adapter_name + "();\n"
|
|
code += " return m_pAdapter;\n"
|
|
code += "}\n\n"
|
|
return code
|
|
|
|
def writeToFile(file_name, content):
|
|
with open(file_name, 'w') as file:
|
|
file.write(content)
|
|
print("\t" + file_name)
|
|
|
|
# MAIN
|
|
parser = argparse.ArgumentParser(description='Generate files for embedding your class into JS')
|
|
parser.add_argument('filename', help='the path to .h file with class you want to embed');
|
|
parser.add_argument('-i', '--internal', action='store_true', help='for internal library usage')
|
|
|
|
# if filename wasn't specified the programm will stop here
|
|
args = parser.parse_args()
|
|
|
|
header_file = args.filename;
|
|
if header_file[-2:] != ".h":
|
|
print("Argument must be a header file with \".h\" extension!")
|
|
sys.exit(1);
|
|
|
|
header_dir = os.path.dirname(header_file)
|
|
if len(header_dir) == 0:
|
|
header_dir = "."
|
|
|
|
header_base_name = os.path.basename(header_file)
|
|
header_base_name_no_extension = header_base_name[:-2]
|
|
class_name, method_lists = parseHeader(header_file)
|
|
|
|
if not class_name:
|
|
print("Proper class was not found in specified header file.")
|
|
sys.exit(1)
|
|
|
|
v8_dir = header_dir + "/v8"
|
|
jsc_dir = header_dir + "/jsc"
|
|
makeDir(v8_dir)
|
|
makeDir(jsc_dir)
|
|
|
|
code_common = generateCommonCode(class_name)
|
|
code_v8 = ""
|
|
code_jsc = ""
|
|
|
|
if args.internal:
|
|
code_v8 = generateV8InternalCode(class_name, method_lists, header_base_name)
|
|
code_jsc = generateJSCInternalCode(class_name, method_lists, header_base_name)
|
|
else:
|
|
code_v8 = generateV8ExternalCode(class_name, method_lists, header_base_name)
|
|
code_jsc = generateJSCExternalCode(class_name, method_lists, header_base_name)
|
|
|
|
print("Generated code was written to:")
|
|
writeToFile(v8_dir + "/v8_" + header_base_name_no_extension + ".cpp", code_v8 + code_common)
|
|
writeToFile(jsc_dir + "/jsc_" + header_base_name_no_extension + ".mm", code_jsc + code_common)
|