# Twisted, the Framework of Your Internet
# Copyright (C) 2001 Matthew W. Lefkowitz
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
This is the beginnings of a Python virtual machine in Python.
"""
import dis
import types
cmp_op = (
'LESS', #0, <
'LESS_OR_EQUAL', #1, <=
'EQUAL', #2, ==
'NOT_EQUAL', #3, != <>
'GREATER', #4, >
'GREATER_OR_EQUAL', #5, >=
'IN', #6, in
'NOT_IN', #7, not in
'IS', #8, is
'IS_NOT', #9, is not
'EXCEPTION_MATCH', #10, exception match
'BAD', #11
)
TRUE = 1
FALSE = 0
class Object:
"""Each of these operators will return a frame.
If they can calculate the return value immediately, they will push the
return onto the frame; otherwise they'll return a different frame, that
will return the correct value when executed.
"""
def __init__(self, value):
self.check(value)
self.value = value
def operator_pos(self, frame):
frame.push(+self.value)
return frame
def operator_neg(self, frame):
frame.push(-self.value)
return frame
def operator_getattr(self, attr, frame):
return frame
def operator_getitem(self, item, frame):
self.push(wrap(self.value[item]))
return frame
def operator_setitem(self, item, value, frame):
self.value[item] = value
return frame
def operator_add(self, other, frame):
if isinstance(other, self.__class__):
frame.push(self.value + other.value)
return frame
def operator_call(self, args, kw, frame):
frame.push(None)
return frame
def become_boolean(self, frame):
if self.value:
frame.push(TRUE)
else:
frame.push(FALSE)
return frame
def become_string(self, frame):
frame.push(TRUE)
return frame
class Number(Object):
pass
class Int(Number):
def check(self, value):
assert type(value) == types.IntType
class Float(Number):
def check(self, value):
assert type(value) == types.FloatType
class Long(Number):
def check(self, value):
assert type(value) == types.LongType
class String(Object):
def check(self, value):
assert type(value) == types.StringType
class Tuple(Object):
def check(self, value):
assert type(value) == types.TupleType
class List(Object):
def check(self, value):
assert type(value) == types.ListType
def safe(func, globals):
return Function(Code(func.func_code), func.func_defaults, globals)
class Function(Object):
def __init__(self, code, defaults, globals):
self.func_code = code
self.func_doc = code.co_consts[0]
self.func_defaults = defaults
self.func_globals = globals
def operator_call(self, args, kw, old):
argmap = {}
if len(args) > self.func_code.co_argcount:
print args
assert 0, 'too many args'
for argn in range(len(args)):
argmap[self.func_code.co_varnames[argn]] = args[argn]
# TODO: assertions about redefinition
for k, v in kw.values():
assert isinstance(k, String), "varnames must be strings!!"
argmap[k.value] = v
if len(argmap) != self.func_code.co_argcount:
assert 0, 'not enough args; needed %s got %s' % (self.func_code.co_argcount, len(argmap))
new = Frame(self.func_code, old, old.f_builtins, argmap, self.func_globals, old.interp)
return new
class Class(Object):
def __init__(self, dict, bases, name):
self.dict = dict
self.bases = bases
self.name = name
def operator_call(self, args, kw, frame):
inst = Instance(self)
f = Function(Code(instance_create.func_code), (), {})
newargs = (self, inst, args, kw)
newkw = {}
return f.operator_call(newargs, newkw, frame)
def operator_getattr(self, attr, frame):
if self.dict.has_key(attr):
obj = self.dict[attr]
if isinstance(obj, Function):
obj = Method(self, None, obj)
frame.push(obj)
return frame
obj = None
if attr == '__dict__':
obj = self.dict
elif attr == '__bases__':
obj = self.bases
elif attr == '__name__':
obj = self.name
if obj is not None:
frame.push(wrap(obj))
return frame
if self.dict.has_key(attr):
if isinstance(self.dict[attr], Function):
return Method(self, None, self.dict[attr])
assert 0, "attribute error"
class Method(Object):
def __init__(self, im_class, im_self, im_func):
self.im_class = im_class
self.im_self = im_self
self.im_func = im_func
def operator_call(self, args, kw, frame):
if self.im_self is not None:
return self.im_func.operator_call((self.im_self,)+args, kw, frame)
else:
## if not (isinstance(args[0], Instance) and args[0].classobj == self.im_self):
## raise something
return self.im_func.operator_call(args, kw, frame)
class Instance(Object):
def __init__(self, classobj):
self.classobj = classobj
def operator_getattr(self, attr, frame):
assert isinstance(attr, String)
class Code(Object):
"""This is a simulated code object.
It is constructed from a real Python code object.
"""
def check(self, obj):
assert type(obj) == types.CodeType
def __init__(self, codeobj):
Object.__init__(self, codeobj)
code = self.co_code = codeobj.co_code
self.orig = codeobj
self.co_consts = codeobj.co_consts
self.co_names = codeobj.co_names
self.co_varnames = codeobj.co_varnames
self.co_argcount = codeobj.co_argcount
self.opcodes = []
self.jumps = {}
i = 0
n = len(code)
while i < n:
start = i
# eat one byte
c = code[i]
i = i + 1
op = ord(c)
name = dis.opname[op]
arg = None
if op >= dis.HAVE_ARGUMENT:
# eat two bytes.
if name == 'CALL_FUNCTION':
arg = (ord(code[i]), ord(code[i+1]))
else:
oparg = ord(code[i]) + (ord(code[i+1])*256)
if op in dis.hasconst:
arg = self.co_consts[oparg]
elif op in dis.hasname:
arg = self.co_names[oparg]
elif op in dis.hasjrel:
arg = oparg
elif op in dis.haslocal:
arg = self.co_varnames[oparg]
elif op in dis.hascompare:
arg = cmp_op[oparg]
else:
arg = oparg
i = i + 2
self.jumps[start] = len(self.opcodes)
self.opcodes.append((start, name, arg))
class Block:
def __init__(self, frame, typ, handler, stacklevel):
self.frame = frame
self.type = type
self.handler = handler
self.stacklevel = stacklevel
class Frame(Object):
def __init__(self, code, back, builtins, locals, globals, interp):
self.lineno = 0
self.interp = interp
self.f_code = code
# exception state
self.f_exc_traceback = None
self.f_exc_type = None
self.f_exc_value = None
# variable bindings
self.f_locals = locals
self.f_globals = globals
self.f_builtins = builtins
# exception state or sys.settrace?
self.f_trace = None
# back pointer to previous stack frame
self.f_back = back
# last instruction executed (don't know if this is necessary...)
self.f_lasti = 0
# last bytecode sequence executed (offset into self.f_code.)
self.f_lastb = 0
# wtf? python sure has some obscure corners...
self.f_restricted = 1
self.stack = []
self.blockstack = []
self.push = self.stack.append
self.pop = self.stack.pop
def op_POP_TOP(self, nothing):
# without arg
self.pop()
return 1, self
def op_ROT_TWO(self, nothing):
v = self.pop()
w = self.pop()
self.push(v)
self.push(w)
return 1, self
def op_ROT_THREE(self, nothing):
v = self.pop()
w = self.pop()
x = self.pop()
self.push(v)
self.push(w)
self.push(x)
return 1, self
def op_MAKE_FUNCTION(self, argc):
defargs = []
code = self.pop()
for x in range(argc):
defargs.insert(self.pop(),0)
func = Function(code, defargs, self.f_globals)
self.push(func)
return 1, self
def op_SETUP_EXCEPT(self, exceptdest):
b = Block(self, 'except', self.f_lasti + exceptdest+3, len(self.stack))
self.blockstack.append(b)
return 1, self
def op_SETUP_FINALLY(self, exceptdest):
b = Block(self, 'finally', self.f_lasti + exceptdest+3, len(self.stack))
self.blockstack.append(b)
return 1, self
def op_SETUP_LOOP(self, exceptdest):
b = Block(self, 'loop', self.f_lasti + exceptdest+3, len(self.stack))
self.blockstack.append(b)
return 1, self
def op_POP_BLOCK(self, none):
self.blockstack.pop()
return 1, self
def op_END_FINALLY(self, none):
print 'op end finally!'
return 1, self
def op_CALL_FUNCTION(self, (posparam, varparam)):
args = []
kw = {}
for each in range(varparam):
value = self.pop()
key = self.pop()
kw[key]=value
for each in range(posparam):
args.append(self.pop())
func = self.pop()
nf = func.operator_call(args, kw, self)
return 1, nf
def op_LOAD_ATTR(self, name):
obj = self.pop()
nf = obj.operator_getattr(name, self)
return 1, nf
def op_ROT_FOUR(self, nothing):
u = self.pop();
v = self.pop();
w = self.pop();
x = self.pop();
self.push(u);
self.push(x);
self.push(w);
self.push(v);
return 1, self
def op_DUP_TOP(self, nothing):
v = self.stack[-1]
self.push(v)
return 1, self
def op_STORE_FAST(self, name):
self.f_locals[name] = self.pop()
return 1, self
op_STORE_NAME = op_STORE_FAST
def op_LOAD_FAST(self, name):
self.push(self.f_locals[name])
return 1, self
op_LOAD_NAME = op_LOAD_FAST
def op_DUP_TOPX(self, arg):
assert arg >= 1
assert arg <= 5
map(self.stack.append, self.stack[-arg:])
return 1, self
def op_LOAD_LOCALS(self, noarg):
self.stack.append(self.f_locals)
return 1, self
def op_LOAD_GLOBAL(self, arg):
self.push(self.f_globals[arg])
return 1, self
def op_BUILD_CLASS(self, noarg):
dict = self.pop()
bases = self.pop()
name = self.pop()
cl = Class(dict, bases, name)
self.push(cl)
return 1, self
def op_UNARY_POSITIVE(self, noarg):
v = self.pop()
return 1, v.operator_pos(self)
def op_UNARY_NEGATIVE(self, noarg):
v = self.pop()
return 1, v.operator_neg(self)
def op_UNARY_NOT(self, noarg):
v = self.pop()
return 1, v.operator_not(self)
def op_JUMP_IF_TRUE(self, fwd, t=TRUE, f=FALSE):
v = self.stack[-1]
if v is f:
return 1, self
elif v is t:
# DO THE JUMP
return self.op_JUMP_FORWARD(fwd)
else:
# take v off the stack
self.pop()
# 'cause this will put it back.
return 0, v.become_boolean(self)
def op_JUMP_FORWARD(self, fwd):
JF_CODE_SIZE = 3
self.f_lasti = self.f_lasti + fwd + JF_CODE_SIZE
self.f_lastb = self.f_code.jumps[self.f_lasti]
return 0, self
def op_JUMP_IF_FALSE(self, fwd):
return self.op_JUMP_IF_TRUE(fwd, FALSE, TRUE)
def op_BINARY_ADD(self, noarg):
TOS = self.pop()
TOS1 = self.pop()
# XXX TOS1 + TOS
print TOS, TOS1
self.push(TOS1.operator_add(TOS, self))
return 1, self
def op_LOAD_CONST(self, const):
self.push(wrap(const))
return 1, self
def op_BUILD_TUPLE(self, count):
tobe = []
for ig in range(count):
tobe.append(self.pop())
self.push(wrap(tuple(tobe)))
return 1, self
def op_SET_LINENO(self, lineno):
self.lineno = lineno
return 1, self
def op_PRINT_ITEM(self, noarg):
TOS = self.pop()
print "\t"+repr(TOS.value)
return 1, self
def op_PRINT_NEWLINE(self, noarg):
return 1, self
def op_RETURN_VALUE(self, noarg):
retval = self.pop()
if self.f_back is not None:
self.f_back.push(retval)
else:
self.interp.gotret = 1
self.interp.retval = retval
return 0, self.f_back
def work(self):
opcode = self.f_code.opcodes[self.f_lastb]
start, name, arg = opcode
print self
print self.stack
print "%d %s %s" % (start,name,arg)
proc = getattr(self, "op_%s" % name)
advance, frame = proc(arg)
if advance:
self.f_lastb = self.f_lastb + 1
self.f_lasti = start
return frame
class Interpreter:
def __init__(self, code, builtins, globals):
self.initial = Frame(code, None, builtins, globals, globals, self)
self.frame = self.initial
self.gotret = 0
def timeslice(self):
self.frame = self.frame.work()
return self.gotret
def run(self):
while not self.timeslice():
pass
return self.retval
_wrapmap = {
types.IntType: Int,
types.StringType: String,
types.FloatType: Float,
types.LongType: Long,
types.CodeType: Code,
types.ListType: List,
types.TupleType: Tuple,
}
def wrap(obj):
if obj is None:
return None
return _wrapmap[type(obj)](obj)
### operator overloading crap.
def instance_create(klass, inst, args, kw):
try:
func = klass.__init__
except AttributeError:
return inst
else:
func((inst,)+args, kw)
def instance_add(a, b):
try:
aadd = a.__add__
except:
try:
badd = b.__radd__
except:
raise TypeError("__add__ nor __radd__ defined for these operands")
else:
return badd(a)
else:
return aadd(b)
if __name__ == '__main__':
def rawthunk(arg):
print 'called thunk with', arg
def rawclasstest():
class foo:
x = 1
f = foo()
def dostuff(self):
## n = 7
## thunk(1)
## classtest()
## if n:
## print 'hi'
## else:
## print 'bye'
class foo:
x = 1
def __init__(self):
print self
f = foo()
return 1
dis.dis(dostuff)
c = Code(dostuff.func_code)
i = Interpreter(c, {},
{'thunk':safe(rawthunk,{}),
'classtest':safe(rawclasstest,{})}
)
print i.run().value
syntax highlighted by Code2HTML, v. 0.9.1