11cb0ef41Sopenharmony_ci#!/usr/bin/env python
21cb0ef41Sopenharmony_ci#
31cb0ef41Sopenharmony_ci# Copyright 2006-2008 the V8 project authors. All rights reserved.
41cb0ef41Sopenharmony_ci# Redistribution and use in source and binary forms, with or without
51cb0ef41Sopenharmony_ci# modification, are permitted provided that the following conditions are
61cb0ef41Sopenharmony_ci# met:
71cb0ef41Sopenharmony_ci#
81cb0ef41Sopenharmony_ci#     * Redistributions of source code must retain the above copyright
91cb0ef41Sopenharmony_ci#       notice, this list of conditions and the following disclaimer.
101cb0ef41Sopenharmony_ci#     * Redistributions in binary form must reproduce the above
111cb0ef41Sopenharmony_ci#       copyright notice, this list of conditions and the following
121cb0ef41Sopenharmony_ci#       disclaimer in the documentation and/or other materials provided
131cb0ef41Sopenharmony_ci#       with the distribution.
141cb0ef41Sopenharmony_ci#     * Neither the name of Google Inc. nor the names of its
151cb0ef41Sopenharmony_ci#       contributors may be used to endorse or promote products derived
161cb0ef41Sopenharmony_ci#       from this software without specific prior written permission.
171cb0ef41Sopenharmony_ci#
181cb0ef41Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
191cb0ef41Sopenharmony_ci# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
201cb0ef41Sopenharmony_ci# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
211cb0ef41Sopenharmony_ci# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
221cb0ef41Sopenharmony_ci# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
231cb0ef41Sopenharmony_ci# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
241cb0ef41Sopenharmony_ci# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
251cb0ef41Sopenharmony_ci# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
261cb0ef41Sopenharmony_ci# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
271cb0ef41Sopenharmony_ci# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
281cb0ef41Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci"""
311cb0ef41Sopenharmony_ciThis is a utility for converting JavaScript source code into uint16_t[],
321cb0ef41Sopenharmony_cithat are used for embedding JavaScript code into the Node.js binary.
331cb0ef41Sopenharmony_ci"""
341cb0ef41Sopenharmony_ciimport argparse
351cb0ef41Sopenharmony_ciimport os
361cb0ef41Sopenharmony_ciimport re
371cb0ef41Sopenharmony_ciimport functools
381cb0ef41Sopenharmony_ciimport codecs
391cb0ef41Sopenharmony_ciimport utils
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_cidef ReadFile(filename):
421cb0ef41Sopenharmony_ci  if is_verbose:
431cb0ef41Sopenharmony_ci    print(filename)
441cb0ef41Sopenharmony_ci  with codecs.open(filename, "r", "utf-8") as f:
451cb0ef41Sopenharmony_ci    lines = f.read()
461cb0ef41Sopenharmony_ci    return lines
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ciTEMPLATE = """
501cb0ef41Sopenharmony_ci#include "env-inl.h"
511cb0ef41Sopenharmony_ci#include "node_builtins.h"
521cb0ef41Sopenharmony_ci#include "node_internals.h"
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_cinamespace node {{
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_cinamespace builtins {{
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci{0}
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_cinamespace {{
611cb0ef41Sopenharmony_ciconst ThreadsafeCopyOnWrite<BuiltinSourceMap> global_source_map {{
621cb0ef41Sopenharmony_ci  BuiltinSourceMap{{ {1} }}
631cb0ef41Sopenharmony_ci}};
641cb0ef41Sopenharmony_ci}}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_civoid BuiltinLoader::LoadJavaScriptSource() {{
671cb0ef41Sopenharmony_ci  source_ = global_source_map;
681cb0ef41Sopenharmony_ci}}
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ciUnionBytes BuiltinLoader::GetConfig() {{
711cb0ef41Sopenharmony_ci  return UnionBytes(config_raw, {2});  // config.gypi
721cb0ef41Sopenharmony_ci}}
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci}}  // namespace builtins
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci}}  // namespace node
771cb0ef41Sopenharmony_ci"""
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ciONE_BYTE_STRING = """
801cb0ef41Sopenharmony_cistatic const uint8_t {0}[] = {{
811cb0ef41Sopenharmony_ci{1}
821cb0ef41Sopenharmony_ci}};
831cb0ef41Sopenharmony_ci"""
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ciTWO_BYTE_STRING = """
861cb0ef41Sopenharmony_cistatic const uint16_t {0}[] = {{
871cb0ef41Sopenharmony_ci{1}
881cb0ef41Sopenharmony_ci}};
891cb0ef41Sopenharmony_ci"""
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ciINITIALIZER = '{{"{0}", UnionBytes{{{1}, {2}}} }},'
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ciCONFIG_GYPI_ID = 'config_raw'
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ciSLUGGER_RE = re.compile(r'[.\-/]')
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ciis_verbose = False
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_cidef GetDefinition(var, source, step=30):
1001cb0ef41Sopenharmony_ci  template = ONE_BYTE_STRING
1011cb0ef41Sopenharmony_ci  code_points = [ord(c) for c in source]
1021cb0ef41Sopenharmony_ci  if any(c > 127 for c in code_points):
1031cb0ef41Sopenharmony_ci    template = TWO_BYTE_STRING
1041cb0ef41Sopenharmony_ci    # Treat non-ASCII as UTF-8 and encode as UTF-16 Little Endian.
1051cb0ef41Sopenharmony_ci    encoded_source = bytearray(source, 'utf-16le')
1061cb0ef41Sopenharmony_ci    code_points = [
1071cb0ef41Sopenharmony_ci      encoded_source[i] + (encoded_source[i + 1] * 256)
1081cb0ef41Sopenharmony_ci      for i in range(0, len(encoded_source), 2)
1091cb0ef41Sopenharmony_ci    ]
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci  # For easier debugging, align to the common 3 char for code-points.
1121cb0ef41Sopenharmony_ci  elements_s = ['%3s' % x for x in code_points]
1131cb0ef41Sopenharmony_ci  # Put no more then `step` code-points in a line.
1141cb0ef41Sopenharmony_ci  slices = [elements_s[i:i + step] for i in range(0, len(elements_s), step)]
1151cb0ef41Sopenharmony_ci  lines = [','.join(s) for s in slices]
1161cb0ef41Sopenharmony_ci  array_content = ',\n'.join(lines)
1171cb0ef41Sopenharmony_ci  definition = template.format(var, array_content)
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  return definition, len(code_points)
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_cidef AddModule(filename, definitions, initializers):
1231cb0ef41Sopenharmony_ci  code = ReadFile(filename)
1241cb0ef41Sopenharmony_ci  name = NormalizeFileName(filename)
1251cb0ef41Sopenharmony_ci  slug = SLUGGER_RE.sub('_', name)
1261cb0ef41Sopenharmony_ci  var = slug + '_raw'
1271cb0ef41Sopenharmony_ci  definition, size = GetDefinition(var, code)
1281cb0ef41Sopenharmony_ci  initializer = INITIALIZER.format(name, var, size)
1291cb0ef41Sopenharmony_ci  definitions.append(definition)
1301cb0ef41Sopenharmony_ci  initializers.append(initializer)
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_cidef NormalizeFileName(filename):
1331cb0ef41Sopenharmony_ci  split = filename.split('/')
1341cb0ef41Sopenharmony_ci  if split[0] == 'deps':
1351cb0ef41Sopenharmony_ci    split = ['internal'] + split
1361cb0ef41Sopenharmony_ci  else:  # `lib/**/*.js` so drop the 'lib' part
1371cb0ef41Sopenharmony_ci    split = split[1:]
1381cb0ef41Sopenharmony_ci  if len(split):
1391cb0ef41Sopenharmony_ci    filename = '/'.join(split)
1401cb0ef41Sopenharmony_ci  return os.path.splitext(filename)[0]
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_cidef JS2C(source_files, target):
1441cb0ef41Sopenharmony_ci  # Build source code lines
1451cb0ef41Sopenharmony_ci  definitions = []
1461cb0ef41Sopenharmony_ci  initializers = []
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  for filename in source_files['.js']:
1491cb0ef41Sopenharmony_ci    AddModule(filename, definitions, initializers)
1501cb0ef41Sopenharmony_ci  for filename in source_files['.mjs']:
1511cb0ef41Sopenharmony_ci    AddModule(filename, definitions, initializers)
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  config_def, config_size = handle_config_gypi(source_files['config.gypi'])
1541cb0ef41Sopenharmony_ci  definitions.append(config_def)
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci  # Emit result
1571cb0ef41Sopenharmony_ci  definitions = ''.join(definitions)
1581cb0ef41Sopenharmony_ci  initializers = '\n  '.join(initializers)
1591cb0ef41Sopenharmony_ci  out = TEMPLATE.format(definitions, initializers, config_size)
1601cb0ef41Sopenharmony_ci  write_if_chaged(out, target)
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_cidef handle_config_gypi(config_filename):
1641cb0ef41Sopenharmony_ci  # if its a gypi file we're going to want it as json
1651cb0ef41Sopenharmony_ci  # later on anyway, so get it out of the way now
1661cb0ef41Sopenharmony_ci  config = ReadFile(config_filename)
1671cb0ef41Sopenharmony_ci  config = jsonify(config)
1681cb0ef41Sopenharmony_ci  config_def, config_size = GetDefinition(CONFIG_GYPI_ID, config)
1691cb0ef41Sopenharmony_ci  return config_def, config_size
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_cidef jsonify(config):
1731cb0ef41Sopenharmony_ci  # 1. string comments
1741cb0ef41Sopenharmony_ci  config = re.sub(r'#.*?\n', '', config)
1751cb0ef41Sopenharmony_ci  # 2. join multiline strings
1761cb0ef41Sopenharmony_ci  config = re.sub(r"'$\s+'", '', config, flags=re.M)
1771cb0ef41Sopenharmony_ci  # 3. normalize string literals from ' into "
1781cb0ef41Sopenharmony_ci  config = re.sub('\'', '"', config)
1791cb0ef41Sopenharmony_ci  # 4. turn pseudo-booleans strings into Booleans
1801cb0ef41Sopenharmony_ci  config = re.sub('"true"', 'true', config)
1811cb0ef41Sopenharmony_ci  config = re.sub('"false"', 'false', config)
1821cb0ef41Sopenharmony_ci  return config
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_cidef write_if_chaged(content, target):
1861cb0ef41Sopenharmony_ci  if os.path.exists(target):
1871cb0ef41Sopenharmony_ci    with open(target, 'rt') as existing:
1881cb0ef41Sopenharmony_ci      old_content = existing.read()
1891cb0ef41Sopenharmony_ci  else:
1901cb0ef41Sopenharmony_ci    old_content = ''
1911cb0ef41Sopenharmony_ci  if old_content == content:
1921cb0ef41Sopenharmony_ci    os.utime(target, None)
1931cb0ef41Sopenharmony_ci    return
1941cb0ef41Sopenharmony_ci  with open(target, "wt") as output:
1951cb0ef41Sopenharmony_ci    output.write(content)
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_cidef SourceFileByExt(files_by_ext, filename):
1991cb0ef41Sopenharmony_ci  """
2001cb0ef41Sopenharmony_ci  :type files_by_ext: dict
2011cb0ef41Sopenharmony_ci  :type filename: str
2021cb0ef41Sopenharmony_ci  :rtype: dict
2031cb0ef41Sopenharmony_ci  """
2041cb0ef41Sopenharmony_ci  ext = os.path.splitext(filename)[-1]
2051cb0ef41Sopenharmony_ci  files_by_ext.setdefault(ext, []).append(filename)
2061cb0ef41Sopenharmony_ci  return files_by_ext
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_cidef main():
2091cb0ef41Sopenharmony_ci  parser = argparse.ArgumentParser(
2101cb0ef41Sopenharmony_ci    description='Convert code files into `uint16_t[]`s',
2111cb0ef41Sopenharmony_ci    fromfile_prefix_chars='@'
2121cb0ef41Sopenharmony_ci  )
2131cb0ef41Sopenharmony_ci  parser.add_argument('--target', help='output file')
2141cb0ef41Sopenharmony_ci  parser.add_argument(
2151cb0ef41Sopenharmony_ci      '--directory',
2161cb0ef41Sopenharmony_ci      default=None,
2171cb0ef41Sopenharmony_ci      help='input file directory')
2181cb0ef41Sopenharmony_ci  parser.add_argument(
2191cb0ef41Sopenharmony_ci      '--root',
2201cb0ef41Sopenharmony_ci      default=None,
2211cb0ef41Sopenharmony_ci      help='root directory containing the sources')
2221cb0ef41Sopenharmony_ci  parser.add_argument('--verbose', action='store_true', help='output file')
2231cb0ef41Sopenharmony_ci  parser.add_argument('sources', nargs='*', help='input files')
2241cb0ef41Sopenharmony_ci  options = parser.parse_args()
2251cb0ef41Sopenharmony_ci  global is_verbose
2261cb0ef41Sopenharmony_ci  is_verbose = options.verbose
2271cb0ef41Sopenharmony_ci  sources = options.sources
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci  if options.root is not None:
2301cb0ef41Sopenharmony_ci    os.chdir(options.root)
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci  if options.directory is not None:
2331cb0ef41Sopenharmony_ci    js_files = utils.SearchFiles(options.directory, 'js')
2341cb0ef41Sopenharmony_ci    mjs_files = utils.SearchFiles(options.directory, 'mjs')
2351cb0ef41Sopenharmony_ci    sources = js_files + mjs_files + options.sources
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci  source_files = functools.reduce(SourceFileByExt, sources, {})
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci  # Should have exactly 3 types: `.js`, `.mjs` and `.gypi`
2401cb0ef41Sopenharmony_ci  assert len(source_files) == 3
2411cb0ef41Sopenharmony_ci  # Currently config.gypi is the only `.gypi` file allowed
2421cb0ef41Sopenharmony_ci  assert len(source_files['.gypi']) == 1
2431cb0ef41Sopenharmony_ci  assert os.path.basename(source_files['.gypi'][0]) == 'config.gypi'
2441cb0ef41Sopenharmony_ci  source_files['config.gypi'] = source_files.pop('.gypi')[0]
2451cb0ef41Sopenharmony_ci  JS2C(source_files, options.target)
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ciif __name__ == "__main__":
2491cb0ef41Sopenharmony_ci  main()
250