Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Unified Diff: tools/js2c.py

Issue 225723002: Add support for --raw and --omit to js2c. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: use optparse instead of argparse. (Some build bots use python 2.6 which doesn't have argparse.) Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/js2c.py
diff --git a/tools/js2c.py b/tools/js2c.py
index f67d053ad26360c07cf190fefea4c7cb73ce6ab6..17182109207f9e51c55bea62c3e30d07e94ebc95 100755
--- a/tools/js2c.py
+++ b/tools/js2c.py
@@ -32,24 +32,23 @@
# library.
import os, re, sys, string
+import optparse
import jsmin
import bz2
+import textwrap
-def ToCAsciiArray(lines):
- result = []
- for chr in lines:
- value = ord(chr)
- assert value < 128
- result.append(str(value))
- return ", ".join(result)
+class Error(Exception):
+ def __init__(self, msg):
+ Exception.__init__(self, msg)
-def ToCArray(lines):
+def ToCArray(byte_sequence):
result = []
- for chr in lines:
+ for chr in byte_sequence:
result.append(str(ord(chr)))
- return ", ".join(result)
+ joined = ", ".join(result)
+ return textwrap.fill(joined, 80)
def RemoveCommentsAndTrailingWhitespace(lines):
@@ -68,46 +67,19 @@ def ReadFile(filename):
return lines
-def ReadLines(filename):
- result = []
- for line in open(filename, "rt"):
- if '#' in line:
- line = line[:line.index('#')]
- line = line.strip()
- if len(line) > 0:
- result.append(line)
- return result
-
-
-def LoadConfigFrom(name):
- import ConfigParser
- config = ConfigParser.ConfigParser()
- config.read(name)
- return config
-
-
-def ParseValue(string):
- string = string.strip()
- if string.startswith('[') and string.endswith(']'):
- return string.lstrip('[').rstrip(']').split()
- else:
- return string
-
-
EVAL_PATTERN = re.compile(r'\beval\s*\(')
WITH_PATTERN = re.compile(r'\bwith\s*\(')
-
-def Validate(lines, file):
- lines = RemoveCommentsAndTrailingWhitespace(lines)
+def Validate(lines):
# Because of simplified context setup, eval and with is not
# allowed in the natives files.
- eval_match = EVAL_PATTERN.search(lines)
- if eval_match:
- raise ("Eval disallowed in natives: %s" % file)
- with_match = WITH_PATTERN.search(lines)
- if with_match:
- raise ("With statements disallowed in natives: %s" % file)
+ if EVAL_PATTERN.search(lines):
+ raise Error("Eval disallowed in natives.")
+ if WITH_PATTERN.search(lines):
+ raise Error("With statements disallowed in natives.")
+
+ # Pass lines through unchanged.
+ return lines
def ExpandConstants(lines, constants):
@@ -187,7 +159,7 @@ PYTHON_MACRO_PATTERN = re.compile(r'^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*
def ReadMacros(lines):
constants = []
macros = []
- for line in lines:
+ for line in lines.split('\n'):
hash = line.find('#')
if hash != -1: line = line[:hash]
line = line.strip()
@@ -213,13 +185,13 @@ def ReadMacros(lines):
fun = eval("lambda " + ",".join(args) + ': ' + body)
macros.append((re.compile("\\b%s\\(" % name), PythonMacro(args, fun)))
else:
- raise ("Illegal line: " + line)
+ raise Error("Illegal line: " + line)
return (constants, macros)
INLINE_MACRO_PATTERN = re.compile(r'macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*\n')
INLINE_MACRO_END_PATTERN = re.compile(r'endmacro\s*\n')
-def ExpandInlineMacros(lines, filename):
+def ExpandInlineMacros(lines):
pos = 0
while True:
macro_match = INLINE_MACRO_PATTERN.search(lines, pos)
@@ -230,7 +202,7 @@ def ExpandInlineMacros(lines, filename):
args = [match.strip() for match in macro_match.group(2).split(',')]
end_macro_match = INLINE_MACRO_END_PATTERN.search(lines, macro_match.end());
if end_macro_match is None:
- raise ("Macro %s unclosed in %s" % (name, filename))
+ raise Error("Macro %s unclosed" % name)
body = lines[macro_match.end():end_macro_match.start()]
# remove macro definition
@@ -245,6 +217,7 @@ def ExpandInlineMacros(lines, filename):
return s
lines = ExpandMacroDefinition(lines, pos, name_pattern, macro, non_expander)
+
HEADER_TEMPLATE = """\
// Copyright 2011 Google Inc. All Rights Reserved.
@@ -259,7 +232,7 @@ HEADER_TEMPLATE = """\
namespace v8 {
namespace internal {
- static const byte sources[] = { %(sources_data)s };
+%(sources_declaration)s\
%(raw_sources_declaration)s\
@@ -311,6 +284,10 @@ namespace internal {
} // v8
"""
+SOURCES_DECLARATION = """\
+ static const byte sources[] = { %s };
+"""
+
RAW_SOURCES_COMPRESSION_DECLARATION = """\
static const char* raw_sources = NULL;
@@ -336,97 +313,202 @@ GET_SCRIPT_NAME_CASE = """\
if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
"""
-def JS2C(source, target, env):
- ids = []
- debugger_ids = []
- modules = []
- # Locate the macros file name.
- consts = []
- macros = []
- for s in source:
- if 'macros.py' == (os.path.split(str(s))[1]):
- (consts, macros) = ReadMacros(ReadLines(str(s)))
- else:
- modules.append(s)
-
- minifier = jsmin.JavaScriptMinifier()
-
- module_offset = 0
- all_sources = []
- for module in modules:
- filename = str(module)
- debugger = filename.endswith('-debugger.js')
- lines = ReadFile(filename)
- lines = ExpandConstants(lines, consts)
- lines = ExpandMacros(lines, macros)
- lines = RemoveCommentsAndTrailingWhitespace(lines)
- lines = ExpandInlineMacros(lines, filename)
- Validate(lines, filename)
- lines = minifier.JSMinify(lines)
- id = (os.path.split(filename)[1])[:-3]
- if debugger: id = id[:-9]
- raw_length = len(lines)
- if debugger:
- debugger_ids.append((id, raw_length, module_offset))
- else:
- ids.append((id, raw_length, module_offset))
- all_sources.append(lines)
- module_offset += raw_length
- total_length = raw_total_length = module_offset
-
- if env['COMPRESSION'] == 'off':
- raw_sources_declaration = RAW_SOURCES_DECLARATION
- sources_data = ToCAsciiArray("".join(all_sources))
+
+def BuildFilterChain(macro_filename):
+ """Build the chain of filter functions to be applied to the sources.
+
+ Args:
+ macro_filename: Name of the macro file, if any.
+
+ Returns:
+ A function (string -> string) that reads a source file and processes it.
+ """
+ filter_chain = [ReadFile]
+
+ if macro_filename:
+ (consts, macros) = ReadMacros(ReadFile(macro_filename))
+ filter_chain.append(lambda l: ExpandConstants(l, consts))
+ filter_chain.append(lambda l: ExpandMacros(l, macros))
+
+ filter_chain.extend([
+ RemoveCommentsAndTrailingWhitespace,
+ ExpandInlineMacros,
+ Validate,
+ jsmin.JavaScriptMinifier().JSMinify
+ ])
+
+ def chain(f1, f2):
+ return lambda x: f2(f1(x))
+
+ return reduce(chain, filter_chain)
+
+
+class Sources:
+ def __init__(self):
+ self.names = []
+ self.modules = []
+ self.is_debugger_id = []
+
+
+def IsDebuggerFile(filename):
+ return filename.endswith("-debugger.js")
+
+def IsMacroFile(filename):
+ return filename.endswith("macros.py")
+
+
+def PrepareSources(source_files):
+ """Read, prepare and assemble the list of source files.
+
+ Args:
+ sources: List of Javascript-ish source files. A file named macros.py
+ will be treated as a list of macros.
+
+ Returns:
+ An instance of Sources.
+ """
+ macro_file = None
+ macro_files = filter(IsMacroFile, source_files)
+ assert len(macro_files) in [0, 1]
+ if macro_files:
+ source_files.remove(macro_files[0])
+ macro_file = macro_files[0]
+
+ filters = BuildFilterChain(macro_file)
+
+ # Sort 'debugger' sources first.
+ source_files = sorted(source_files,
+ lambda l,r: IsDebuggerFile(r) - IsDebuggerFile(l))
+
+ result = Sources()
+ for source in source_files:
+ try:
+ lines = filters(source)
+ except Error as e:
+ raise Error("In file %s:\n%s" % (source, str(e)))
+
+ result.modules.append(lines);
+
+ is_debugger = IsDebuggerFile(source)
+ result.is_debugger_id.append(is_debugger);
+
+ name = os.path.basename(source)[:-3]
+ result.names.append(name if not is_debugger else name[:-9]);
+ return result
+
+
+def BuildMetadata(sources, source_bytes, native_type, omit):
+ """Build the meta data required to generate a libaries file.
+
+ Args:
+ sources: A Sources instance with the prepared sources.
+ source_bytes: A list of source bytes.
+ (The concatenation of all sources; might be compressed.)
+ native_type: The parameter for the NativesCollection template.
+ omit: bool, whether we should omit the sources in the output.
+
+ Returns:
+ A dictionary for use with HEADER_TEMPLATE.
+ """
+ total_length = len(source_bytes)
+ raw_sources = "".join(sources.modules)
+
+ # The sources are expected to be ASCII-only.
+ assert not filter(lambda value: ord(value) >= 128, raw_sources)
+
+ # Loop over modules and build up indices into the source blob:
+ get_index_cases = []
+ get_script_name_cases = []
+ get_raw_script_source_cases = []
+ offset = 0
+ for i in xrange(len(sources.modules)):
+ native_name = "native %s.js" % sources.names[i]
+ d = {
+ "i": i,
+ "id": sources.names[i],
+ "name": native_name,
+ "length": len(native_name),
+ "offset": offset,
+ "raw_length": len(sources.modules[i]),
+ }
+ get_index_cases.append(GET_INDEX_CASE % d)
+ get_script_name_cases.append(GET_SCRIPT_NAME_CASE % d)
+ get_raw_script_source_cases.append(GET_RAW_SCRIPT_SOURCE_CASE % d)
+ offset += len(sources.modules[i])
+ assert offset == len(raw_sources)
+
+ # If we have the raw sources we can declare them accordingly.
+ have_raw_sources = source_bytes == raw_sources and not omit
+ raw_sources_declaration = (RAW_SOURCES_DECLARATION
+ if have_raw_sources else RAW_SOURCES_COMPRESSION_DECLARATION)
+
+ metadata = {
+ "builtin_count": len(sources.modules),
+ "debugger_count": sum(sources.is_debugger_id),
+ "sources_declaration": SOURCES_DECLARATION % ToCArray(source_bytes),
+ "sources_data": ToCArray(source_bytes) if not omit else "",
+ "raw_sources_declaration": raw_sources_declaration,
+ "raw_total_length": sum(map(len, sources.modules)),
+ "total_length": total_length,
+ "get_index_cases": "".join(get_index_cases),
+ "get_raw_script_source_cases": "".join(get_raw_script_source_cases),
+ "get_script_name_cases": "".join(get_script_name_cases),
+ "type": native_type,
+ }
+ return metadata
+
+
+def CompressMaybe(sources, compression_type):
+ """Take the prepared sources and generate a sequence of bytes.
+
+ Args:
+ sources: A Sources instance with the prepared sourced.
+ compression_type: string, describing the desired compression.
+
+ Returns:
+ A sequence of bytes.
+ """
+ sources_bytes = "".join(sources.modules)
+ if compression_type == "off":
+ return sources_bytes
+ elif compression_type == "bz2":
+ return bz2.compress(sources_bytes)
else:
- raw_sources_declaration = RAW_SOURCES_COMPRESSION_DECLARATION
- if env['COMPRESSION'] == 'bz2':
- all_sources = bz2.compress("".join(all_sources))
- total_length = len(all_sources)
- sources_data = ToCArray(all_sources)
-
- # Build debugger support functions
- get_index_cases = [ ]
- get_raw_script_source_cases = [ ]
- get_script_name_cases = [ ]
-
- i = 0
- for (id, raw_length, module_offset) in debugger_ids + ids:
- native_name = "native %s.js" % id
- get_index_cases.append(GET_INDEX_CASE % { 'id': id, 'i': i })
- get_raw_script_source_cases.append(GET_RAW_SCRIPT_SOURCE_CASE % {
- 'offset': module_offset,
- 'raw_length': raw_length,
- 'i': i
- })
- get_script_name_cases.append(GET_SCRIPT_NAME_CASE % {
- 'name': native_name,
- 'length': len(native_name),
- 'i': i
- })
- i = i + 1
-
- # Emit result
- output = open(str(target[0]), "w")
- output.write(HEADER_TEMPLATE % {
- 'builtin_count': len(ids) + len(debugger_ids),
- 'debugger_count': len(debugger_ids),
- 'sources_data': sources_data,
- 'raw_sources_declaration': raw_sources_declaration,
- 'raw_total_length': raw_total_length,
- 'total_length': total_length,
- 'get_index_cases': "".join(get_index_cases),
- 'get_raw_script_source_cases': "".join(get_raw_script_source_cases),
- 'get_script_name_cases': "".join(get_script_name_cases),
- 'type': env['TYPE']
- })
+ raise Error("Unknown compression type %s." % compression_type)
+
+
+def JS2C(source, target, native_type, compression_type, raw_file, omit):
+ sources = PrepareSources(source)
+ sources_bytes = CompressMaybe(sources, compression_type)
+ metadata = BuildMetadata(sources, sources_bytes, native_type, omit)
+
+ # Optionally emit raw file.
+ if raw_file:
+ output = open(raw_file, "w")
+ output.write(sources_bytes)
+ output.close()
+
+ # Emit resulting source file.
+ output = open(target, "w")
+ output.write(HEADER_TEMPLATE % metadata)
output.close()
+
def main():
- natives = sys.argv[1]
- type = sys.argv[2]
- compression = sys.argv[3]
- source_files = sys.argv[4:]
- JS2C(source_files, [natives], { 'TYPE': type, 'COMPRESSION': compression })
+ parser = optparse.OptionParser()
+ parser.add_option("--raw", action="store",
+ help="file to write the processed sources array to.")
+ parser.add_option("--omit", dest="omit", action="store_true",
+ help="Omit the raw sources from the generated code.")
+ parser.set_usage("""js2c out.cc type compression sources.js ...
+ out.cc: C code to be generated.
+ type: type parameter for NativesCollection template.
+ compression: type of compression used. [off|bz2]
+ sources.js: JS internal sources or macros.py.""")
+ (options, args) = parser.parse_args()
+
+ JS2C(args[3:], args[0], args[1], args[2], options.raw, options.omit)
+
if __name__ == "__main__":
main()
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698