embedjs.py (7130B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 # 5 # ToCAsciiArray and ToCArray are from V8's js2c.py. 6 # 7 # Copyright 2012 the V8 project authors. All rights reserved. 8 # Redistribution and use in source and binary forms, with or without 9 # modification, are permitted provided that the following conditions are 10 # met: 11 # 12 # * Redistributions of source code must retain the above copyright 13 # notice, this list of conditions and the following disclaimer. 14 # * Redistributions in binary form must reproduce the above 15 # copyright notice, this list of conditions and the following 16 # disclaimer in the documentation and/or other materials provided 17 # with the distribution. 18 # * Neither the name of Google Inc. nor the names of its 19 # contributors may be used to endorse or promote products derived 20 # from this software without specific prior written permission. 21 # 22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 34 # This utility converts JS files containing self-hosted builtins into a C 35 # header file that can be embedded into SpiderMonkey. 36 # 37 # It uses the C preprocessor to process its inputs. 38 39 import errno 40 import os 41 import re 42 import shlex 43 import subprocess 44 import sys 45 46 import buildconfig 47 import mozpack.path as mozpath 48 from mozfile import which 49 50 51 def ToCAsciiArray(lines): 52 result = [] 53 for chr in lines: 54 value = ord(chr) 55 assert value < 128 56 result.append(str(value)) 57 return ", ".join(result) 58 59 60 def ToCArray(lines): 61 result = [] 62 for chr in lines: 63 result.append(str(chr)) 64 return ", ".join(result) 65 66 67 HEADER_TEMPLATE = """\ 68 /* This Source Code Form is subject to the terms of the Mozilla Public 69 * License, v. 2.0. If a copy of the MPL was not distributed with this 70 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 71 72 namespace js { 73 namespace %(namespace)s { 74 static const %(sources_type)s data[] = { %(sources_data)s }; 75 76 static const %(sources_type)s * const %(sources_name)s = reinterpret_cast<const %(sources_type)s *>(data); 77 78 uint32_t GetCompressedSize() { 79 return %(compressed_total_length)i; 80 } 81 82 uint32_t GetRawScriptsSize() { 83 return %(raw_total_length)i; 84 } 85 } // selfhosted 86 } // js 87 """ # NOQA: E501 88 89 90 def embed( 91 cxx, preprocessorOption, cppflags, msgs, sources, c_out, js_out, namespace, env 92 ): 93 objdir = os.getcwd() 94 # Use relative pathnames to avoid path translation issues in WSL. 95 combinedSources = "\n".join( 96 [msgs] 97 + [ 98 '#include "%(s)s"' % {"s": mozpath.relpath(source, objdir)} 99 for source in sources 100 ] 101 ) 102 args = cppflags + ["-D%(k)s=%(v)s" % {"k": k, "v": env[k]} for k in env] 103 preprocessed = preprocess(cxx, preprocessorOption, combinedSources, args) 104 processed = "\n".join([ 105 line 106 for line in preprocessed.splitlines() 107 if (line.strip() and not line.startswith("#")) 108 ]) 109 110 js_out.write(processed) 111 import zlib 112 113 compressed = zlib.compress(processed.encode("utf-8")) 114 data = ToCArray(compressed) 115 c_out.write( 116 HEADER_TEMPLATE 117 % { 118 "sources_type": "unsigned char", 119 "sources_data": data, 120 "sources_name": "compressedSources", 121 "compressed_total_length": len(compressed), 122 "raw_total_length": len(processed), 123 "namespace": namespace, 124 } 125 ) 126 127 128 def preprocess(cxx, preprocessorOption, source, args=[]): 129 if not os.path.exists(cxx[0]): 130 binary = cxx[0] 131 cxx[0] = which(binary) 132 if not cxx[0]: 133 raise OSError(errno.ENOENT, "%s not found on PATH" % binary) 134 135 # Clang seems to complain and not output anything if the extension of the 136 # input is not something it recognizes, so just fake a .cpp here. 137 tmpIn = "self-hosting-cpp-input.cpp" 138 tmpOut = "self-hosting-preprocessed.pp" 139 outputArg = shlex.split(preprocessorOption + tmpOut) 140 141 with open(tmpIn, "wb") as input: 142 input.write(source.encode("utf-8")) 143 144 if os.environ.get("BUILD_VERBOSE_LOG"): 145 print("Executing:", " ".join(cxx + outputArg + args + [tmpIn])) 146 result = subprocess.Popen(cxx + outputArg + args + [tmpIn]).wait() 147 if result != 0: 148 sys.exit(result) 149 with open(tmpOut) as output: 150 processed = output.read() 151 os.remove(tmpIn) 152 os.remove(tmpOut) 153 return processed 154 155 156 def messages(jsmsg): 157 defines = [] 158 for line in open(jsmsg): 159 match = re.match(r"MSG_DEF\((JSMSG_(\w+))", line) 160 if match: 161 defines.append("#define %s %i" % (match.group(1), len(defines))) 162 continue 163 164 # Make sure that MSG_DEF isn't preceded by whitespace 165 assert not line.strip().startswith("MSG_DEF") 166 167 # This script doesn't support preprocessor 168 assert not line.strip().startswith("#") 169 return "\n".join(defines) 170 171 172 def get_config_defines(buildconfig): 173 # Collect defines equivalent to ACDEFINES and add MOZ_DEBUG_DEFINES. 174 env = buildconfig.defines["ALLDEFINES"] 175 for define in buildconfig.substs["MOZ_DEBUG_DEFINES"]: 176 env[define] = 1 177 return env 178 179 180 def process_inputs(namespace, c_out, msg_file, inputs): 181 deps = [path for path in inputs if path.endswith(".h") or path.endswith(".h.js")] 182 sources = [ 183 path for path in inputs if path.endswith(".js") and not path.endswith(".h.js") 184 ] 185 assert len(deps) + len(sources) == len(inputs) 186 cxx = shlex.split(buildconfig.substs["CXX"]) 187 pp_option = buildconfig.substs["PREPROCESS_OPTION"] 188 cppflags = buildconfig.substs["OS_CPPFLAGS"] 189 cppflags += shlex.split(buildconfig.substs["WARNINGS_AS_ERRORS"]) 190 env = get_config_defines(buildconfig) 191 js_path = re.sub(r"\.out\.h$", "", c_out.name) + ".js" 192 msgs = messages(msg_file) 193 with open(js_path, "w") as js_out: 194 embed(cxx, pp_option, cppflags, msgs, sources, c_out, js_out, namespace, env) 195 196 197 def generate_selfhosted(c_out, msg_file, *inputs): 198 # Called from moz.build to embed selfhosted JS. 199 process_inputs("selfhosted", c_out, msg_file, inputs) 200 201 202 def generate_shellmoduleloader(c_out, msg_file, *inputs): 203 # Called from moz.build to embed shell module loader JS. 204 process_inputs("moduleloader", c_out, msg_file, inputs)