-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathregenByteCode.py
executable file
·289 lines (251 loc) · 12.2 KB
/
regenByteCode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#!/usr/bin/env python
#-------------------------------------------------------------------------------------------------------
# Copyright (C) Microsoft. All rights reserved.
# Copyright (c) ChakraCore Project Contributors. All rights reserved.
# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
#-------------------------------------------------------------------------------------------------------
# Regenerate embedded bytecode headers. This script must be run when:
# a) Ahy changes have been made to the javascript in lib/Runtime/Library/InJavascript
# b) Any changes have been made to the bytecode emission process including edits to JnDirectFields.h
# NOTEs:
# 1. this script relies on forcing 64bit CC builds to produce 32bit bytecode - this could break due to future
# changes to CC. If this facility breaks it will need to be fixed or this script will need to be updated to
# use 32 bit builds as well as 64 bit
# 2. Options:
# --skip-build Don't build CC just generate bytecode with already built binaries
# --jit only generate bytecode for CC with jit (default is to do both jit and noJit)
# --noJit only generate bytecode for CC with noJit (default is to do both jit and noJit)
# --verify throw an error if bytecode changes detected - intended for use in CI
# --binary=path provide a path to a binary to use, requires either --jit or --noJit to be set
# --x86 specify that provided binary is an x86 build, will generate x86 bytecode only - requires pre-built binary
# 3. Python version - this script is designed to run on both Python 2.x and 3.x
import subprocess
import sys
import uuid
import os
# Parse provided parameters
verification_mode = False
skip_build = False
noJit = True
jit = True
x86 = False
overide_binary = ""
for param in sys.argv:
if param == '--skip-build':
skip_build = True
elif param == '--verify':
# welcome message for CI
print('######### Verifying generated bytecode #########')
verification_mode = True
elif param == '--noJit':
jit = False
elif param == '--jit':
noJit = False
elif param == '--x86':
x86 = True
elif param[:9] == '--binary=':
overide_binary = param[9:]
skip_build = True
# Detect OS
windows = False
if os.name == 'posix':
print('OS is Linux or macOS')
else:
print('OS is Windows')
windows = True
# If Jit or noJit flag has been give print a message also if both flags given revert to default behaviour
if jit == False:
if noJit == True:
print('Regenerating bytecode for no-jit build only')
else:
noJit = True
jit = True
elif noJit == False:
print('Regenerating bytecode for jit build only')
if x86 == True:
if overide_binary == "":
print('x86 build can only be used when pre-built and provided with the --binary command line parameter')
sys.exit(1)
# Adjust path for running from different locations
base_path = os.path.abspath(os.path.dirname(__file__))
# Compile ChakraCore both noJit and Jit variants (unless disabled by args)
def run_sub(message, commands, error):
print(message)
sub = subprocess.Popen(commands, shell=windows)
sub.wait()
if sub.returncode != 0:
print(error)
sys.exit(1)
if skip_build == False:
# build for linux or macOS with build.sh script - could update to use cmake directly but this works for now
if windows == False:
if noJit == True:
run_sub('Compiling ChakraCore with no Jit',
[base_path + '/../build.sh', '--no-jit', '--debug', '--static', '--target-path=' + base_path + '/../out/noJit', '-j=8'],
'No Jit build failed - aborting bytecode generation')
if jit == True:
run_sub('Compiling ChakraCore with Jit',
[base_path + '/../build.sh', '--debug', '--static', '--target-path=' + base_path + '/../out/Jit', '-j=8'],
'Jit build failed - aborting bytecode generation')
# build for windows
else:
if noJit == True:
run_sub('Compiling ChakraCore with no Jit',
['msbuild', '/P:platform=x64', '/P:configuration=debug', '/M', '/p:BuildJIT=false', base_path+ '/../Build/Chakra.Core.sln'],
'No Jit build failed - aborting bytecode generation')
if jit == True:
run_sub('Compiling ChakraCore with Jit',
['msbuild', '/P:platform=x64', '/P:configuration=debug', '/M', base_path + '/../Build/Chakra.Core.sln'],
'No Jit build failed - aborting bytecode generation')
# Generate the new bytecode checking for changes to each file
# First define variables and methods that will be used then the calls take place below
changes_detected = False
# this header text will be placed at the top of the generated files
header_text = '''//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// Generated Bytecode Header, this file was created by tools/regenByteCode.py
// This file contains:
// a) bytecode for Intl library methods implemented in javascript and
// b) bytecode for other Js library methods, JsBuiltIns, implemented in javascript
#define JsBuiltIns(VALUE)'''
def append_bytecode(header, command, in_path, file_name, error):
command_with_file = command[:]
command_with_file.append(in_path + file_name)
header.write('//Bytecode generated from ' + file_name + '\nconst char Library_Bytecode_')
header.write(file_name[:-3])
header.flush()
job = subprocess.Popen(command_with_file, stdout=header)
job.wait()
if job.returncode != 0:
print(error)
print('Command line: ')
print(*command_with_file)
sys.exit(1)
# Load file and ensure line endings are '\n' if on windows
def load_file(path, mode):
global windows
if windows == True:
if sys.version_info[0] < 3:
return open(path, mode + 'b')
else:
return open(path, mode, newline='\n')
else:
return open(path, mode)
# Regenerate the bytecode
def bytecode_job(out_path, command, in_path, error):
if verification_mode == True:
print('Checking bytecode in file ' + out_path)
else:
print('Generating bytecode in file ' + out_path)
old_version = ''
global changes_detected
if changes_detected == False:
old_version = load_file(out_path, 'r').read()
header = load_file(out_path, 'w')
header.write(header_text)
files = os.listdir(in_path)
files.sort()
filtered_files = []
for file_name in files:
if file_name.endswith('.js'):
if file_name != 'Intl.js':
without_extension = file_name[:-3]
parts = without_extension.split('_')
header.write(' \\\nVALUE(' + parts[0] + ', ' + parts[1] + ', ' + parts[0] + parts[1].title() + ')')
filtered_files.append(file_name)
header.write('\n\nnamespace js\n{\n\n#ifdef ENABLE_JS_BUILTINS\n\n')
# generate bytecode for JsBuiltins
command_with_chakra_lib = command[:]
command_with_chakra_lib.append('-LdChakraLib')
command_with_chakra_lib.append('-JsBuiltIn')
for file_name in filtered_files:
append_bytecode(header, command_with_chakra_lib, in_path, file_name, error)
# generate bytecode for Intl
command.append('-Intl')
header.write('#endif\n\n#ifdef ENABLE_INTL_OBJECT\n\n')
append_bytecode(header, command, in_path, 'Intl.js', error)
header.write('#endif\n\n}\n')
header.close()
if changes_detected == False:
new_version = load_file(out_path, 'r').read()
if new_version != old_version:
changes_detected = True
if verification_mode == True:
new_lines = new_version.split('\n')
old_lines = old_version.split('\n')
max_lines = min(len(new_lines), len(old_lines))
for i in range(0, max_lines):
if new_lines[i] != old_lines[i]:
print('Error found - output on line ' + str(i + 1) + ' is:')
print(new_lines[i].replace('\r', '\\r'))
print('Expected output was:')
print(old_lines[i].replace('\r', '\\r'))
break
# set paths for binaries - default paths based on build seteps above (different for windows to macOS and linux)
# OR overridden path provided on command line
noJitpath = base_path + "/../out/noJit/debug/ch"
jitPath = base_path + "/../out/jit/debug/ch"
if overide_binary != "":
noJitpath = overide_binary
jitPath = overide_binary
if jit == True and noJit == True:
print("Cannot use override binary option without specifying either jit or noJit")
sys.exit(1)
elif windows == True:
noJitpath = base_path + '/../Build/VcBuild.NoJIT/bin/x64_debug/ch.exe'
jitPath = base_path + '/../Build/VcBuild/bin/x64_debug/ch.exe'
# Call the functions above to generate the bytecode
if noJit == True:
commands = [noJitpath, '-GenerateLibraryByteCodeHeader']
if x86 == False:
bytecode_job(base_path + '/../lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.64b.h',
commands, base_path + '/../lib/Runtime/Library/InJavascript/',
'Failed to generate noJit 64bit Bytecode')
commands.append('-Force32BitByteCode')
bytecode_job(base_path + '/../lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.32b.h',
commands, base_path + '/../lib/Runtime/Library/InJavascript/',
'Failed to generate noJit 32bit JsBuiltin Bytecode')
if jit == True:
commands = [jitPath, '-GenerateLibraryByteCodeHeader']
if x86 == False:
bytecode_job(base_path + '/../lib/Runtime/Library/InJavascript/JsBuiltIn.bc.64b.h',
commands, base_path + '/../lib/Runtime/Library/InJavascript/',
'Failed to generate 64bit JsBuiltin Bytecode')
commands.append('-Force32BitByteCode')
bytecode_job(base_path + '/../lib/Runtime/Library/InJavascript/JsBuiltIn.bc.32b.h',
commands, base_path + '/../lib/Runtime/Library/InJavascript/',
'Failed to generate 32bit JsBuiltin Bytecode')
# Bytecode regeneration complete - assess changes, report result AND if appropriate generate a new GUID
if changes_detected == True:
if verification_mode == True:
print('Bytecode changes detected - the generated bytecode files are not up to date please run tools/regenByteCode.py\n')
sys.exit(1)
if jit == False or noJit == False:
print("Bytecode updated for one variant only - ensure you re-run for both variants before submitting code")
else:
print('Generating new GUID for new bytecode')
guid_header = load_file(base_path + '/../lib/Runtime/Bytecode/ByteCodeCacheReleaseFileVersion.h', 'w')
guid = str(uuid.uuid4())
output_str = '''//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// NOTE: If there is a merge conflict the correct fix is to make a new GUID.
// This file was generated with tools/regenByteCode.py
// {%s}
const GUID byteCodeCacheReleaseFileVersion =
{ 0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s } };
''' % (guid,
guid[:8], guid[9:13], guid[14:18], guid[19:21], guid[21:23], guid[24:26],
guid[26:28], guid[28:30], guid[30:32], guid[32:34], guid[-2:])
guid_header.write(output_str)
print('Bytecode successfully regenerated. Please rebuild ChakraCore to incorporate it.')
else:
if verification_mode == True:
print('Bytecode is up to date\n')
else:
print('Bytecode update was not required')