1#!/usr/bin/env python
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 cStringIO 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 %d opcodes in Interp.h (expected %d)" \
41        % (len(opcodes), NUM_PACKED_OPCODES)
42    raise SyntaxError, "bad opcode count"
43  return opcodes
44
45indent_re = re.compile(r"^%( *)")
46
47# Finds variable references in text: $foo or ${foo}
48escape_re = re.compile(r'''
49  (?<!\$)        # Look-back: must not be preceded by another $.
50  \$
51  (\{)?          # May be enclosed by { } pair.
52  (?P<name>\w+)  # Save the symbol in named group.
53  (?(1)\})       # Expect } if and only if { was present.
54''', re.VERBOSE)
55
56def generate_script(output_filename, input_filenames):
57  # Create new python script and write the initial setup code.
58  script = StringIO()  # File-like in-memory buffer.
59  script.write("# DO NOT EDIT: This file was generated by gen-mterp.py.\n")
60  script.write(open(SCRIPT_SETUP_CODE, "r").read())
61  script.write("def opcodes():\n")
62  for i, opcode in enumerate(getOpcodeList()):
63    script.write('  write_opcode({0}, "{1}", {1})\n'.format(i, opcode))
64
65  # Read all template files and translate them into python code.
66  for input_filename in sorted(input_filenames):
67    lines = open(input_filename, "r").readlines()
68    indent = ""
69    for line in lines:
70      line = line.rstrip()
71      if line.startswith("%"):
72        script.write(line.lstrip("%") + "\n")
73        indent = indent_re.match(line).group(1)
74        if line.endswith(":"):
75          indent += "  "
76      else:
77        line = escape_re.sub(r"''' + \g<name> + '''", line)
78        line = line.replace("\\", "\\\\")
79        line = line.replace("$$", "$")
80        script.write(indent + "write_line('''" + line + "''')\n")
81    script.write("\n")
82
83  script.write("generate('''" + output_filename + "''')\n")
84  script.seek(0)
85  return script.read()
86
87if len(sys.argv) <= 3:
88  print("Usage: output_file input_file(s)")
89  sys.exit(1)
90
91# Generate the script and execute it.
92output_filename = sys.argv[1]
93input_filenames = sys.argv[2:]
94script_filename = output_filename + ".py"
95script = generate_script(output_filename, input_filenames)
96with open(script_filename, "w") as script_file:
97  script_file.write(script)  # Write to disk for debugging.
98exec(compile(script, script_filename, mode='exec'))
99