1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5Copyright (c) 2021 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: Use ark to execute js files 19""" 20 21import argparse 22import os 23import platform 24import sys 25import signal 26import re 27import fileinput 28import subprocess 29from utils import * 30from config import * 31 32 33def parse_args(): 34 parser = argparse.ArgumentParser() 35 parser.add_argument('--ark-tool', 36 default=DEFAULT_ARK_TOOL, 37 required=False, 38 help="ark's binary tool") 39 parser.add_argument('--ark-aot', action='store_true', 40 required=False, 41 help="Run test262 with aot") 42 parser.add_argument('--ark-aot-tool', 43 default=DEFAULT_ARK_AOT_TOOL, 44 required=False, 45 help="ark's aot tool") 46 parser.add_argument("--libs-dir", 47 default=DEFAULT_LIBS_DIR, 48 required=False, 49 help="The path collection of dependent so has been divided by':'") 50 parser.add_argument("--js-file", 51 required=True, 52 help="js file") 53 parser.add_argument('--ark-frontend', 54 default=DEFAULT_ARK_FRONTEND, 55 required=False, 56 nargs='?', choices=ARK_FRONTEND_LIST, type=str, 57 help="Choose one of them") 58 parser.add_argument('--ark-frontend-binary', 59 default=DEFAULT_ARK_FRONTEND_BINARY, 60 required=False, 61 help="ark frontend conversion binary tool") 62 parser.add_argument('--ark-arch', 63 default=DEFAULT_ARK_ARCH, 64 required=False, 65 nargs='?', choices=ARK_ARCH_LIST, type=str, 66 help="Choose one of them") 67 parser.add_argument('--ark-arch-root', 68 default=DEFAULT_ARK_ARCH, 69 required=False, 70 help="the root path for qemu-aarch64 or qemu-arm") 71 parser.add_argument('--opt-level', 72 default=DEFAULT_OPT_LEVEL, 73 required=False, 74 help="the opt level for es2abc") 75 parser.add_argument('--es2abc-thread-count', 76 default=DEFAULT_ES2ABC_THREAD_COUNT, 77 required=False, 78 help="the thread count for es2abc") 79 parser.add_argument('--merge-abc-binary', 80 default=DEFAULT_MERGE_ABC_BINARY, 81 required=False, 82 help="frontend merge abc binary tool") 83 parser.add_argument('--merge-abc-mode', 84 default=DEFAULT_MERGE_ABC_MODE, 85 required=False, 86 help="run test for merge abc mode") 87 arguments = parser.parse_args() 88 return arguments 89 90 91ICU_PATH = f"--icu-data-path={CODE_ROOT}/third_party/icu/ohos_icu4j/data" 92if platform.system() == "Windows" : 93 ICU_PATH = ICU_PATH.replace("/","\\") 94ARK_TOOL = DEFAULT_ARK_TOOL 95LIBS_DIR = DEFAULT_LIBS_DIR 96ARK_AOT_TOOL = DEFAULT_ARK_AOT_TOOL 97ARK_FRONTEND = DEFAULT_ARK_FRONTEND 98ARK_FRONTEND_BINARY = DEFAULT_ARK_FRONTEND_BINARY 99ARK_ARCH = DEFAULT_ARK_ARCH 100PROTO_BIN_SUFFIX = "protoBin" 101 102 103def output(retcode, msg): 104 if retcode == 0: 105 if msg != '': 106 print(str(msg)) 107 elif retcode == -6: 108 sys.stderr.write("Aborted (core dumped)") 109 elif retcode == -4: 110 sys.stderr.write("Aborted (core dumped)") 111 elif retcode == -11: 112 sys.stderr.write("Segmentation fault (core dumped)") 113 elif msg != '': 114 sys.stderr.write(str(msg)) 115 else: 116 sys.stderr.write("Unknown Error: " + str(retcode)) 117 118 119def exec_command(cmd_args, timeout=DEFAULT_TIMEOUT): 120 proc = subprocess.Popen(cmd_args, 121 stderr=subprocess.PIPE, 122 stdout=subprocess.PIPE, 123 close_fds=True, 124 start_new_session=True) 125 cmd_string = " ".join(cmd_args) 126 code_format = 'utf-8' 127 if platform.system() == "Windows": 128 code_format = 'gbk' 129 130 try: 131 (output_res, errs) = proc.communicate(timeout=timeout) 132 ret_code = proc.poll() 133 134 if errs.decode(code_format, 'ignore') != '': 135 output(1, errs.decode(code_format, 'ignore')) 136 return 1 137 138 if ret_code and ret_code != 1: 139 code = ret_code 140 msg = f"Command {cmd_string}: \n" 141 msg += f"error: {str(errs.decode(code_format,'ignore'))}" 142 else: 143 code = 0 144 msg = str(output_res.decode(code_format, 'ignore')) 145 146 except subprocess.TimeoutExpired: 147 proc.kill() 148 proc.terminate() 149 os.kill(proc.pid, signal.SIGTERM) 150 code = 1 151 msg = f"Timeout:'{cmd_string}' timed out after' {str(timeout)} seconds" 152 except Exception as err: 153 code = 1 154 msg = f"{cmd_string}: unknown error: {str(err)}" 155 output(code, msg) 156 return code 157 158def print_command(cmd_args): 159 sys.stderr.write("\n") 160 sys.stderr.write(" ".join(cmd_args)) 161 sys.stderr.write("\n") 162 163# for debug use, to keep aot file 164def run_command(cmd_args): 165 return subprocess.run(" ".join(cmd_args)) 166 167class ArkProgram(): 168 def __init__(self, args): 169 self.args = args 170 self.ark_tool = ARK_TOOL 171 self.ark_aot = False 172 self.ark_aot_tool = ARK_AOT_TOOL 173 self.libs_dir = LIBS_DIR 174 self.ark_frontend = ARK_FRONTEND 175 self.ark_frontend_binary = ARK_FRONTEND_BINARY 176 self.module_list = [] 177 self.dynamicImport_list = [] 178 self.js_file = "" 179 self.module = False 180 self.abc_file = "" 181 self.arch = ARK_ARCH 182 self.arch_root = "" 183 self.opt_level = DEFAULT_OPT_LEVEL 184 self.es2abc_thread_count = DEFAULT_ES2ABC_THREAD_COUNT 185 self.merge_abc_binary = DEFAULT_MERGE_ABC_BINARY 186 self.merge_abc_mode = DEFAULT_MERGE_ABC_MODE 187 188 def proce_parameters(self): 189 if self.args.ark_tool: 190 self.ark_tool = self.args.ark_tool 191 192 if self.args.ark_aot: 193 self.ark_aot = self.args.ark_aot 194 195 if self.args.ark_aot_tool: 196 self.ark_aot_tool = self.args.ark_aot_tool 197 198 if self.args.ark_frontend_binary: 199 self.ark_frontend_binary = self.args.ark_frontend_binary 200 201 if self.args.libs_dir: 202 self.libs_dir = self.args.libs_dir 203 204 if self.args.ark_frontend: 205 self.ark_frontend = self.args.ark_frontend 206 207 if self.args.opt_level: 208 self.opt_level = self.args.opt_level 209 210 if self.args.es2abc_thread_count: 211 self.es2abc_thread_count = self.args.es2abc_thread_count 212 213 if self.args.merge_abc_binary: 214 self.merge_abc_binary = self.args.merge_abc_binary 215 216 if self.args.merge_abc_mode: 217 self.merge_abc_mode = self.args.merge_abc_mode 218 219 self.module_list = MODULE_LIST 220 221 self.dynamicImport_list = DYNAMIC_IMPORT_LIST 222 223 self.js_file = self.args.js_file 224 225 self.arch = self.args.ark_arch 226 227 self.arch_root = self.args.ark_arch_root 228 229 def gen_dependency_abc(self, dependency): 230 cmd_args = [] 231 output_file = os.path.splitext(os.path.join(BASE_OUT_DIR, 232 os.path.split(dependency)[1]))[0] 233 output_abc = f"{output_file}.abc" 234 frontend_tool = self.ark_frontend_binary 235 merge_abc_binary = self.args.merge_abc_binary 236 merge_abc_mode = self.merge_abc_mode 237 238 if merge_abc_mode != "0": 239 proto_bin_file = output_file + "." + PROTO_BIN_SUFFIX 240 cmd_args = [frontend_tool, dependency, '--outputProto', 241 proto_bin_file, '--module', '--merge-abc'] 242 else: 243 # for testing no-record-name abc 244 cmd_args = [frontend_tool, dependency, '--output', output_abc, 245 '--module'] 246 proc = subprocess.Popen(cmd_args) 247 proc.wait() 248 249 def gen_merged_abc(self, dependencies, file_name_pre, proto_bin_file, retcode): 250 merge_abc_binary = self.args.merge_abc_binary 251 file_dir = os.path.split(self.js_file)[0] 252 proto_abc_file = ".".join([os.path.splitext(os.path.basename(self.js_file))[0], "abc"]) 253 generate_merged_abc = True 254 # collect protoBin file into new-made testcase dir 255 if (len(dependencies) != 0): 256 if os.path.exists(file_name_pre): 257 subprocess.run(['rm', '-rf', file_name_pre]) 258 subprocess.run(['mkdir', file_name_pre]) 259 260 for dependency in list(set(dependencies)): 261 dependency_file_prefix = os.path.basename(dependency)[:-3] 262 dependency_bin_file = file_dir + "/" + \ 263 ".".join([dependency_file_prefix, 264 PROTO_BIN_SUFFIX]) 265 # test262 report syntax error cases 266 if not os.path.exists(dependency_bin_file): 267 generate_merged_abc = False 268 else: 269 subprocess.run(['cp', dependency_bin_file, file_name_pre]) 270 271 if not os.path.exists(proto_bin_file): 272 generate_merged_abc = False 273 else: 274 subprocess.run(['cp', proto_bin_file, file_name_pre]) 275 276 if (len(dependencies) != 0) and generate_merged_abc: 277 # module test262 cases 278 cmd_args = [merge_abc_binary, '--input', file_name_pre, 279 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 280 file_dir, '--output', proto_abc_file] 281 self.abc_file = f'{file_name_pre}.abc' 282 retcode = exec_command(cmd_args) 283 elif os.path.exists(proto_bin_file): 284 cmd_args = [merge_abc_binary, '--input', proto_bin_file, 285 '--suffix', PROTO_BIN_SUFFIX, '--outputFilePath', 286 file_dir, '--output', proto_abc_file] 287 self.abc_file = f'{file_name_pre}.abc' 288 retcode = exec_command(cmd_args) 289 return retcode 290 291 def gen_abc(self): 292 js_file = self.js_file 293 file_name_pre = os.path.splitext(js_file)[0] 294 file_name = os.path.basename(js_file) 295 out_file = f"{file_name_pre}.abc" 296 proto_bin_file = file_name_pre + "." + PROTO_BIN_SUFFIX 297 self.abc_file = out_file 298 mod_opt_index = 0 299 cmd_args = [] 300 dependency_cmd_args = [] 301 frontend_tool = self.ark_frontend_binary 302 merge_abc_mode = self.merge_abc_mode 303 dependencies = [] 304 305 # pre-generate the dependencies' abc when ark_frontend is [es2panda] 306 if (file_name in self.module_list or file_name in self.dynamicImport_list): 307 search_dir = "language/module-code" if file_name in self.module_list \ 308 else "language/expressions/dynamic-import" 309 dependencies = collect_module_dependencies(js_file, os.path.join(TEST_FULL_DIR, search_dir), []) 310 if (self.ark_frontend == ARK_FRONTEND_LIST[1]): 311 for dependency in list(set(dependencies)): 312 self.gen_dependency_abc(dependency) 313 314 if self.ark_frontend == ARK_FRONTEND_LIST[0]: 315 mod_opt_index = 3 316 if merge_abc_mode != "0": 317 cmd_args = ['node', '--expose-gc', frontend_tool, js_file, 318 '--output-proto', '--merge-abc'] 319 else: 320 # for testing no-record-name abc 321 cmd_args = ['node', '--expose-gc', frontend_tool, 322 js_file, '-o', out_file] 323 if file_name in self.module_list: 324 cmd_args.insert(mod_opt_index, "-m") 325 self.module = True 326 elif self.ark_frontend == ARK_FRONTEND_LIST[1]: 327 mod_opt_index = 1 328 if merge_abc_mode != "0": 329 # '--merge-abc' is added due to 'merge-abc' is not opened as default in es2abc, should be removed later 330 cmd_args = [frontend_tool, '--function-threads=' + 331 str(self.es2abc_thread_count), '--outputProto', 332 proto_bin_file, js_file, '--merge-abc', '--opt-level=' + str(self.opt_level)] 333 else: 334 # for testing no-record-name abc 335 cmd_args = [frontend_tool, '--opt-level=' + str(self.opt_level), 336 '--function-threads=' + 337 str(self.es2abc_thread_count), '--output', 338 out_file, js_file] 339 if file_name in self.module_list: 340 cmd_args.insert(mod_opt_index, "--module") 341 self.module = True 342 # get abc file list from import statement 343 if merge_abc_mode == "0" and self.ark_aot and self.module: 344 self.abc_file = os.path.abspath(out_file) 345 js_dir = os.path.dirname(js_file) 346 for line in fileinput.input(js_file): 347 import_line = re.findall(r"^(?:ex|im)port.*\.js", line) 348 if len(import_line): 349 import_file = re.findall(r"['\"].*\.js", import_line[0]) 350 if len(import_file): 351 abc_file = import_file[0][1:].replace(".js", ".abc") 352 abc_file = os.path.abspath(f'{js_dir}/{abc_file}') 353 if self.abc_file.find(abc_file) < 0: 354 self.abc_file += f':{abc_file}' 355 356 retcode = exec_command(cmd_args) 357 self.abc_cmd = cmd_args 358 359 if merge_abc_mode != "0": 360 retcode = self.gen_merged_abc(dependencies, file_name_pre, 361 proto_bin_file, retcode) 362 363 return retcode 364 365 def compile_aot(self): 366 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 367 file_name_pre = os.path.splitext(self.js_file)[0] 368 cmd_args = [] 369 if self.arch == ARK_ARCH_LIST[1]: 370 cmd_args = [self.ark_aot_tool, ICU_PATH, 371 f'--target-triple=aarch64-unknown-linux-gnu', 372 f'--aot-file={file_name_pre}', 373 self.abc_file] 374 elif self.arch == ARK_ARCH_LIST[2]: 375 cmd_args = [self.ark_aot_tool, ICU_PATH, 376 f'--target-triple=arm-unknown-linux-gnu', 377 f'--aot-file={file_name_pre}', 378 self.abc_file] 379 elif self.arch == ARK_ARCH_LIST[0]: 380 cmd_args = [self.ark_aot_tool, ICU_PATH, 381 f'--aot-file={file_name_pre}', 382 self.abc_file] 383 retcode = exec_command(cmd_args, 180000) 384 if retcode: 385 print_command(self.abc_cmd) 386 print_command(cmd_args) 387 388 def execute_aot(self): 389 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 390 file_name_pre = os.path.splitext(self.js_file)[0] 391 cmd_args = [] 392 if self.arch == ARK_ARCH_LIST[1]: 393 qemu_tool = "qemu-aarch64" 394 qemu_arg1 = "-L" 395 qemu_arg2 = self.arch_root 396 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 397 ICU_PATH, 398 f'--aot-file={file_name_pre}', 399 f'{file_name_pre}.abc'] 400 elif self.arch == ARK_ARCH_LIST[2]: 401 qemu_tool = "qemu-arm" 402 qemu_arg1 = "-L" 403 qemu_arg2 = self.arch_root 404 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 405 ICU_PATH, 406 f'--aot-file={file_name_pre}', 407 f'{file_name_pre}.abc'] 408 elif self.arch == ARK_ARCH_LIST[0]: 409 cmd_args = [self.ark_tool, ICU_PATH, 410 f'--aot-file={file_name_pre}', 411 f'{file_name_pre}.abc'] 412 413 record_name = os.path.splitext(os.path.split(self.js_file)[1])[0] 414 cmd_args.insert(-1, f'--entry-point={record_name}') 415 retcode = exec_command(cmd_args) 416 if retcode: 417 print_command(cmd_args) 418 return retcode 419 420 def execute(self): 421 if platform.system() == "Windows" : 422 #add env path for cmd/powershell execute 423 libs_dir = self.libs_dir.replace(":", ";") 424 libs_dir = libs_dir.replace("/", "\\") 425 os.environ["PATH"] = libs_dir + ";" + os.environ["PATH"] 426 elif platform.system() == "Linux" : 427 os.environ["LD_LIBRARY_PATH"] = self.libs_dir 428 else : 429 sys.exit(f" test262 on {platform.system()} not supported"); 430 file_name_pre = os.path.splitext(self.js_file)[0] 431 cmd_args = [] 432 if self.arch == ARK_ARCH_LIST[1]: 433 qemu_tool = "qemu-aarch64" 434 qemu_arg1 = "-L" 435 qemu_arg2 = self.arch_root 436 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 437 ICU_PATH, 438 f'{file_name_pre}.abc'] 439 elif self.arch == ARK_ARCH_LIST[2]: 440 qemu_tool = "qemu-arm" 441 qemu_arg1 = "-L" 442 qemu_arg2 = self.arch_root 443 cmd_args = [qemu_tool, qemu_arg1, qemu_arg2, self.ark_tool, 444 ICU_PATH, 445 f'{file_name_pre}.abc'] 446 elif self.arch == ARK_ARCH_LIST[0]: 447 cmd_args = [self.ark_tool, ICU_PATH, 448 f'{file_name_pre}.abc'] 449 450 record_name = os.path.splitext(os.path.split(self.js_file)[1])[0] 451 cmd_args.insert(-1, f'--entry-point={record_name}') 452 retcode = exec_command(cmd_args) 453 if retcode: 454 print_command(cmd_args) 455 return retcode 456 457 def is_legal_frontend(self): 458 if self.ark_frontend not in ARK_FRONTEND_LIST: 459 sys.stderr.write("Wrong ark front-end option") 460 return False 461 return True 462 463 def execute_ark(self): 464 self.proce_parameters() 465 if not self.is_legal_frontend(): 466 return 467 if self.gen_abc(): 468 return 469 if self.ark_aot: 470 self.compile_aot() 471 self.execute_aot() 472 else: 473 self.execute() 474 475def main(): 476 args = parse_args() 477 478 ark = ArkProgram(args) 479 ark.execute_ark() 480 481 482if __name__ == "__main__": 483 sys.exit(main()) 484