#!/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') 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\s+\w+\s*\(.*?\)", re.MULTILINE | re.S) functionNameRegExp = re.compile(r"function\s+(\w+)\s*\(", re.MULTILINE | re.S) functionParameterFinder = re.compile(r"^function\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)) 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] functionParameters = functionParameterFinder.findall(function)[0].split(',') if len(functionParameters[0]) == 0: functionParameters = [] result.append((functionName, function, functionParameters)) return result 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)) def mangleName(object, name): qName = object + "." + 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 return mangledName 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)) 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 JSCBuiltins_H #define JSCBuiltins_H namespace JSC { class FunctionExecutable; class Identifier; class JSGlobalObject; class SourceCode; class UnlinkedFunctionExecutable; class VM; FunctionExecutable* createBuiltinExecutable(VM&, UnlinkedFunctionExecutable*, const SourceCode&); """ % (generatorString, copyrightText)) codeReferences = [] for (objectName, functions) in builtins: print("Generating bindings for the %s builtin." % objectName) builtinsHeader.write("/* %s functions */\n" % objectName) for (name, implementation, _) in functions: mangledName = mangleName(objectName, name) mangledName = mangledName[0].lower() + mangledName[1:] + "Code" codeReferences.append((mangledName, name, implementation)) builtinsHeader.write("extern const char* s_%s;\n" % mangledName) builtinsHeader.write("extern const int s_%sLength;\n" % mangledName) builtinsHeader.write("\n") builtinsHeader.write("#define JSC_FOREACH_%s_BUILTIN(macro) \\\n" % objectName.replace(".", "_").upper()) for (name, implementation, arguments) in functions: mangledName = mangleName(objectName, name) builtinsHeader.write(" macro(%s, %s, %d) \\\n" % (name, mangledName, len(arguments))) builtinsHeader.write("\n") for (name, implementation, arguments) in functions: builtinsHeader.write("#define JSC_BUILTIN_%s 1\n" % mangleName(objectName, name).upper()) builtinsHeader.write("\n\n") names = [] builtinsHeader.write("#define JSC_FOREACH_BUILTIN(macro)\\\n") for (codeReference, functionName, source) in codeReferences: builtinsHeader.write(" macro(%s, %s, s_%sLength) \\\n" % (codeReference, functionName, codeReference)) names.append(functionName) builtinsHeader.write("\n\n") builtinsHeader.write("#define JSC_FOREACH_BUILTIN_FUNCTION_NAME(macro) \\\n") for name in sorted(set(names)): builtinsHeader.write(" macro(%s) \\\n" % name) builtinsHeader.write(""" #define JSC_DECLARE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ FunctionExecutable* codeName##Generator(VM&); JSC_FOREACH_BUILTIN(JSC_DECLARE_BUILTIN_GENERATOR) #undef JSC_DECLARE_BUILTIN_GENERATOR #define JSC_BUILTIN_EXISTS(name) defined JSC_BUILTIN_ ## name } #endif """) builtinsImplementation.write("""%s %s #include "config.h" #include "JSCBuiltins.h" #include "BuiltinExecutables.h" #include "Executable.h" #include "JSCellInlines.h" #include "VM.h" namespace JSC { """ % (generatorString, copyrightText)) for (codeReference, name, source) in codeReferences: 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 builtinsImplementation.write(""" #define JSC_DEFINE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ FunctionExecutable* codeName##Generator(VM& vm) \\ { \\ return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source()); \\ } JSC_FOREACH_BUILTIN(JSC_DEFINE_BUILTIN_GENERATOR) #undef JSC_DEFINE_BUILTIN_GENERATOR } """) 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")