[pypy-svn] r34023 - in pypy/dist/pypy/translator: cli js jvm jvm/src jvm/test oosupport
niko at codespeak.net
niko at codespeak.net
Wed Nov 1 14:57:44 CET 2006
Author: niko
Date: Wed Nov 1 14:57:43 2006
New Revision: 34023
Modified:
pypy/dist/pypy/translator/cli/function.py
pypy/dist/pypy/translator/cli/opcodes.py
pypy/dist/pypy/translator/js/function.py
pypy/dist/pypy/translator/jvm/conftest.py
pypy/dist/pypy/translator/jvm/database.py
pypy/dist/pypy/translator/jvm/generator.py
pypy/dist/pypy/translator/jvm/node.py
pypy/dist/pypy/translator/jvm/opcodes.py
pypy/dist/pypy/translator/jvm/option.py
pypy/dist/pypy/translator/jvm/src/PyPy.java
pypy/dist/pypy/translator/jvm/test/ (props changed)
pypy/dist/pypy/translator/jvm/test/runtest.py
pypy/dist/pypy/translator/jvm/typesystem.py
pypy/dist/pypy/translator/oosupport/function.py
pypy/dist/pypy/translator/oosupport/metavm.py
Log:
Update JVM handling of class objects. Add support for casts, new, etc.
Some minor refactoring into oosupport as well.
Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py (original)
+++ pypy/dist/pypy/translator/cli/function.py Wed Nov 1 14:57:43 2006
@@ -99,6 +99,10 @@
# Generator interface
+
+ def add_comment(self, text):
+ pass
+
def function_signature(self, graph, func_name=None):
return self.cts.graph_to_signature(graph, False, func_name)
@@ -138,9 +142,13 @@
signature, virtual = self.cts.method_signature(obj, name)
self.ilasm.call_method(signature, virtual)
- def downcast(self, type):
+ def downcast(self, TYPE):
+ type = self.cts.lltype_to_cts(TYPE)
return self.ilasm.opcode('castclass', type)
+ def instantiate(self):
+ self.call_signature('object [pypylib]pypy.runtime.Utils::RuntimeNew(class [mscorlib]System.Type)')
+
def load(self, v):
if isinstance(v, flowmodel.Variable):
if v.name in self.argset:
Modified: pypy/dist/pypy/translator/cli/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/cli/opcodes.py (original)
+++ pypy/dist/pypy/translator/cli/opcodes.py Wed Nov 1 14:57:43 2006
@@ -1,8 +1,8 @@
-from pypy.translator.cli.metavm import Call, CallMethod, RuntimeNew, \
+from pypy.translator.cli.metavm import Call, CallMethod, \
IndirectCall, GetField, SetField, CastTo, OOString, DownCast, NewCustomDict,\
CastWeakAdrToPtr, MapException, Box, Unbox, GetArrayElem
from pypy.translator.oosupport.metavm import PushArg, PushAllArgs, StoreResult, InstructionList,\
- New
+ New, RuntimeNew
from pypy.translator.cli.cts import WEAKREF
# some useful instruction patterns
Modified: pypy/dist/pypy/translator/js/function.py
==============================================================================
--- pypy/dist/pypy/translator/js/function.py (original)
+++ pypy/dist/pypy/translator/js/function.py Wed Nov 1 14:57:43 2006
@@ -368,6 +368,9 @@
# following methods belongs to the Generator interface
+ def add_comment(self, text):
+ pass
+
def function_signature(self, graph):
return self.cts.graph_to_signature(graph, False)
Modified: pypy/dist/pypy/translator/jvm/conftest.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/conftest.py (original)
+++ pypy/dist/pypy/translator/jvm/conftest.py Wed Nov 1 14:57:43 2006
@@ -14,5 +14,7 @@
Option('--noassemble', action='store_true', dest="noasm", default=False,
help="don't assemble jasmin files"),
Option('--package', action='store', dest='package', default='pypy',
- help='Package to output generated classes into')
+ help='Package to output generated classes into'),
+ Option('--trace', action='store_true', dest='trace', default=False,
+ help='Trace execution of generated code')
)
Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py (original)
+++ pypy/dist/pypy/translator/jvm/database.py Wed Nov 1 14:57:43 2006
@@ -18,7 +18,8 @@
self.genoo = genoo
# Private attributes:
- self._classes = {} # Maps ootype class objects to node.Class objects
+ self._classes = {} # Maps ootype class objects to node.Class objects,
+ # and JvmType objects as well
self._counter = 0 # Used to create unique names
self._functions = {} # graph -> jvmgen.Method
@@ -61,14 +62,26 @@
# Create class object if it does not already exist:
if OOCLASS in self._classes:
return self._classes[OOCLASS]
+
+ # Resolve super class first
+ if OOCLASS._superclass:
+ superclsnm = self.lltype_to_cts(OOCLASS._superclass).class_name()
+ else:
+ superclsobj = "java.lang.Object" #?
+
# TODO --- make package of java class reflect the package of the
# OO class?
clsnm = self._pkg(
self._uniq(OOCLASS._name.replace('.','_')))
- clsobj = node.Class(clsnm)
+ clsobj = node.Class(clsnm, superclsnm)
- # TODO --- mangle field and method names? Must be deterministic or
- # use hashtable to avoid conflicts between classes?
+ # Store the class object for future calls
+ self._classes[OOCLASS] = clsobj
+ self._classes[clsobj.jvm_type()] = clsobj
+
+ # TODO --- mangle field and method names? Must be
+ # deterministic, or use hashtable to avoid conflicts between
+ # classes?
# Add fields:
for fieldnm, (FIELDOOTY, fielddef) in OOCLASS._fields.iteritems():
@@ -86,17 +99,24 @@
# this class it means that this method this method is
# not really used by the class: don't render it, else
# there would be a type mismatch.
- args = m_meth.graph.getargs()
+ args = mimpl.graph.getargs()
SELF = args[0].concretetype
if not ootype.isSubclass(OOCLASS, SELF): continue
mobj = self._function_for_graph(
- clsobj, mimpl.name, False, mimpl.graph)
+ clsobj, mname, False, mimpl.graph)
clsobj.add_method(mobj)
- self._classes[OOCLASS] = clsobj
+ # currently, we always include a special "dump" method for debugging
+ # purposes
+ dump_method = node.TestDumpMethod(self, OOCLASS, clsobj)
+ clsobj.add_dump_method(dump_method)
+
self.pending_node(clsobj)
return clsobj
+ def class_obj_for_jvm_type(self, jvmtype):
+ return self._classes[jvmtype]
+
def pending_function(self, graph):
"""
This is invoked when a standalone function is to be compiled.
@@ -108,7 +128,7 @@
if graph in self._functions:
return self._functions[graph]
classnm = self._pkg(self._uniq(graph.name))
- classobj = node.Class(classnm)
+ classobj = node.Class(classnm, 'java.lang.Object')
funcobj = self._function_for_graph(classobj, "invoke", True, graph)
classobj.add_method(funcobj)
self.pending_node(classobj)
@@ -147,8 +167,23 @@
# For NOW, we create a new class PER constant.
# Clearly this is probably undesirable in the long
# term.
- print "TYPE=" + repr(TYPE)
- return jvmgen.VoidConst() # TODO
+ return jvmgen.WarnNullConst() # TODO
+
+ # Other
+
+ _type_printing_methods = {
+ ootype.Signed:jvmgen.PYPYDUMPINT,
+ ootype.Unsigned:jvmgen.PYPYDUMPUINT,
+ ootype.SignedLongLong:jvmgen.PYPYDUMPLONG,
+ ootype.Float:jvmgen.PYPYDUMPDOUBLE,
+ ootype.Bool:jvmgen.PYPYDUMPBOOLEAN,
+ ootype.Class:jvmgen.PYPYDUMPOBJECT,
+ }
+
+ def generate_dump_method_for_ootype(self, OOTYPE):
+ if OOTYPE in self._type_printing_methods:
+ return self._type_printing_methods[OOTYPE]
+ return self.pending_class(OOTYPE).dump_method.method()
# Type translation functions
Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py (original)
+++ pypy/dist/pypy/translator/jvm/generator.py Wed Nov 1 14:57:43 2006
@@ -1,7 +1,8 @@
import os #
from pypy.objspace.flow import model as flowmodel
from pypy.translator.oosupport.metavm import Generator
-from pypy.translator.jvm.typesystem import JvmType
+from pypy.translator.jvm.typesystem import \
+ JvmType, jObject, jPrintStream, jvm_for_class, jVoid
from pypy.rpython.ootypesystem import ootype
# ___________________________________________________________________________
@@ -46,6 +47,8 @@
return self.jvmstr + "_m1", ()
elif args[0] >= 0 and args[0] <= 5:
return self.jvmstr + "_" + str(args[0]), ()
+ else:
+ return "ldc", args # HACK
if self.flags & CONST3:
assert len(args) == 1
@@ -132,6 +135,7 @@
# Method invocation
INVOKESTATIC = Opcode(INVOKE, 'invokestatic')
+INVOKEVIRTUAL = Opcode(INVOKE, 'invokevirtual')
INVOKESPECIAL = Opcode(INVOKE, 'invokespecial')
# Other opcodes
@@ -148,7 +152,7 @@
PUTFIELD = Opcode(FIELD, 'putfield')
GETSTATIC = Opcode(FIELD, 'getstatic')
PUTSTATIC = Opcode(FIELD, 'putstatic')
-CHECKCAST = Opcode(NOFLAGS, 'checkcast')
+CHECKCAST = Opcode(CLASSINM, 'checkcast')
INEG = Opcode(NOFLAGS, 'ineg')
IXOR = Opcode(NOFLAGS, 'ixor')
IADD = Opcode(NOFLAGS, 'iadd')
@@ -190,6 +194,7 @@
NEW = Opcode(CLASSINM, 'new')
DUP = Opcode(NOFLAGS, 'dup')
POP = Opcode(NOFLAGS, 'pop')
+INSTANCEOF= Opcode(CLASSINM, 'instanceof')
# Loading/storing local variables
LOAD = OpcodeFamily(CONST3, "load")
STORE = OpcodeFamily(CONST3, "store")
@@ -231,6 +236,8 @@
MATHLABS = Method('java.lang.Math', 'abs', '(L)L')
MATHDABS = Method('java.lang.Math', 'abs', '(D)D')
MATHFLOOR = Method('java.lang.Math', 'floor', '(D)D')
+PRINTSTREAMPRINTSTR = Method('java.io.PrintStream', 'print',
+ '(Ljava/lang/String;)V', opcode=INVOKEVIRTUAL)
PYPYUINTCMP = Method('pypy.PyPy', 'uint_cmp', '(II)I')
PYPYULONGCMP = Method('pypy.PyPy', 'ulong', '(LL)I')
PYPYUINTTODOUBLE = Method('pypy.PyPy', 'uint_to_double', '(I)D')
@@ -252,12 +259,19 @@
'(Ljava/lang/String;)D')
PYPYSTRTOCHAR = Method('pypy.PyPy', 'str_to_char',
'(Ljava/lang/String;)C')
-PYPYDUMPINT = Method('pypy.PyPy', 'dump_int', '(I)V')
-PYPYDUMPUINT = Method('pypy.PyPy', 'dump_uint', '(I)V')
-PYPYDUMPLONG = Method('pypy.PyPy', 'dump_long', '(L)V')
-PYPYDUMPDOUBLE = Method('pypy.PyPy', 'dump_double', '(D)V')
-PYPYDUMPSTRING = Method('pypy.PyPy', 'dump_string', '([B)V')
-PYPYDUMPBOOLEAN = Method('pypy.PyPy', 'dump_boolean', '(Z)V')
+PYPYDUMPINDENTED = Method('pypy.PyPy', 'dump_indented',
+ '(ILjava/lang/String;)V')
+PYPYDUMPINT = Method('pypy.PyPy', 'dump_int', '(II)V')
+PYPYDUMPUINT = Method('pypy.PyPy', 'dump_uint', '(II)V')
+PYPYDUMPLONG = Method('pypy.PyPy', 'dump_long', '(LI)V')
+PYPYDUMPDOUBLE = Method('pypy.PyPy', 'dump_double', '(DI)V')
+PYPYDUMPSTRING = Method('pypy.PyPy', 'dump_string', '([BI)V')
+PYPYDUMPBOOLEAN = Method('pypy.PyPy', 'dump_boolean', '(ZI)V')
+PYPYDUMPOBJECT = Method('pypy.PyPy', 'dump_object',
+ '(Ljava/lang/Object;I)V')
+PYPYRUNTIMENEW = Method('pypy.PyPy', 'RuntimeNew',
+ '(Ljava/lang/Class;)Ljava/lang/Object;')
+
# ___________________________________________________________________________
# Fields
@@ -285,6 +299,9 @@
return "%s/%s %s" % (
self.class_name.replace('.','/'), self.field_name, self.jtype)
+SYSTEMOUT = Field('java.lang.System', 'out', jPrintStream, True)
+SYSTEMERR = Field('java.lang.System', 'err', jPrintStream, True)
+
# ___________________________________________________________________________
# Constants
#
@@ -305,6 +322,15 @@
def load(self, gen):
pass
+class NullConst(object):
+ def load(self, gen):
+ gen.emit(ACONST_NULL)
+
+class WarnNullConst(NullConst):
+ def load(self, gen):
+ gen.add_comment(" substituting NULL")
+ NullConst.load(self, gen)
+
class SignedIntConst(Const):
def __init__(self, value):
self.value = value
@@ -372,7 +398,13 @@
self.value = value
def load(self, gen):
assert isinstance(self.value, unicode)
- res = '"' + self.value.encode('utf-8').replace('"', r'\"') + '"'
+ def escape(char):
+ if char == '"': return r'\"'
+ if char == '\n': return r'\n'
+ return char
+ res = ('"' +
+ "".join(escape(c) for c in self.value.encode('utf-8')) +
+ '"')
gen.emit(LDC, res)
class ComplexConst(Const):
@@ -396,6 +428,40 @@
gen.emit(self.methodinfo)
# ___________________________________________________________________________
+# Generator State
+
+class ClassState(object):
+ """ When you invoked begin_class(), one of these objects is allocated
+ and tracks the state as we go through the definition process. """
+ def __init__(self, classnm, superclassnm):
+ self.class_name = classnm
+ self.superclass_name = superclassnm
+ def out(self, arg):
+ self.file.write(arg)
+
+class FunctionState(object):
+ """ When you invoked begin_function(), one of these objects is allocated
+ and tracks the state as we go through the definition process. """
+ def __init__(self):
+ self.next_offset = 0
+ self.local_vars = {}
+ self.instr_counter = 0
+ def add_var(self, jvar, jtype):
+ """ Adds new entry for variable 'jvar', of java type 'jtype' """
+ idx = self.next_offset
+ self.next_offset += jtype.type_width()
+ if jvar:
+ assert jvar not in self.local_vars # never been added before
+ self.local_vars[jvar] = idx
+ return idx
+ def var_offset(self, jvar, jtype):
+ """ Returns offset for variable 'jvar', of java type 'jtype' """
+ if jvar in self.local_vars:
+ return self.local_vars[jvar]
+ return self.add_var(jvar, jtype)
+
+
+# ___________________________________________________________________________
# Generator
class JVMGenerator(Generator):
@@ -410,6 +476,8 @@
def __init__(self, db):
self.db = db
self.label_counter = 0
+ self.curclass = None
+ self.curfunc = None
# __________________________________________________________________
# JVM specific methods to be overloaded by a subclass
@@ -417,14 +485,38 @@
# If the name does not begin with '_', it will be called from
# outside the generator.
- def begin_class(self, classnm):
+ def begin_class(self, classnm, superclsnm):
"""
+ Begins a class declaration. Overall flow of class declaration
+ looks like:
+
+ begin_class()
+ [add_field()]
+ emit_constructor()
+ [begin_function()...end_function()]
+ end_class()
+
+ Where items in brackets may appear anywhere from 0 to inf times.
+
classnm --- full Java name of the class (i.e., "java.lang.String")
+ superclassnm --- full Java name of the super class
"""
- unimplemented
+ assert not self.curclass
+ self.curclass = ClassState(classnm, superclsnm)
+ self._begin_class()
def end_class(self):
- unimplemented
+ self._end_class()
+ self.curclass = None
+ self.curfunc = None
+
+ def _begin_class(self):
+ """ Main implementation of begin_class """
+ raise NotImplementedError
+
+ def _end_class(self):
+ """ Main implementation of end_class """
+ raise NotImplementedError
def add_field(self, fobj):
"""
@@ -432,13 +524,29 @@
"""
unimplemented
+ def emit_constructor(self):
+ """
+ Emits the constructor for this class, which merely invokes the
+ parent constructor.
+
+ superclsnm --- same Java name of super class as from begin_class
+ """
+ jtype = jvm_for_class(self.curclass.class_name)
+ self.begin_function("<init>", [], [jtype], jVoid)
+ self.load_jvm_var(jtype, 0)
+ jmethod = Method(self.curclass.superclass_name, "<init>", "()V",
+ opcode=INVOKESPECIAL)
+ jmethod.invoke(self)
+ self.return_val(jVoid)
+ self.end_function()
+
def begin_function(self, funcname, argvars, argtypes, rettype,
static=False):
"""
funcname --- name of the function
argvars --- list of objects passed to load() that represent arguments;
should be in order, or () if load() will not be used
- argtypes --- JvmType for each argument
+ argtypes --- JvmType for each argument [INCLUDING this]
rettype --- JvmType for the return value
static --- keyword, if true then a static func is generated
@@ -448,13 +556,12 @@
# Compute the indicates of each argument in the local variables
# for the function. Note that some arguments take up two slots
# depending on their type [this is compute by type_width()]
- self.next_offset = 0
- self.local_vars = {}
+ assert not self.curfunc
+ self.curfunc = FunctionState()
for idx, ty in enumerate(argtypes):
- if idx < len(argvars):
- var = argvars[idx]
- self.local_vars[var] = self.next_offset
- self.next_offset += ty.type_width()
+ if idx < len(argvars): var = argvars[idx]
+ else: var = None
+ self.curfunc.add_var(var, ty)
# Prepare a map for the local variable indices we will add
# Let the subclass do the rest of the work; note that it does
# not need to know the argvars parameter, so don't pass it
@@ -469,8 +576,7 @@
def end_function(self):
self._end_function()
- del self.next_offset
- del self.local_vars
+ self.curfunc = None
def _end_function(self):
unimplemented
@@ -488,17 +594,24 @@
""" Returns a value from top of stack of the JvmType 'vartype' """
self._instr(RETURN.for_type(vartype))
+ def load_string(self, str):
+ uni = str.decode('utf-8')
+ UnicodeConst(uni).load(self)
+
def load_jvm_var(self, vartype, varidx):
""" Loads from jvm slot #varidx, which is expected to hold a value of
type vartype """
+ assert varidx < self.curfunc.next_offset
opc = LOAD.for_type(vartype)
- print "load_jvm_jar: vartype=%s varidx=%s opc=%s" % (
- repr(vartype), repr(varidx), repr(opc))
+ self.add_comment(" load_jvm_jar: vartype=%s varidx=%s" % (
+ repr(vartype), repr(varidx)))
self._instr(opc, varidx)
def store_jvm_var(self, vartype, varidx):
""" Loads from jvm slot #varidx, which is expected to hold a value of
type vartype """
+ self.add_comment(" store_jvm_jar: vartype=%s varidx=%s" % (
+ repr(vartype), repr(varidx)))
self._instr(STORE.for_type(vartype), varidx)
def load_from_array(self, elemtype):
@@ -531,6 +644,11 @@
if mark:
self.mark(res)
return res
+
+ def load_this_ptr(self):
+ """ Convenience method. Be sure you only call it from a
+ virtual method, not static methods. """
+ self.load_jvm_var(jObject, 0)
# __________________________________________________________________
# Exception Handling
@@ -604,17 +722,11 @@
jty = self.db.lltype_to_cts(v.concretetype)
# Determine index in stack frame slots:
# note that arguments and locals can be treated the same here
- if v in self.local_vars:
- idx = self.local_vars[v]
- else:
- idx = self.local_vars[v] = self.next_offset
- self.next_offset += jty.type_width()
- return jty, idx
+ return jty, self.curfunc.var_offset(v, jty)
def load(self, value):
if isinstance(value, flowmodel.Variable):
jty, idx = self._var_data(value)
- print "load_jvm_var: jty=%s idx=%s" % (repr(jty), repr(idx))
return self.load_jvm_var(jty, idx)
if isinstance(value, flowmodel.Constant):
@@ -624,6 +736,10 @@
repr(value.concretetype) + " v=" + repr(value))
def store(self, v):
+ # Ignore Void values
+ if v.concretetype is ootype.Void:
+ return
+
if isinstance(v, flowmodel.Variable):
jty, idx = self._var_data(v)
return self.store_jvm_var(jty, idx)
@@ -631,6 +747,9 @@
def set_field(self, CONCRETETYPE, fieldname):
if fieldname == "meta":
+ # temporary hack
+ self.add_comment(" WARNING: emulating meta for now")
+ self.emit(POP)
self.emit(POP)
else:
clsobj = self.db.pending_class(CONCRETETYPE)
@@ -639,6 +758,7 @@
def get_field(self, CONCRETETYPE, fieldname):
if fieldname == 'meta':
+ self.add_comment(" WARNING: emulating meta for now")
self.emit(POP)
self.emit(ACONST_NULL)
else:
@@ -646,8 +766,13 @@
fieldobj = clsobj.lookup_field(fieldname)
fieldobj.load(self)
- def downcast(self, type):
- self._instr(CHECKCAST, type)
+ def downcast(self, TYPE):
+ jtype = self.db.lltype_to_cts(TYPE)
+ self._instr(CHECKCAST, jtype)
+
+ def instanceof(self, TYPE):
+ jtype = self.db.lltype_to_cts(TYPE)
+ self._instr(INSTANCEOF, jtype)
def branch_unconditionally(self, target_label):
self._instr(GOTO, target_label)
@@ -662,6 +787,11 @@
mthd = self.db.pending_function(graph)
mthd.invoke(self)
+ def call_method(self, OOCLASS, method_name):
+ clsobj = self.db.pending_class(OOCLASS)
+ mthd = clsobj.lookup_method(method_name)
+ mthd.invoke(self)
+
def call_primitive(self, graph):
raise NotImplementedError
@@ -671,6 +801,9 @@
self.emit(NEW, clsobj.jvm_type())
self.emit(DUP)
self.emit(ctor)
+
+ def instantiate(self):
+ self.emit(PYPYRUNTIMENEW)
# __________________________________________________________________
# Methods invoked directly by strings in jvm/opcode.py
@@ -713,6 +846,9 @@
self._instr(ICONST, 1)
self.mark(endlbl)
+ is_null = lambda self: self._compare_op(IFNULL)
+ is_not_null = lambda self: self._compare_op(IFNOTNULL)
+
logical_not = lambda self: self._compare_op(IFEQ)
equals_zero = logical_not
not_equals_zero = lambda self: self._compare_op(IFNE)
@@ -774,74 +910,60 @@
JVMGenerator.__init__(self, db)
self.outdir = outdir
- def add_comment(self, comment):
- self.out.write(" ; %s\n" % comment)
-
- def begin_class(self, classnm):
+ def _begin_class(self):
"""
classnm --- full Java name of the class (i.e., "java.lang.String")
"""
+
+ iclassnm = self.curclass.class_name.replace('.', '/')
+ isuper = self.curclass.superclass_name.replace('.', '/')
- iclassnm = classnm.replace('.', '/')
jfile = "%s/%s.j" % (self.outdir, iclassnm)
try:
jdir = jfile[:jfile.rindex('/')]
os.makedirs(jdir)
except OSError: pass
- self.out = open(jfile, 'w')
+ self.curclass.file = open(jfile, 'w')
# Write the JasminXT header
- #self.out.write(".bytecode XX\n")
- #self.out.write(".source \n")
- self.out.write(".class public %s\n" % iclassnm)
-
- # FIX: custom super class
- self.out.write(".super java/lang/Object\n")
- self.constructor_emitted = False
+ self.curclass.out(".class public %s\n" % iclassnm)
+ self.curclass.out(".super %s\n" % isuper)
- def end_class(self):
- self._emit_constructor()
- self.out.close()
- self.out = None
+ def _end_class(self):
+ self.curclass.file.close()
def close(self):
- assert self.out is None, "Unended class"
+ assert self.curclass is None
+
+ def add_comment(self, comment):
+ self.curclass.out(" ; %s\n" % comment)
def add_field(self, fobj):
- self.out.write('.field public %s %s\n' % (
+ self.curclass.out('.field public %s %s\n' % (
fobj.field_name, fobj.jtype))
- def _emit_constructor(self):
- if not self.constructor_emitted:
- self.out.write(".method public <init>()V\n")
- self.out.write(" aload_0\n")
- self.out.write(" invokespecial java/lang/Object/<init>()V\n")
- self.out.write(" return\n")
- self.out.write(".end method\n")
- self.constructor_emitted = True
-
def _begin_function(self, funcname, argtypes, rettype, static):
- self._emit_constructor()
+ if not static: argtypes = argtypes[1:]
# Throws clause? Only use RuntimeExceptions?
kw = ['public']
if static: kw.append('static')
- self.out.write('.method %s %s(%s)%s\n' % (
+ self.curclass.out('.method %s %s(%s)%s\n' % (
" ".join(kw), funcname,
"".join(argtypes), rettype))
def _end_function(self):
- self.out.write('.limit stack 100\n') # HACK, track max offset
- self.out.write('.limit locals %d\n' % self.next_offset)
- self.out.write('.end method\n')
+ self.curclass.out('.limit stack 100\n') # HACK, track max offset
+ self.curclass.out('.limit locals %d\n' % self.curfunc.next_offset)
+ self.curclass.out('.end method\n')
def mark(self, lbl):
""" Marks the point that a label indicates. """
_, lblnum, lbldesc = lbl
assert _ == "Label"
- self.out.write(' %s_%s:\n' % (lbldesc, lblnum))
+ self.curclass.out(' %s_%s:\n' % (lbldesc, lblnum))
def _instr(self, opcode, *args):
jvmstr, args = opcode.specialize_opcode(args)
@@ -853,10 +975,12 @@
if opcode.flags & (INVOKE|FIELD):
assert len(args) == 1
args = (args[0].jasmin_syntax(),)
- self.out.write(' %s %s\n' % (
- jvmstr, " ".join([str(s) for s in args])))
+ instr_text = ' %s %s' % (jvmstr, " ".join([str(s) for s in args]))
+ self.curclass.out(' %-60s ; %d\n' % (
+ instr_text, self.curfunc.instr_counter))
+ self.curfunc.instr_counter+=1
def try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
- self.out.write(' .catch %s from %s to %s using %s\n' % (
+ self.curclass.out(' .catch %s from %s to %s using %s\n' % (
excclsty.int_class_name(), trystartlbl, tryendlbl, catchlbl))
Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py (original)
+++ pypy/dist/pypy/translator/jvm/node.py Wed Nov 1 14:57:43 2006
@@ -8,8 +8,9 @@
from pypy.rpython.ootypesystem import ootype
from pypy.translator.jvm.typesystem import \
jString, jStringArray, jVoid, jThrowable
-from pypy.translator.jvm.typesystem import jvm_for_class, jvm_method_desc
+from pypy.translator.jvm.typesystem import jvm_for_class, jvm_method_desc, jInt
from pypy.translator.jvm.opcodes import opcodes
+from pypy.translator.jvm.option import getoption
from pypy.translator.oosupport.function import Function as OOFunction
import pypy.translator.jvm.generator as jvmgen
@@ -60,16 +61,8 @@
ootype.Char:jvmgen.PYPYSTRTOCHAR
}
- _type_printing_methods = {
- ootype.Signed:jvmgen.PYPYDUMPINT,
- ootype.Unsigned:jvmgen.PYPYDUMPUINT,
- ootype.SignedLongLong:jvmgen.PYPYDUMPLONG,
- ootype.Float:jvmgen.PYPYDUMPDOUBLE,
- ootype.Bool:jvmgen.PYPYDUMPBOOLEAN,
- }
-
def render(self, gen):
- gen.begin_class('pypy.Main')
+ gen.begin_class('pypy.Main', 'java.lang.Object')
gen.begin_function(
'main', (), [jStringArray], jVoid, static=True)
@@ -97,10 +90,10 @@
# Print result?
if self.print_result:
- resootype = self.graph.getreturnvar().concretetype
- resjtype = self.db.lltype_to_cts(resootype)
- meth = self._type_printing_methods[resootype]
- gen.emit(meth)
+ gen.emit(jvmgen.ICONST, 0)
+ RESOOTYPE = self.graph.getreturnvar().concretetype
+ dumpmethod = self.db.generate_dump_method_for_ootype(RESOOTYPE)
+ dumpmethod.invoke(gen)
# And finish up
gen.return_val(jVoid)
@@ -131,9 +124,13 @@
def method(self):
""" Returns a jvmgen.Method that can invoke this function """
- if not self.is_method: opcode = jvmgen.INVOKESTATIC
- else: opcode = jvmgen.INVOKEVIRTUAL
- mdesc = jvm_method_desc(self.jargtypes, self.jrettype)
+ if not self.is_method:
+ opcode = jvmgen.INVOKESTATIC
+ startidx = 0
+ else:
+ opcode = jvmgen.INVOKEVIRTUAL
+ startidx = 1
+ mdesc = jvm_method_desc(self.jargtypes[startidx:], self.jrettype)
return jvmgen.Method(self.classnm, self.name, mdesc, opcode=opcode)
def begin_render(self):
@@ -196,15 +193,14 @@
self.ilasm.load(exc)
self.ilasm.throw()
- def _setup_link(self, link):
- target = link.target
- for to_load, to_store in zip(link.args, target.inputargs):
- if to_load.concretetype is not ootype.Void:
- self.ilasm.load(to_load)
- self.ilasm.store(to_store)
-
def _render_op(self, op):
self.generator.add_comment(str(op))
+
+ if getoption('trace'):
+ jvmgen.SYSTEMERR.load(self.generator)
+ self.generator.load_string(str(op) + "\n")
+ jvmgen.PRINTSTREAMPRINTSTR.invoke(self.generator)
+
OOFunction._render_op(self, op)
class Class(Node):
@@ -212,15 +208,16 @@
""" Represents a class to be emitted. Note that currently, classes
are emitted all in one shot, not piecemeal. """
- def __init__(self, name):
+ def __init__(self, name, supername):
"""
- 'name' should be a fully qualified Java class name like
+ 'name' and 'super_name' should be fully qualified Java class names like
"java.lang.String"
"""
- self.name = name # public attribute
- self.fields = []
- self.methods = []
+ self.name = name # public attribute
+ self.super_name = supername # public attribute
+ self.fields = {}
self.rendered = False
+ self.methods = {}
def jvm_type(self):
return jvm_for_class(self.name)
@@ -229,29 +226,104 @@
""" Creates a new field accessed via the jvmgen.Field
descriptor 'fieldobj'. Must be called before render()."""
assert not self.rendered and isinstance(fieldobj, jvmgen.Field)
- self.fields.append(fieldobj)
+ self.fields[fieldobj.field_name] = fieldobj
def lookup_field(self, fieldnm):
- for f in self.fields:
- if f.field_name == fieldnm: return f
- assert False, "No field named '%s' found" % fieldnm
+ """ Given a field name, returns a jvmgen.Field object """
+ return self.fields[fieldnm]
+
+ def lookup_method(self, methodnm):
+ """ Given the method name, returns a jvmgen.Method object """
+ return self.methods[methodnm].method()
def add_method(self, func):
""" Creates a new method in this class, represented by the
Function object 'func'. Must be called before render();
intended to be invoked by the database. Note that some of these
'methods' may actually represent static functions. """
- self.methods.append(func)
+ self.methods[func.name] = func
+ def add_dump_method(self, dm):
+ self.dump_method = dm # public attribute for reading
+ self.add_method(dm)
+
def render(self, gen):
self.rendered = True
- gen.begin_class(self.name)
+ gen.begin_class(self.name, self.super_name)
- for field in self.fields:
+ for field in self.fields.values():
gen.add_field(field)
- for method in self.methods:
+ gen.emit_constructor()
+
+ for method in self.methods.values():
method.render(gen)
gen.end_class()
+class TestDumpMethod(object):
+
+ def __init__(self, db, OOCLASS, clsobj):
+ self.db = db
+ self.OOCLASS = OOCLASS
+ self.clsobj = clsobj
+ self.name = "_pypy_dump"
+ self.jargtypes = [clsobj.jvm_type(), jInt]
+ self.jrettype = jVoid
+
+ def method(self):
+ """ Returns a jvmgen.Method that can invoke this function """
+ mdesc = jvm_method_desc(self.jargtypes[1:], self.jrettype)
+ return jvmgen.Method(self.clsobj.name, self.name, mdesc,
+ opcode=jvmgen.INVOKEVIRTUAL)
+
+ def render(self, gen):
+ clsobj = self.clsobj
+
+ gen.begin_function(
+ self.name, (), self.jargtypes, self.jrettype, static=False)
+
+ def genprint(str, unoffset=0):
+ gen.load_jvm_var(jInt, 1)
+ if unoffset:
+ gen.emit(jvmgen.ICONST, unoffset)
+ gen.emit(jvmgen.ISUB)
+ gen.load_string(str)
+ jvmgen.PYPYDUMPINDENTED.invoke(gen)
+
+ # Start the dump
+ genprint("InstanceWrapper([")
+
+ # Increment the indent
+ gen.load_jvm_var(jInt, 1)
+ gen.emit(jvmgen.ICONST, 2)
+ gen.emit(jvmgen.IADD)
+ gen.store_jvm_var(jInt, 1)
+
+ for fieldnm, (FIELDOOTY, fielddef) in self.OOCLASS._fields.iteritems():
+
+ if FIELDOOTY is ootype.Void: continue
+
+ genprint("(")
+ genprint(fieldnm+",")
+
+ print "fieldnm=%r fieldty=%r" % (fieldnm, FIELDOOTY)
+
+ # Print the value of the field:
+ gen.load_this_ptr()
+ fieldobj = clsobj.lookup_field(fieldnm)
+ fieldobj.load(gen)
+ gen.load_jvm_var(jInt, 1)
+ dumpmethod = self.db.generate_dump_method_for_ootype(FIELDOOTY)
+ gen.emit(dumpmethod)
+
+ genprint(")")
+
+ # Decrement indent and dump close
+ genprint("])", 2)
+
+ gen.emit(jvmgen.RETURN.for_type(jVoid))
+
+ gen.end_function()
+
+
Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py (original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py Wed Nov 1 14:57:43 2006
@@ -7,7 +7,7 @@
from pypy.translator.oosupport.metavm import \
PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing, Call,\
- SetField, GetField
+ SetField, GetField, CallMethod, DownCast, RuntimeNew
import pypy.translator.jvm.generator as jvmgen
def _check_zer(op):
@@ -23,14 +23,14 @@
opcodes = {
# __________ object oriented operations __________
'new': [New, StoreResult],
- #'runtimenew': [RuntimeNew],
+ 'runtimenew': [RuntimeNew, StoreResult],
'oosetfield': [SetField],
'oogetfield': [GetField, StoreResult],
- #'oosend': [CallMethod],
- #'ooupcast': DoNothing,
- #'oodowncast': [DownCast],
- #'oois': 'ceq',
- #'oononnull': [PushAllArgs, 'ldnull', 'ceq']+Not,
+ 'oosend': [CallMethod, StoreResult],
+ 'ooupcast': DoNothing,
+ 'oodowncast': [DownCast,StoreResult],
+ 'oois': 'is_null',
+ 'oononnull': 'is_not_null',
#'instanceof': [CastTo, 'ldnull', 'cgt.un'],
#'subclassof': [PushAllArgs, 'call bool [pypylib]pypy.runtime.Utils::SubclassOf(class [mscorlib]System.Type, class[mscorlib]System.Type)'],
#'ooidentityhash': [PushAllArgs, 'callvirt instance int32 object::GetHashCode()'],
@@ -41,7 +41,7 @@
#
'same_as': DoNothing,
#'hint': [PushArg(0), StoreResult],
- 'direct_call': [Call],
+ 'direct_call': [Call, StoreResult],
#'indirect_call': [IndirectCall],
#
#'cast_ptr_to_weakadr': [PushAllArgs, 'newobj instance void class %s::.ctor(object)' % WEAKREF],
Modified: pypy/dist/pypy/translator/jvm/option.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/option.py (original)
+++ pypy/dist/pypy/translator/jvm/option.py Wed Nov 1 14:57:43 2006
@@ -8,7 +8,8 @@
'noasm':False,
'package':'pypy',
'wd':False,
- 'norun':False
+ 'norun':False,
+ 'trace':False
}
def getoption(name):
Modified: pypy/dist/pypy/translator/jvm/src/PyPy.java
==============================================================================
--- pypy/dist/pypy/translator/jvm/src/PyPy.java (original)
+++ pypy/dist/pypy/translator/jvm/src/PyPy.java Wed Nov 1 14:57:43 2006
@@ -185,45 +185,69 @@
// Used in testing:
- public static void dump_int(int i) {
- System.out.println(i);
+ public static void dump_indented(int indent, String text) {
+ for (int i = 0; i < indent; i++)
+ System.out.print(" ");
+ System.out.println(text);
}
- public static void dump_uint(int i) {
+ public static void dump_int(int i, int indent) {
+ dump_indented(indent, Integer.toString(i));
+ }
+
+ public static void dump_uint(int i, int indent) {
if (i >= 0)
- System.out.println(i);
+ dump_indented(indent, Integer.toString(i));
else {
int loword = i & 0xFFFF;
int hiword = i >>> 16;
long res = loword + (hiword*0xFFFF);
- System.out.println(res);
+ dump_indented(indent, Long.toString(res));
}
}
- public static void dump_boolean(boolean l) {
+ public static void dump_boolean(boolean l, int indent) {
if (l)
- System.out.println("True");
+ dump_indented(indent, "True");
else
- System.out.println("False");
+ dump_indented(indent, "False");
}
- public static void dump_long(long l) {
- System.out.println(l);
+ public static void dump_long(long l, int indent) {
+ dump_indented(indent, Long.toString(l));
}
- public static void dump_double(double d) {
- System.out.println(d);
+ public static void dump_double(double d, int indent) {
+ dump_indented(indent, Double.toString(d));
}
- public static void dump_string(char[] b) {
- System.out.print('"');
+ public static void dump_string(char[] b, int indent) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('"');
for (char c : b) {
if (c == '"')
- System.out.print("\\\"");
- System.out.print(c);
+ sb.append("\\\"");
+ else
+ sb.append(c);
+ }
+ sb.append('"');
+ dump_indented(indent, sb.toString());
+ }
+
+ public static void dump_object(Object o, int indent) {
+ dump_indented(indent, o.toString());
+ }
+
+ // ----------------------------------------------------------------------
+ // Type Manipulation Routines
+
+ public static Object RuntimeNew(Class c) {
+ // all classes in our system have constructors w/ no arguments
+ try {
+ return c.getConstructor().newInstance();
+ } catch (Exception exc) {
+ throw new RuntimeException("Unexpected", exc);
}
- System.out.print('"');
- System.out.println();
}
// ----------------------------------------------------------------------
Modified: pypy/dist/pypy/translator/jvm/test/runtest.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/test/runtest.py (original)
+++ pypy/dist/pypy/translator/jvm/test/runtest.py Wed Nov 1 14:57:43 2006
@@ -40,6 +40,14 @@
def __repr__(self):
return 'ExceptionWrapper(%s)' % repr(self.class_name)
+class InstanceWrapper:
+ def __init__(self, fields):
+ # fields is a list of (name, value) tuples
+ self.fields = fields
+
+ def __repr__(self):
+ return 'InstanceWrapper(%s)' % repr(self.fields)
+
# CLI could-be duplicate
class JvmGeneratedSourceWrapper(object):
def __init__(self, gensrc):
Modified: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/typesystem.py (original)
+++ pypy/dist/pypy/translator/jvm/typesystem.py Wed Nov 1 14:57:43 2006
@@ -48,9 +48,13 @@
return not self.is_scalar()
def is_array(self):
return self[0] == '['
+ def class_name(self):
+ """ Converts a descriptor like Ljava/lang/Object; to
+ full class name java.lang.Object """
+ return self.int_class_name().replace('/','.')
def int_class_name(self):
""" Converts a descriptor like Ljava/lang/Object; to
- java/lang/Object """
+ internal class name java/lang/Object """
assert self[0] == 'L' and self[-1] == ';'
return self[1:-1]
def type_width(self):
@@ -93,6 +97,7 @@
jIterator = jvm_for_class('java.util.Iterator')
jClass = jvm_for_class('java.lang.Class')
jStringBuilder = jvm_for_class('java.lang.StringBuilder')
+jPrintStream = jvm_for_class('java.io.PrintStream')
# Map from OOType to an internal JVM type descriptor
# only handles the simple cases
Modified: pypy/dist/pypy/translator/oosupport/function.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/function.py (original)
+++ pypy/dist/pypy/translator/oosupport/function.py Wed Nov 1 14:57:43 2006
@@ -158,9 +158,12 @@
self.generator.branch_conditionally(link.exitcase, target_label)
def _setup_link(self, link):
+ self.generator.add_comment("Setup link")
target = link.target
for to_load, to_store in zip(link.args, target.inputargs):
if to_load.concretetype is not Void:
+ self.generator.add_comment(" to_load=%r to_store=%r" % (
+ to_load, to_store))
self.generator.load(to_load)
self.generator.store(to_store)
Modified: pypy/dist/pypy/translator/oosupport/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/metavm.py (original)
+++ pypy/dist/pypy/translator/oosupport/metavm.py Wed Nov 1 14:57:43 2006
@@ -15,6 +15,19 @@
from pypy.rpython.ootypesystem.bltregistry import ExternalType
class Generator(object):
+
+ def add_comment(self, text):
+ """
+ Called w/in a function w/ a text string that could be
+ usefully added to the output.
+ """
+ pass
+
+ def dup(self, TYPE):
+ """ Duplicates the top of the stack, which is of the given TYPE.
+
+ Stack: val, ... -> val, val, ..."""
+ raise NotImplementedError
def emit(self, instr, *args):
"""
@@ -49,7 +62,7 @@
Stack: value, item, ... -> ...
"""
- pass
+ raise NotImplementedError
def get_field(self, CONCRETETYPE, fieldname):
"""
@@ -60,15 +73,35 @@
Stack: item, ... -> ...
"""
- pass
+ raise NotImplementedError
- def downcast(self, type):
+ def downcast(self, TYPE):
"""
Casts the object on the top of the stack to be of the specified
- type. Assumed to raise an exception on failure.
+ ootype. Assumed to raise an exception on failure.
Stack: obj, ... -> obj, ...
"""
+ raise NotImplementedError
+
+
+ def instantiate(self):
+ """
+ Instantiates an instance of the Class object that is on top of
+ the stack. Class objects refers to an object representing a
+ class. Used to implement RuntimeNew.
+
+ Stack: class_obj, ... -> instance_obj, ...
+ """
+ raise NotImplementedError
+
+ def instanceof(self, TYPE):
+ """
+ Determines whether the object on the top of the stack is an
+ instance of TYPE (an ootype).
+
+ Stack: obj, ... -> boolean, ...
+ """
pass
def branch_unconditionally(self, target_label):
@@ -85,13 +118,20 @@
raise NotImplementedError
def call_graph(self, graph):
- """ Invokes the method corresponding to the given graph. The
+ """ Invokes the function corresponding to the given graph. The
arguments to the graph have already been pushed in order
(i.e., first argument pushed first, etc). Pushes the return
value.
Stack: argN...arg2, arg1, arg0, ... -> ret, ... """
- raise NotImplementedError
+ raise NotImplementedError
+
+ def call_method(self, OOCLASS, method_name):
+ """ Invokes the given method on the object on the stack. The
+ this ptr and all arguments have already been pushed.
+
+ Stack: argN, arg2, arg1, this, ... -> ret, ... """
+ raise NotImplementedError
def call_primitive(self, graph):
""" Like call_graph, but it has been suggested that the method be
@@ -106,6 +146,9 @@
Stack: ... -> newobj, ... """
raise NotImplementedError
+ def push_null(self):
+ raise NotImplementedError
+
class InstructionList(list):
def render(self, generator, op):
for instr in self:
@@ -182,9 +225,16 @@
result on top of the stack. """
def render(self, generator, op):
RESULTTYPE = op.result.concretetype
- resulttype = generator.cts.lltype_to_cts(RESULTTYPE)
generator.load(op.args[0])
- generator.downcast(resulttype)
+ generator.downcast(RESULTTYPE)
+
+class _InstanceOf(MicroInstruction):
+ """ Push the argument op.args[0] and cast it to the desired type, leaving
+ result on top of the stack. """
+ def render(self, generator, op):
+ RESULTTYPE = op.result.concretetype
+ generator.load(op.args[0])
+ generator.instanceof(RESULTTYPE)
# There are three distinct possibilities where we need to map call differently:
# 1. Object is marked with rpython_hints as a builtin, so every attribut access
@@ -292,37 +342,23 @@
else:
generator.call_graph(graph)
else:
- self._render_method(generator, method_name, op.args[1:])
-
- def _render_method(self, generator, method_name, args):
- this = args[0]
- for arg in args: # push parameters
- generator.load(arg)
-
- # XXX: very hackish, need refactoring
- if this.concretetype is ootype.String:
- # special case for string: don't use methods, but plain functions
- METH = this.concretetype._METHODS[method_name]
- cts = generator.cts
- ret_type = cts.lltype_to_cts(METH.RESULT)
- arg_types = [cts.lltype_to_cts(arg) for arg in METH.ARGS if arg is not ootype.Void]
- arg_types.insert(0, cts.lltype_to_cts(ootype.String))
- arg_list = ', '.join(arg_types)
- signature = '%s %s::%s(%s)' % (ret_type, STRING_HELPER_CLASS, method_name, arg_list)
- generator.call_signature(signature)
- else:
+ this = op.args[1]
generator.call_method(this.concretetype, method_name)
- # special case: DictItemsIterator(XXX,
- # Void).ll_current_value needs to return an int32 because
- # we can't use 'void' as a parameter of a Generic. This
- # means that after the call to ll_current_value there will
- # be a value on the stack, and we need to explicitly pop
- # it.
- if isinstance(this.concretetype, ootype.DictItemsIterator) and \
- this.concretetype._VALUETYPE is ootype.Void and \
- method_name == 'll_current_value':
- generator.ilasm.pop()
+class _CallMethod(MicroInstruction):
+ def render(self, generator, op):
+ method = op.args[0] # a FlowConstant string...
+ this = op.args[1]
+ for arg in op.args[1:]:
+ generator.load(arg)
+ generator.call_method(this.concretetype, method.value)
+
+class _RuntimeNew(MicroInstruction):
+ def render(self, generator, op):
+ generator.load(op.args[0])
+ generator.instantiate()
+ generator.downcast(op.result.concretetype)
+
New = _New()
@@ -333,3 +369,5 @@
DownCast = _DownCast()
DoNothing = _DoNothing()
Call = _Call()
+CallMethod = _CallMethod()
+RuntimeNew = _RuntimeNew()
More information about the Pypy-commit
mailing list