• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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