| #!/usr/bin/python |
| # Copyright (C) 2014 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. 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. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. |
| |
| import argparse |
| import filecmp |
| import fnmatch |
| import os |
| import re |
| import shutil |
| import sys |
| import datetime |
| import json |
| |
| parser = argparse.ArgumentParser() |
| parser.add_argument('input_file', nargs='*', help='Input JS files which builtins generated from') |
| parser.add_argument('--input-directory', help='All JS files will be used as input from this directory.') |
| parser.add_argument('--output', help='path to output cpp or h file') |
| parser.add_argument('--prefix', default='JSC', help='prefix used for public macros') |
| parser.add_argument('--namespace', default='JSC', help='C++ namespace') |
| args = parser.parse_args() |
| |
| copyrightText = """ * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. |
| */\n |
| """ |
| |
| generatorString = "/* Generated by %s do not hand edit. */\n" % os.path.basename(__file__) |
| |
| functionHeadRegExp = re.compile(r"(?:function|constructor)\s+\w+\s*\(.*?\)", re.MULTILINE | re.S) |
| functionNameRegExp = re.compile(r"(?:function|constructor)\s+(\w+)\s*\(", re.MULTILINE | re.S) |
| functionIsConstructorRegExp = re.compile(r"^constructor", re.MULTILINE | re.S) |
| functionParameterFinder = re.compile(r"^(?:function|constructor)\s+(?:\w+)\s*\(((?:\s*\w+)?\s*(?:\s*,\s*\w+)*)?\s*\)", re.MULTILINE | re.S) |
| |
| multilineCommentRegExp = re.compile(r"\/\*.*?\*\/", re.MULTILINE | re.S) |
| singleLineCommentRegExp = re.compile(r"\/\/.*?\n", re.MULTILINE | re.S) |
| |
| def getCopyright(source): |
| copyrightBlock = multilineCommentRegExp.findall(source)[0] |
| copyrightBlock = copyrightBlock[:copyrightBlock.index("Redistribution")] |
| copyRightLines = [] |
| |
| for line in copyrightBlock.split("\n"): |
| line = line.replace("/*", "") |
| line = line.replace("*/", "") |
| line = line.replace("*", "") |
| line = line.replace("Copyright", "") |
| line = line.replace("copyright", "") |
| line = line.replace("(C)", "") |
| line = line.replace("(c)", "") |
| line = line.strip() |
| if len(line) == 0: |
| continue |
| |
| copyRightLines.append(line) |
| |
| return list(set(copyRightLines)) |
| |
| class Function(object): |
| def __init__(self, name, source, isConstructor, parameters): |
| self.name = name |
| self.source = source |
| self.isConstructor = isConstructor |
| self.parameters = parameters |
| |
| def mangleName(self, object): |
| qName = object + "." + self.name |
| mangledName = "" |
| i = 0 |
| while i < len(qName): |
| if qName[i] == '.': |
| mangledName = mangledName + qName[i + 1].upper() |
| i = i + 1 |
| else: |
| mangledName = mangledName + qName[i] |
| i = i + 1 |
| if self.isConstructor: |
| mangledName = mangledName + "Constructor" |
| return mangledName |
| |
| def getFunctions(source): |
| |
| source = multilineCommentRegExp.sub("/**/", singleLineCommentRegExp.sub("//\n", source)) |
| |
| matches = [ f for f in functionHeadRegExp.finditer(source)] |
| functionBounds = [] |
| start = 0 |
| end = 0 |
| for match in matches: |
| start = match.start() |
| if start < end: |
| continue |
| end = match.end() |
| while source[end] != '{': |
| end = end + 1 |
| depth = 1 |
| end = end + 1 |
| while depth > 0: |
| if source[end] == '{': |
| depth = depth + 1 |
| elif source[end] == '}': |
| depth = depth - 1 |
| end = end + 1 |
| functionBounds.append((start, end)) |
| |
| functions = [source[start:end].strip() for (start, end) in functionBounds] |
| result = [] |
| for function in functions: |
| function = multilineCommentRegExp.sub("", function) |
| functionName = functionNameRegExp.findall(function)[0] |
| functionIsConstructor = functionIsConstructorRegExp.match(function) != None |
| functionParameters = functionParameterFinder.findall(function)[0].split(',') |
| if len(functionParameters[0]) == 0: |
| functionParameters = [] |
| |
| result.append(Function(functionName, function, functionIsConstructor, functionParameters)) |
| return result |
| |
| def writeIncludeDirectives(writer, headerNames): |
| for headerName in headerNames: |
| writer.write("#include " + headerName + "\n") |
| |
| def generateCode(source): |
| inputFile = open(source, "r") |
| baseName = os.path.basename(source).replace(".js", "") |
| |
| source = "" |
| for line in inputFile: |
| source = source + line |
| |
| if sys.platform == "cygwin": |
| source = source.replace("\r\n", "\n") |
| return (baseName, getFunctions(source), getCopyright(source)) |
| |
| builtins = [] |
| copyrights = [] |
| (output_base, _) = os.path.splitext(args.output) |
| |
| if args.input_directory: |
| for file in os.listdir(args.input_directory): |
| args.input_file.append(os.path.join(args.input_directory, file)) |
| |
| for file in args.input_file: |
| if fnmatch.fnmatch(file, '*.js'): |
| (baseName, functions, objectCopyrights) = generateCode(file) |
| copyrights.extend(objectCopyrights) |
| builtins.append((baseName, functions)) |
| namespace = args.namespace |
| macroPrefix = args.prefix |
| scopeName = os.path.splitext(os.path.basename(args.output))[0] |
| includeGuard = scopeName + "_H" |
| headerName = scopeName + ".h" |
| |
| headerIncludes = ["\"BuiltinUtils.h\""] if namespace == "JSC" else ["<builtins/BuiltinUtils.h>"] |
| contentIncludes = ["\"BuiltinExecutables.h\"", "\"Executable.h\"", "\"JSCellInlines.h\"", "\"VM.h\""] if namespace == "JSC" else ["<runtime/Executable.h>", "<runtime/StructureInlines.h>", "<runtime/JSCJSValueInlines.h>", "<runtime/JSCellInlines.h>", "<runtime/VM.h>", "\"WebCoreJSClientData.h\""] |
| |
| copyrights = list(set(copyrights)) |
| |
| copyrightBody = "" |
| for copyright in copyrights: |
| copyrightBody = copyrightBody +" * Copyright (C) " + copyright + "\n" |
| |
| builtinsHeader = open(output_base + ".h.tmp", "w") |
| builtinsImplementation = open(output_base + ".cpp.tmp", "w") |
| copyrightText = "/*\n" + copyrightBody + copyrightText |
| builtinsHeader.write("""%s |
| %s |
| |
| #ifndef %s |
| #define %s |
| |
| """ % (generatorString, copyrightText, includeGuard, includeGuard)) |
| |
| writeIncludeDirectives(builtinsHeader, headerIncludes) |
| |
| builtinsHeader.write(""" |
| namespace JSC { |
| class FunctionExecutable; |
| } |
| |
| namespace %s { |
| |
| """ % namespace) |
| |
| codeReferences = [] |
| |
| for (objectName, functions) in builtins: |
| print("Generating bindings for the %s builtin." % objectName) |
| builtinsHeader.write("/* %s functions */\n" % objectName) |
| for function in functions: |
| name = function.name |
| mangledName = function.mangleName(objectName) |
| mangledName = mangledName[0].lower() + mangledName[1:] + "Code" |
| codeReferences.append((mangledName, function)) |
| builtinsHeader.write("extern const char* s_%s;\n" % mangledName) |
| builtinsHeader.write("extern const int s_%sLength;\n" % mangledName) |
| builtinsHeader.write("extern const JSC::ConstructAbility s_%sConstructAbility;\n" % mangledName) |
| builtinsHeader.write("\n") |
| builtinsHeader.write("#define %s_FOREACH_%s_BUILTIN(macro) \\\n" % (macroPrefix, objectName.replace(".", "_").upper())) |
| for function in functions: |
| mangledName = function.mangleName(objectName) |
| builtinsHeader.write(" macro(%s, %s, %d) \\\n" % (function.name, mangledName, len(function.parameters))) |
| builtinsHeader.write("\n") |
| for function in functions: |
| builtinsHeader.write("#define %s_BUILTIN_%s 1\n" % (macroPrefix, function.mangleName(objectName).upper())) |
| builtinsHeader.write("\n\n") |
| names = [] |
| builtinsHeader.write("#define %s_FOREACH_BUILTIN(macro)\\\n" % macroPrefix) |
| for (codeReference, function) in codeReferences: |
| builtinsHeader.write(" macro(%s, %s, s_%sLength) \\\n" % (codeReference, function.name, codeReference)) |
| names.append(function.name) |
| |
| builtinsHeader.write("\n\n") |
| builtinsHeader.write("#define %s_FOREACH_BUILTIN_FUNCTION_NAME(macro) \\\n" % macroPrefix) |
| for name in sorted(set(names)): |
| builtinsHeader.write(" macro(%s) \\\n" % name) |
| builtinsHeader.write(""" |
| |
| #define DECLARE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ |
| JSC::FunctionExecutable* codeName##Generator(JSC::VM&); |
| |
| %s_FOREACH_BUILTIN(DECLARE_BUILTIN_GENERATOR) |
| #undef DECLARE_BUILTIN_GENERATOR |
| |
| #define %s_BUILTIN_EXISTS(name) defined %s_BUILTIN_ ## name |
| |
| } |
| |
| #endif // %s |
| |
| """ % (macroPrefix, macroPrefix, macroPrefix, includeGuard)) |
| |
| builtinsImplementation.write("""%s |
| %s |
| |
| #include "config.h" |
| |
| #include "%s" |
| |
| """ % (generatorString, copyrightText, headerName)) |
| |
| writeIncludeDirectives(builtinsImplementation, contentIncludes) |
| |
| builtinsImplementation.write(""" |
| namespace %s { |
| |
| """ % (namespace)) |
| |
| |
| |
| for (codeReference, function) in codeReferences: |
| source = function.source |
| source = "(function " + source[source.index("("):] + ")" |
| lines = json.dumps(source)[1:-1].split("\\n") |
| sourceLength = len(source) |
| source = "" |
| for line in lines: |
| source = source + (" \"%s\\n\" \\\n" % line) |
| builtinsImplementation.write("const char* s_%s =\n%s;\n\n" % (codeReference, source)) |
| builtinsImplementation.write("const int s_%sLength = %d;\n\n" % (codeReference, sourceLength + 1)) # + 1 for \n |
| constructAbility = "JSC::ConstructAbility::CannotConstruct" |
| if function.isConstructor: |
| constructAbility = "JSC::ConstructAbility::CanConstruct" |
| builtinsImplementation.write("const JSC::ConstructAbility s_%sConstructAbility = %s;\n\n" % (codeReference, constructAbility)) # + 1 for \n |
| |
| builtinsImplementation.write(""" |
| #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ |
| JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\ |
| """); |
| |
| if (namespace == "JSC"): |
| builtinsImplementation.write("""{\\ |
| return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source()); \\ |
| """) |
| else: |
| builtinName = scopeName[0].lower() + scopeName[1:] |
| builtinsImplementation.write("""{\\ |
| JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); \\ |
| return clientData->builtinFunctions().%s().codeName##Executable()->link(vm, clientData->builtinFunctions().%s().codeName##Source()); \\ |
| """% (builtinName, builtinName)) |
| |
| builtinsImplementation.write("""} |
| %s_FOREACH_BUILTIN(DEFINE_BUILTIN_GENERATOR) |
| #undef DEFINE_BUILTIN_GENERATOR |
| } |
| |
| """% (macroPrefix)) |
| |
| builtinsHeader.close() |
| builtinsImplementation.close() |
| |
| if (not os.path.exists(output_base + ".h")) or (not filecmp.cmp(output_base + ".h.tmp", output_base + ".h", shallow=False)): |
| if (os.path.exists(output_base + ".h")): |
| os.remove(output_base + ".h") |
| os.rename(output_base + ".h.tmp", output_base + ".h") |
| else: |
| os.remove(output_base + ".h.tmp") |
| |
| if (not os.path.exists(output_base + ".cpp")) or (not filecmp.cmp(output_base + ".cpp.tmp", output_base + ".cpp", shallow=False)): |
| if (os.path.exists(output_base + ".cpp")): |
| os.remove(output_base + ".cpp") |
| os.rename(output_base + ".cpp.tmp", output_base + ".cpp") |
| else: |
| os.remove(output_base + ".cpp.tmp") |