1#!/usr/bin/env python3
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import sys, re, os
18from io import StringIO
19
20SCRIPT_DIR = os.path.dirname(sys.argv[0])
21# This file is included verbatim at the start of the in-memory python script.
22SCRIPT_SETUP_CODE = SCRIPT_DIR + "/common/gen_setup.py"
23INTERP_DEFS_FILE = SCRIPT_DIR + "/../../../libdexfile/dex/dex_instruction_list.h"
24NUM_PACKED_OPCODES = 256
25
26# Extract an ordered list of instructions from the VM sources.  We use the
27# "goto table" definition macro, which has exactly NUM_PACKED_OPCODES entries.
28def getOpcodeList():
29  opcodes = []
30  opcode_fp = open(INTERP_DEFS_FILE)
31  opcode_re = re.compile(r"^\s*V\((....), (\w+),.*", re.DOTALL)
32  for line in opcode_fp:
33    match = opcode_re.match(line)
34    if not match:
35      continue
36    opcodes.append("op_" + match.group(2).lower())
37  opcode_fp.close()
38
39  if len(opcodes) != NUM_PACKED_OPCODES:
40    print("ERROR: found ", len(opcodes), " opcodes in Interp.h (expected ", NUM_PACKED_OPCODES, ")")
41    raise SyntaxError("bad opcode count")
42  return opcodes
43
44indent_re = re.compile(r"^%( *)")
45
46# Finds variable references in text: $foo or ${foo}
47escape_re = re.compile(r'''
48  (?<!\$)        # Look-back: must not be preceded by another $.
49  \$
50  (\{)?          # May be enclosed by { } pair.
51  (?P<name>\w+)  # Save the symbol in named group.
52  (?(1)\})       # Expect } if and only if { was present.
53''', re.VERBOSE)
54
55def generate_script(output_filename, input_filenames):
56  # Create new python script and write the initial setup code.
57  script = StringIO()  # File-like in-memory buffer.
58  script.write("# DO NOT EDIT: This file was generated by gen-mterp.py.\n")
59  script.write(open(SCRIPT_SETUP_CODE, "r").read())
60  script.write("def opcodes():\n")
61  for i, opcode in enumerate(getOpcodeList()):
62    script.write('  write_opcode({0}, "{1}", {1})\n'.format(i, opcode))
63
64  # Read all template files and translate them into python code.
65  for input_filename in sorted(input_filenames):
66    lines = open(input_filename, "r").readlines()
67    indent = ""
68    for line in lines:
69      line = line.rstrip()
70      if line.startswith("%"):
71        script.write(line.lstrip("%") + "\n")
72        indent = indent_re.match(line).group(1)
73        if line.endswith(":"):
74          indent += "  "
75      else:
76        line = escape_re.sub(r"''' + \g<name> + '''", line)
77        line = line.replace("\\", "\\\\")
78        line = line.replace("$$", "$")
79        script.write(indent + "write_line('''" + line + "''')\n")
80    script.write("\n")
81
82  script.write("generate('''" + output_filename + "''')\n")
83  script.seek(0)
84  return script.read()
85
86if len(sys.argv) <= 3:
87  print("Usage: output_file input_file(s)")
88  sys.exit(1)
89
90# Generate the script and execute it.
91output_filename = sys.argv[1]
92input_filenames = sys.argv[2:]
93script_filename = output_filename + ".py"
94script = generate_script(output_filename, input_filenames)
95with open(script_filename, "w") as script_file:
96  script_file.write(script)  # Write to disk for debugging.
97exec(compile(script, script_filename, mode='exec'))
98