145 lines
5.9 KiB
Python
145 lines
5.9 KiB
Python
from disass import disassemble
|
|
|
|
import compile
|
|
from objspace import ObjectSpace
|
|
|
|
|
|
class ByteCodeError(Exception):
|
|
pass
|
|
|
|
|
|
class Interpreter(object):
|
|
|
|
def __init__(self, builtincode=None):
|
|
# Using an instance variable to keep the public interface
|
|
self.space = ObjectSpace(self)
|
|
self.space.setup_builtins(builtincode)
|
|
|
|
def eval(self, ast, w_context):
|
|
code = compile.compile(ast)
|
|
return self.run(code, w_context)
|
|
|
|
def read4(self, code, pc):
|
|
""" Converts 4 unicode characters to single 4 byte value """
|
|
highval = ord(code[pc + 3]) # most significant byte
|
|
if highval >= 128: # convert from 2's complement?
|
|
highval -= 256
|
|
return (ord(code[pc]) | # merge single bytes into 4 byte value
|
|
(ord(code[pc + 1]) << 8) |
|
|
(ord(code[pc + 2]) << 16) |
|
|
(highval << 24))
|
|
|
|
def run(self, bytecode, w_context):
|
|
pc = 0
|
|
stack = []
|
|
code = bytecode.code
|
|
# print(disassemble(bytecode))
|
|
while pc < len(code):
|
|
opcode = ord(code[pc]) # convert unicode to number
|
|
pc += 1
|
|
if compile.isjump(opcode):
|
|
oparg = self.read4(code, pc)
|
|
pc += 4
|
|
if opcode == compile.JUMP:
|
|
pc += oparg
|
|
elif opcode == compile.JUMP_IF_FALSE:
|
|
w_condition = stack.pop()
|
|
if self.space.isfalse(w_condition):
|
|
pc += oparg
|
|
elif compile.hasarg(opcode):
|
|
oparg = ord(code[pc])
|
|
pc += 1
|
|
if oparg >= 128:
|
|
if oparg > 128:
|
|
oparg -= 256
|
|
else:
|
|
oparg = self.read4(code, pc)
|
|
pc += 4
|
|
if opcode == compile.MAKE_OBJECT:
|
|
name = bytecode.symbols[oparg]
|
|
obj = self.space.newobject(name, {'__parent__': w_context}, [])
|
|
stack.append(obj)
|
|
elif opcode == compile.MAKE_OBJECT_CALL:
|
|
self.run(bytecode.subbytecodes[oparg], stack[-1])
|
|
|
|
# Project -----
|
|
elif opcode == compile.INT_LITERAL:
|
|
w_value = self.space.newint(oparg)
|
|
stack.append(w_value)
|
|
elif opcode == compile.BOOL_LITERAL:
|
|
w_value = self.space.newbool(oparg) # oparg is 1 or 0
|
|
stack.append(w_value)
|
|
elif opcode == compile.STRING_LITERAL:
|
|
value = bytecode.symbols[oparg]
|
|
w_value = self.space.newstring(value)
|
|
stack.append(w_value)
|
|
elif opcode == compile.DOUBLE_LITERAL:
|
|
value = bytecode.symbols[oparg]
|
|
w_value = self.space.newdouble(value)
|
|
stack.append(w_value)
|
|
# -------------
|
|
|
|
elif opcode == compile.MAKE_FUNCTION:
|
|
bc = bytecode.subbytecodes[oparg]
|
|
w_method = self.space.definemethod(name=bc.name, code=bc, w_target=w_context)
|
|
stack.append(w_method)
|
|
elif opcode == compile.METHOD_LOOKUP:
|
|
name = bytecode.symbols[oparg]
|
|
w_method = self.space.getvalue(stack[-1], name)
|
|
stack.append(w_method)
|
|
elif opcode == compile.METHOD_CALL:
|
|
arguments_w = [stack.pop() for _ in range(oparg)]
|
|
arguments_w.reverse()
|
|
#
|
|
w_method = stack.pop()
|
|
w_receiver = stack.pop()
|
|
w_result = self.space.call(w_method, w_receiver, arguments_w)
|
|
stack.append(w_result)
|
|
elif opcode == compile.PRIMITIVE_METHOD_CALL:
|
|
nargs = self.space.get_number_of_arguments_of_primitive(oparg)
|
|
arguments_w = [stack.pop() for _ in range(nargs)]
|
|
arguments_w.reverse()
|
|
w_receiver = stack.pop()
|
|
w_result = self.space.call_primitive(oparg, w_receiver, arguments_w)
|
|
stack.append(w_result)
|
|
elif opcode == compile.SET_LOCAL:
|
|
w_value = stack[-1]
|
|
name = bytecode.symbols[oparg]
|
|
self.space.setvalue(w_context, name, w_value)
|
|
elif opcode == compile.ASSIGNMENT:
|
|
w_value = stack.pop()
|
|
name = bytecode.symbols[oparg]
|
|
self.space.setvalue(stack[-1], name, w_value)
|
|
elif opcode == compile.ASSIGNMENT_APPEND_PARENT:
|
|
w_value = stack.pop()
|
|
name = bytecode.symbols[oparg]
|
|
self.space.setvalue(stack[-1], name, w_value)
|
|
self.space.addparent(stack[-1], name)
|
|
elif opcode == compile.GET_LOCAL:
|
|
name = bytecode.symbols[oparg]
|
|
w_value = self.space.getvalue(w_context, name)
|
|
w_value = self.space.call(w_value, w_context, [])
|
|
stack.append(w_value)
|
|
else:
|
|
raise ByteCodeError('Invalid bytecode with arguments')
|
|
else:
|
|
if opcode == compile.POP:
|
|
stack.pop()
|
|
elif opcode == compile.IMPLICIT_SELF:
|
|
stack.append(w_context)
|
|
elif opcode == compile.DUP:
|
|
stack.append(stack[-1])
|
|
|
|
# Project
|
|
elif opcode == compile.GC:
|
|
self.space.gc(w_context)
|
|
|
|
else:
|
|
raise ByteCodeError('Invalid bytecode')
|
|
assert pc == len(code)
|
|
assert len(stack) == 1
|
|
return stack.pop()
|
|
|
|
def make_module(self):
|
|
return self.space.make_module()
|