Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 4 | # Redistribution and use in source and binary forms, with or without |
| 5 | # modification, are permitted provided that the following conditions are |
| 6 | # met: |
| 7 | # |
| 8 | # * Redistributions of source code must retain the above copyright |
| 9 | # notice, this list of conditions and the following disclaimer. |
| 10 | # * Redistributions in binary form must reproduce the above |
| 11 | # copyright notice, this list of conditions and the following |
| 12 | # disclaimer in the documentation and/or other materials provided |
| 13 | # with the distribution. |
| 14 | # * Neither the name of Google Inc. nor the names of its |
| 15 | # contributors may be used to endorse or promote products derived |
| 16 | # from this software without specific prior written permission. |
| 17 | # |
| 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
| 30 | # This is a utility for converting JavaScript source code into C-style |
| 31 | # char arrays. It is used for embedded JavaScript code in the V8 |
| 32 | # library. |
| 33 | |
| 34 | import os, re, sys, string |
| 35 | import jsmin |
| 36 | |
| 37 | |
| 38 | def ToCArray(lines): |
| 39 | result = [] |
| 40 | for chr in lines: |
| 41 | value = ord(chr) |
| 42 | assert value < 128 |
| 43 | result.append(str(value)) |
| 44 | result.append("0") |
| 45 | return ", ".join(result) |
| 46 | |
| 47 | |
| 48 | def RemoveCommentsAndTrailingWhitespace(lines): |
| 49 | lines = re.sub(r'//.*\n', '\n', lines) # end-of-line comments |
| 50 | lines = re.sub(re.compile(r'/\*.*?\*/', re.DOTALL), '', lines) # comments. |
| 51 | lines = re.sub(r'\s+\n+', '\n', lines) # trailing whitespace |
| 52 | return lines |
| 53 | |
| 54 | |
| 55 | def ReadFile(filename): |
| 56 | file = open(filename, "rt") |
| 57 | try: |
| 58 | lines = file.read() |
| 59 | finally: |
| 60 | file.close() |
| 61 | return lines |
| 62 | |
| 63 | |
| 64 | def ReadLines(filename): |
| 65 | result = [] |
| 66 | for line in open(filename, "rt"): |
| 67 | if '#' in line: |
| 68 | line = line[:line.index('#')] |
| 69 | line = line.strip() |
| 70 | if len(line) > 0: |
| 71 | result.append(line) |
| 72 | return result |
| 73 | |
| 74 | |
| 75 | def LoadConfigFrom(name): |
| 76 | import ConfigParser |
| 77 | config = ConfigParser.ConfigParser() |
| 78 | config.read(name) |
| 79 | return config |
| 80 | |
| 81 | |
| 82 | def ParseValue(string): |
| 83 | string = string.strip() |
| 84 | if string.startswith('[') and string.endswith(']'): |
| 85 | return string.lstrip('[').rstrip(']').split() |
| 86 | else: |
| 87 | return string |
| 88 | |
| 89 | |
| 90 | EVAL_PATTERN = re.compile(r'\beval\s*\('); |
| 91 | WITH_PATTERN = re.compile(r'\bwith\s*\('); |
| 92 | |
| 93 | |
| 94 | def Validate(lines, file): |
| 95 | lines = RemoveCommentsAndTrailingWhitespace(lines) |
| 96 | # Because of simplified context setup, eval and with is not |
| 97 | # allowed in the natives files. |
| 98 | eval_match = EVAL_PATTERN.search(lines) |
| 99 | if eval_match: |
| 100 | raise ("Eval disallowed in natives: %s" % file) |
| 101 | with_match = WITH_PATTERN.search(lines) |
| 102 | if with_match: |
| 103 | raise ("With statements disallowed in natives: %s" % file) |
| 104 | |
| 105 | |
| 106 | def ExpandConstants(lines, constants): |
| 107 | for key, value in constants.items(): |
| 108 | lines = lines.replace(key, str(value)) |
| 109 | return lines |
| 110 | |
| 111 | |
| 112 | def ExpandMacros(lines, macros): |
| 113 | for name, macro in macros.items(): |
| 114 | start = lines.find(name + '(', 0) |
| 115 | while start != -1: |
| 116 | # Scan over the arguments |
| 117 | assert lines[start + len(name)] == '(' |
| 118 | height = 1 |
| 119 | end = start + len(name) + 1 |
| 120 | last_match = end |
| 121 | arg_index = 0 |
| 122 | mapping = { } |
| 123 | def add_arg(str): |
| 124 | # Remember to expand recursively in the arguments |
| 125 | replacement = ExpandMacros(str.strip(), macros) |
| 126 | mapping[macro.args[arg_index]] = replacement |
| 127 | while end < len(lines) and height > 0: |
| 128 | # We don't count commas at higher nesting levels. |
| 129 | if lines[end] == ',' and height == 1: |
| 130 | add_arg(lines[last_match:end]) |
| 131 | last_match = end + 1 |
| 132 | elif lines[end] in ['(', '{', '[']: |
| 133 | height = height + 1 |
| 134 | elif lines[end] in [')', '}', ']']: |
| 135 | height = height - 1 |
| 136 | end = end + 1 |
| 137 | # Remember to add the last match. |
| 138 | add_arg(lines[last_match:end-1]) |
| 139 | result = macro.expand(mapping) |
| 140 | # Replace the occurrence of the macro with the expansion |
| 141 | lines = lines[:start] + result + lines[end:] |
| 142 | start = lines.find(name + '(', end) |
| 143 | return lines |
| 144 | |
| 145 | class TextMacro: |
| 146 | def __init__(self, args, body): |
| 147 | self.args = args |
| 148 | self.body = body |
| 149 | def expand(self, mapping): |
| 150 | result = self.body |
| 151 | for key, value in mapping.items(): |
| 152 | result = result.replace(key, value) |
| 153 | return result |
| 154 | |
| 155 | class PythonMacro: |
| 156 | def __init__(self, args, fun): |
| 157 | self.args = args |
| 158 | self.fun = fun |
| 159 | def expand(self, mapping): |
| 160 | args = [] |
| 161 | for arg in self.args: |
| 162 | args.append(mapping[arg]) |
| 163 | return str(self.fun(*args)) |
| 164 | |
| 165 | CONST_PATTERN = re.compile(r'^const\s+([a-zA-Z0-9_]+)\s*=\s*([^;]*);$') |
| 166 | MACRO_PATTERN = re.compile(r'^macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$') |
| 167 | PYTHON_MACRO_PATTERN = re.compile(r'^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$') |
| 168 | |
| 169 | def ReadMacros(lines): |
| 170 | constants = { } |
| 171 | macros = { } |
| 172 | for line in lines: |
| 173 | hash = line.find('#') |
| 174 | if hash != -1: line = line[:hash] |
| 175 | line = line.strip() |
| 176 | if len(line) is 0: continue |
| 177 | const_match = CONST_PATTERN.match(line) |
| 178 | if const_match: |
| 179 | name = const_match.group(1) |
| 180 | value = const_match.group(2).strip() |
| 181 | constants[name] = value |
| 182 | else: |
| 183 | macro_match = MACRO_PATTERN.match(line) |
| 184 | if macro_match: |
| 185 | name = macro_match.group(1) |
| 186 | args = map(string.strip, macro_match.group(2).split(',')) |
| 187 | body = macro_match.group(3).strip() |
| 188 | macros[name] = TextMacro(args, body) |
| 189 | else: |
| 190 | python_match = PYTHON_MACRO_PATTERN.match(line) |
| 191 | if python_match: |
| 192 | name = python_match.group(1) |
| 193 | args = map(string.strip, python_match.group(2).split(',')) |
| 194 | body = python_match.group(3).strip() |
| 195 | fun = eval("lambda " + ",".join(args) + ': ' + body) |
| 196 | macros[name] = PythonMacro(args, fun) |
| 197 | else: |
| 198 | raise ("Illegal line: " + line) |
| 199 | return (constants, macros) |
| 200 | |
| 201 | |
| 202 | HEADER_TEMPLATE = """\ |
| 203 | // Copyright 2008 Google Inc. All Rights Reserved. |
| 204 | |
| 205 | // This file was generated from .js source files by SCons. If you |
| 206 | // want to make changes to this file you should either change the |
| 207 | // javascript source files or the SConstruct script. |
| 208 | |
| 209 | #include "v8.h" |
| 210 | #include "natives.h" |
| 211 | |
| 212 | namespace v8 { |
| 213 | namespace internal { |
| 214 | |
| 215 | %(source_lines)s\ |
| 216 | |
| 217 | template <> |
| 218 | int NativesCollection<%(type)s>::GetBuiltinsCount() { |
| 219 | return %(builtin_count)i; |
| 220 | } |
| 221 | |
| 222 | template <> |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 223 | int NativesCollection<%(type)s>::GetDebuggerCount() { |
| 224 | return %(debugger_count)i; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | template <> |
| 228 | int NativesCollection<%(type)s>::GetIndex(const char* name) { |
| 229 | %(get_index_cases)s\ |
| 230 | return -1; |
| 231 | } |
| 232 | |
| 233 | template <> |
| 234 | Vector<const char> NativesCollection<%(type)s>::GetScriptSource(int index) { |
| 235 | %(get_script_source_cases)s\ |
| 236 | return Vector<const char>("", 0); |
| 237 | } |
| 238 | |
| 239 | template <> |
| 240 | Vector<const char> NativesCollection<%(type)s>::GetScriptName(int index) { |
| 241 | %(get_script_name_cases)s\ |
| 242 | return Vector<const char>("", 0); |
| 243 | } |
| 244 | |
| 245 | } // internal |
| 246 | } // v8 |
| 247 | """ |
| 248 | |
| 249 | |
| 250 | SOURCE_DECLARATION = """\ |
| 251 | static const char %(id)s[] = { %(data)s }; |
| 252 | """ |
| 253 | |
| 254 | |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 255 | GET_DEBUGGER_INDEX_CASE = """\ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 256 | if (strcmp(name, "%(id)s") == 0) return %(i)i; |
| 257 | """ |
| 258 | |
| 259 | |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 260 | GET_DEBUGGER_SCRIPT_SOURCE_CASE = """\ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 261 | if (index == %(i)i) return Vector<const char>(%(id)s, %(length)i); |
| 262 | """ |
| 263 | |
| 264 | |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 265 | GET_DEBUGGER_SCRIPT_NAME_CASE = """\ |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 266 | if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i); |
| 267 | """ |
| 268 | |
| 269 | def JS2C(source, target, env): |
| 270 | ids = [] |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 271 | debugger_ids = [] |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 272 | modules = [] |
| 273 | # Locate the macros file name. |
| 274 | consts = {} |
| 275 | macros = {} |
| 276 | for s in source: |
| 277 | if 'macros.py' == (os.path.split(str(s))[1]): |
| 278 | (consts, macros) = ReadMacros(ReadLines(str(s))) |
| 279 | else: |
| 280 | modules.append(s) |
| 281 | |
| 282 | # Build source code lines |
| 283 | source_lines = [ ] |
| 284 | |
| 285 | minifier = jsmin.JavaScriptMinifier() |
| 286 | |
| 287 | source_lines_empty = [] |
| 288 | for module in modules: |
| 289 | filename = str(module) |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 290 | debugger = filename.endswith('-debugger.js') |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 291 | lines = ReadFile(filename) |
| 292 | lines = ExpandConstants(lines, consts) |
| 293 | lines = ExpandMacros(lines, macros) |
| 294 | Validate(lines, filename) |
| 295 | lines = minifier.JSMinify(lines) |
| 296 | data = ToCArray(lines) |
| 297 | id = (os.path.split(filename)[1])[:-3] |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 298 | if debugger: id = id[:-9] |
| 299 | if debugger: |
| 300 | debugger_ids.append((id, len(lines))) |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 301 | else: |
| 302 | ids.append((id, len(lines))) |
| 303 | source_lines.append(SOURCE_DECLARATION % { 'id': id, 'data': data }) |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 304 | source_lines_empty.append(SOURCE_DECLARATION % { 'id': id, 'data': data }) |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 305 | |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 306 | # Build debugger support functions |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 307 | get_index_cases = [ ] |
| 308 | get_script_source_cases = [ ] |
| 309 | get_script_name_cases = [ ] |
| 310 | |
| 311 | i = 0 |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 312 | for (id, length) in debugger_ids: |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 313 | native_name = "native %s.js" % id |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 314 | get_index_cases.append(GET_DEBUGGER_INDEX_CASE % { 'id': id, 'i': i }) |
| 315 | get_script_source_cases.append(GET_DEBUGGER_SCRIPT_SOURCE_CASE % { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 316 | 'id': id, |
| 317 | 'length': length, |
| 318 | 'i': i |
| 319 | }) |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 320 | get_script_name_cases.append(GET_DEBUGGER_SCRIPT_NAME_CASE % { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 321 | 'name': native_name, |
| 322 | 'length': len(native_name), |
| 323 | 'i': i |
| 324 | }); |
| 325 | i = i + 1 |
| 326 | |
| 327 | for (id, length) in ids: |
| 328 | native_name = "native %s.js" % id |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 329 | get_index_cases.append(GET_DEBUGGER_INDEX_CASE % { 'id': id, 'i': i }) |
| 330 | get_script_source_cases.append(GET_DEBUGGER_SCRIPT_SOURCE_CASE % { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 331 | 'id': id, |
| 332 | 'length': length, |
| 333 | 'i': i |
| 334 | }) |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 335 | get_script_name_cases.append(GET_DEBUGGER_SCRIPT_NAME_CASE % { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 336 | 'name': native_name, |
| 337 | 'length': len(native_name), |
| 338 | 'i': i |
| 339 | }); |
| 340 | i = i + 1 |
| 341 | |
| 342 | # Emit result |
| 343 | output = open(str(target[0]), "w") |
| 344 | output.write(HEADER_TEMPLATE % { |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 345 | 'builtin_count': len(ids) + len(debugger_ids), |
| 346 | 'debugger_count': len(debugger_ids), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 347 | 'source_lines': "\n".join(source_lines), |
| 348 | 'get_index_cases': "".join(get_index_cases), |
| 349 | 'get_script_source_cases': "".join(get_script_source_cases), |
| 350 | 'get_script_name_cases': "".join(get_script_name_cases), |
| 351 | 'type': env['TYPE'] |
| 352 | }) |
| 353 | output.close() |
| 354 | |
| 355 | if len(target) > 1: |
| 356 | output = open(str(target[1]), "w") |
| 357 | output.write(HEADER_TEMPLATE % { |
Andrei Popescu | 3100271 | 2010-02-23 13:46:05 +0000 | [diff] [blame] | 358 | 'builtin_count': len(ids) + len(debugger_ids), |
| 359 | 'debugger_count': len(debugger_ids), |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 360 | 'source_lines': "\n".join(source_lines_empty), |
| 361 | 'get_index_cases': "".join(get_index_cases), |
| 362 | 'get_script_source_cases': "".join(get_script_source_cases), |
| 363 | 'get_script_name_cases': "".join(get_script_name_cases), |
| 364 | 'type': env['TYPE'] |
| 365 | }) |
| 366 | output.close() |
| 367 | |
| 368 | def main(): |
| 369 | natives = sys.argv[1] |
| 370 | natives_empty = sys.argv[2] |
| 371 | type = sys.argv[3] |
| 372 | source_files = sys.argv[4:] |
| 373 | JS2C(source_files, [natives, natives_empty], { 'TYPE': type }) |
| 374 | |
| 375 | if __name__ == "__main__": |
| 376 | main() |