]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/python | |
2 | # Copyright (C) 2014 Apple Inc. All rights reserved. | |
3 | # | |
4 | # Redistribution and use in source and binary forms, with or without | |
5 | # modification, are permitted provided that the following conditions | |
6 | # are met: | |
7 | # 1. Redistributions of source code must retain the above copyright | |
8 | # notice, this list of conditions and the following disclaimer. | |
9 | # 2. Redistributions in binary form must reproduce the above copyright | |
10 | # notice, this list of conditions and the following disclaimer in the | |
11 | # documentation and/or other materials provided with the distribution. | |
12 | # | |
13 | # THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | ||
25 | import argparse | |
26 | import filecmp | |
27 | import fnmatch | |
28 | import os | |
29 | import re | |
30 | import shutil | |
31 | import sys | |
32 | import datetime | |
33 | import json | |
34 | ||
35 | parser = argparse.ArgumentParser() | |
36 | parser.add_argument('input_file', nargs='*', help='Input JS files which builtins generated from') | |
37 | parser.add_argument('--input-directory', help='All JS files will be used as input from this directory.') | |
38 | parser.add_argument('--output', help='path to output cpp or h file') | |
39 | args = parser.parse_args() | |
40 | ||
41 | copyrightText = """ * | |
42 | * Redistribution and use in source and binary forms, with or without | |
43 | * modification, are permitted provided that the following conditions | |
44 | * are met: | |
45 | * 1. Redistributions of source code must retain the above copyright | |
46 | * notice, this list of conditions and the following disclaimer. | |
47 | * 2. Redistributions in binary form must reproduce the above copyright | |
48 | * notice, this list of conditions and the following disclaimer in the | |
49 | * documentation and/or other materials provided with the distribution. | |
50 | * | |
51 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
52 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
54 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
55 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
56 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
57 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
58 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
59 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
60 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
61 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
62 | */\n | |
63 | """ | |
64 | ||
65 | generatorString = "/* Generated by %s do not hand edit. */\n" % os.path.basename(__file__) | |
66 | ||
67 | functionHeadRegExp = re.compile(r"function\s+\w+\s*\(.*?\)", re.MULTILINE | re.S) | |
68 | functionNameRegExp = re.compile(r"function\s+(\w+)\s*\(", re.MULTILINE | re.S) | |
69 | functionParameterFinder = re.compile(r"^function\s+(?:\w+)\s*\(((?:\s*\w+)?\s*(?:\s*,\s*\w+)*)?\s*\)", re.MULTILINE | re.S) | |
70 | ||
71 | multilineCommentRegExp = re.compile(r"\/\*.*?\*\/", re.MULTILINE | re.S) | |
72 | singleLineCommentRegExp = re.compile(r"\/\/.*?\n", re.MULTILINE | re.S) | |
73 | ||
74 | def getCopyright(source): | |
75 | copyrightBlock = multilineCommentRegExp.findall(source)[0] | |
76 | copyrightBlock = copyrightBlock[:copyrightBlock.index("Redistribution")] | |
77 | copyRightLines = [] | |
78 | ||
79 | for line in copyrightBlock.split("\n"): | |
80 | line = line.replace("/*", "") | |
81 | line = line.replace("*/", "") | |
82 | line = line.replace("*", "") | |
83 | line = line.replace("Copyright", "") | |
84 | line = line.replace("copyright", "") | |
85 | line = line.replace("(C)", "") | |
86 | line = line.replace("(c)", "") | |
87 | line = line.strip() | |
88 | if len(line) == 0: | |
89 | continue | |
90 | ||
91 | copyRightLines.append(line) | |
92 | ||
93 | return list(set(copyRightLines)) | |
94 | ||
95 | ||
96 | ||
97 | def getFunctions(source): | |
98 | ||
99 | source = multilineCommentRegExp.sub("/**/", singleLineCommentRegExp.sub("//\n", source)) | |
100 | ||
101 | matches = [ f for f in functionHeadRegExp.finditer(source)] | |
102 | functionBounds = [] | |
103 | start = 0 | |
104 | end = 0 | |
105 | for match in matches: | |
106 | start = match.start() | |
107 | if start < end: | |
108 | continue | |
109 | end = match.end() | |
110 | while source[end] != '{': | |
111 | end = end + 1 | |
112 | depth = 1 | |
113 | end = end + 1 | |
114 | while depth > 0: | |
115 | if source[end] == '{': | |
116 | depth = depth + 1 | |
117 | elif source[end] == '}': | |
118 | depth = depth - 1 | |
119 | end = end + 1 | |
120 | functionBounds.append((start, end)) | |
121 | ||
122 | functions = [source[start:end].strip() for (start, end) in functionBounds] | |
123 | result = [] | |
124 | for function in functions: | |
125 | function = multilineCommentRegExp.sub("", function) | |
126 | functionName = functionNameRegExp.findall(function)[0] | |
127 | functionParameters = functionParameterFinder.findall(function)[0].split(',') | |
128 | if len(functionParameters[0]) == 0: | |
129 | functionParameters = [] | |
130 | ||
131 | result.append((functionName, function, functionParameters)) | |
132 | return result | |
133 | ||
134 | ||
135 | def generateCode(source): | |
136 | inputFile = open(source, "r") | |
137 | baseName = os.path.basename(source).replace(".js", "") | |
138 | ||
139 | source = "" | |
140 | for line in inputFile: | |
141 | source = source + line | |
142 | ||
143 | if sys.platform == "cygwin": | |
144 | source = source.replace("\r\n", "\n") | |
145 | return (baseName, getFunctions(source), getCopyright(source)) | |
146 | ||
147 | def mangleName(object, name): | |
148 | qName = object + "." + name | |
149 | mangledName = "" | |
150 | i = 0 | |
151 | while i < len(qName): | |
152 | if qName[i] == '.': | |
153 | mangledName = mangledName + qName[i + 1].upper() | |
154 | i = i + 1 | |
155 | else: | |
156 | mangledName = mangledName + qName[i] | |
157 | i = i + 1 | |
158 | return mangledName | |
159 | ||
160 | builtins = [] | |
161 | copyrights = [] | |
162 | (output_base, _) = os.path.splitext(args.output) | |
163 | ||
164 | if args.input_directory: | |
165 | for file in os.listdir(args.input_directory): | |
166 | args.input_file.append(os.path.join(args.input_directory, file)) | |
167 | ||
168 | for file in args.input_file: | |
169 | if fnmatch.fnmatch(file, '*.js'): | |
170 | (baseName, functions, objectCopyrights) = generateCode(file) | |
171 | copyrights.extend(objectCopyrights) | |
172 | builtins.append((baseName, functions)) | |
173 | ||
174 | copyrights = list(set(copyrights)) | |
175 | ||
176 | copyrightBody = "" | |
177 | for copyright in copyrights: | |
178 | copyrightBody = copyrightBody +" * Copyright (C) " + copyright + "\n" | |
179 | ||
180 | builtinsHeader = open(output_base + ".h.tmp", "w") | |
181 | builtinsImplementation = open(output_base + ".cpp.tmp", "w") | |
182 | copyrightText = "/*\n" + copyrightBody + copyrightText | |
183 | builtinsHeader.write("""%s | |
184 | %s | |
185 | ||
186 | #ifndef JSCBuiltins_H | |
187 | #define JSCBuiltins_H | |
188 | ||
189 | namespace JSC { | |
190 | ||
191 | class FunctionExecutable; | |
192 | class Identifier; | |
193 | class JSGlobalObject; | |
194 | class SourceCode; | |
195 | class UnlinkedFunctionExecutable; | |
196 | class VM; | |
197 | ||
198 | FunctionExecutable* createBuiltinExecutable(VM&, UnlinkedFunctionExecutable*, const SourceCode&); | |
199 | ||
200 | """ % (generatorString, copyrightText)) | |
201 | ||
202 | codeReferences = [] | |
203 | ||
204 | for (objectName, functions) in builtins: | |
205 | print("Generating bindings for the %s builtin." % objectName) | |
206 | builtinsHeader.write("/* %s functions */\n" % objectName) | |
207 | for (name, implementation, _) in functions: | |
208 | mangledName = mangleName(objectName, name) | |
209 | mangledName = mangledName[0].lower() + mangledName[1:] + "Code" | |
210 | codeReferences.append((mangledName, name, implementation)) | |
211 | builtinsHeader.write("extern const char* s_%s;\n" % mangledName) | |
212 | builtinsHeader.write("extern const int s_%sLength;\n" % mangledName) | |
213 | builtinsHeader.write("\n") | |
214 | builtinsHeader.write("#define JSC_FOREACH_%s_BUILTIN(macro) \\\n" % objectName.replace(".", "_").upper()) | |
215 | for (name, implementation, arguments) in functions: | |
216 | mangledName = mangleName(objectName, name) | |
217 | builtinsHeader.write(" macro(%s, %s, %d) \\\n" % (name, mangledName, len(arguments))) | |
218 | builtinsHeader.write("\n") | |
219 | for (name, implementation, arguments) in functions: | |
220 | builtinsHeader.write("#define JSC_BUILTIN_%s 1\n" % mangleName(objectName, name).upper()) | |
221 | builtinsHeader.write("\n\n") | |
222 | names = [] | |
223 | builtinsHeader.write("#define JSC_FOREACH_BUILTIN(macro)\\\n") | |
224 | for (codeReference, functionName, source) in codeReferences: | |
225 | builtinsHeader.write(" macro(%s, %s, s_%sLength) \\\n" % (codeReference, functionName, codeReference)) | |
226 | names.append(functionName) | |
227 | ||
228 | builtinsHeader.write("\n\n") | |
229 | builtinsHeader.write("#define JSC_FOREACH_BUILTIN_FUNCTION_NAME(macro) \\\n") | |
230 | for name in sorted(set(names)): | |
231 | builtinsHeader.write(" macro(%s) \\\n" % name) | |
232 | builtinsHeader.write(""" | |
233 | ||
234 | #define JSC_DECLARE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ | |
235 | FunctionExecutable* codeName##Generator(VM&); | |
236 | ||
237 | JSC_FOREACH_BUILTIN(JSC_DECLARE_BUILTIN_GENERATOR) | |
238 | #undef JSC_DECLARE_BUILTIN_GENERATOR | |
239 | ||
240 | #define JSC_BUILTIN_EXISTS(name) defined JSC_BUILTIN_ ## name | |
241 | ||
242 | } | |
243 | ||
244 | #endif | |
245 | ||
246 | """) | |
247 | ||
248 | builtinsImplementation.write("""%s | |
249 | %s | |
250 | ||
251 | #include "config.h" | |
252 | ||
253 | #include "JSCBuiltins.h" | |
254 | ||
255 | #include "BuiltinExecutables.h" | |
256 | #include "Executable.h" | |
257 | #include "JSCellInlines.h" | |
258 | #include "VM.h" | |
259 | ||
260 | namespace JSC { | |
261 | ||
262 | """ % (generatorString, copyrightText)) | |
263 | ||
264 | for (codeReference, name, source) in codeReferences: | |
265 | source = "(function " + source[source.index("("):] + ")" | |
266 | lines = json.dumps(source)[1:-1].split("\\n") | |
267 | sourceLength = len(source) | |
268 | source = "" | |
269 | for line in lines: | |
270 | source = source + (" \"%s\\n\" \\\n" % line) | |
271 | builtinsImplementation.write("const char* s_%s =\n%s;\n\n" % (codeReference, source)) | |
272 | builtinsImplementation.write("const int s_%sLength = %d;\n\n" % (codeReference, sourceLength + 1)) # + 1 for \n | |
273 | ||
274 | builtinsImplementation.write(""" | |
275 | #define JSC_DEFINE_BUILTIN_GENERATOR(codeName, functionName, argumentCount) \\ | |
276 | FunctionExecutable* codeName##Generator(VM& vm) \\ | |
277 | { \\ | |
278 | return vm.builtinExecutables()->codeName##Executable()->link(vm, vm.builtinExecutables()->codeName##Source()); \\ | |
279 | } | |
280 | ||
281 | JSC_FOREACH_BUILTIN(JSC_DEFINE_BUILTIN_GENERATOR) | |
282 | #undef JSC_DEFINE_BUILTIN_GENERATOR | |
283 | } | |
284 | ||
285 | """) | |
286 | ||
287 | builtinsHeader.close() | |
288 | builtinsImplementation.close() | |
289 | ||
290 | if (not os.path.exists(output_base + ".h")) or (not filecmp.cmp(output_base + ".h.tmp", output_base + ".h", shallow=False)): | |
291 | if (os.path.exists(output_base + ".h")): | |
292 | os.remove(output_base + ".h") | |
293 | os.rename(output_base + ".h.tmp", output_base + ".h") | |
294 | else: | |
295 | os.remove(output_base + ".h.tmp") | |
296 | ||
297 | if (not os.path.exists(output_base + ".cpp")) or (not filecmp.cmp(output_base + ".cpp.tmp", output_base + ".cpp", shallow=False)): | |
298 | if (os.path.exists(output_base + ".cpp")): | |
299 | os.remove(output_base + ".cpp") | |
300 | os.rename(output_base + ".cpp.tmp", output_base + ".cpp") | |
301 | else: | |
302 | os.remove(output_base + ".cpp.tmp") |