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