[pypy-svn] r33528 - in pypy/dist/pypy/translator: jvm jvm/src jvm/test oosupport
niko at codespeak.net
niko at codespeak.net
Sun Oct 22 01:37:02 CEST 2006
Author: niko
Date: Sun Oct 22 01:36:59 2006
New Revision: 33528
Added:
pypy/dist/pypy/translator/jvm/__init__.py
pypy/dist/pypy/translator/jvm/option.py
pypy/dist/pypy/translator/jvm/test/
pypy/dist/pypy/translator/jvm/test/__init__.py
pypy/dist/pypy/translator/jvm/test/runtest.py
pypy/dist/pypy/translator/jvm/test/test_bool.py
pypy/dist/pypy/translator/jvm/typesystem.py
Removed:
pypy/dist/pypy/translator/jvm/types.py
Modified:
pypy/dist/pypy/translator/jvm/ (props changed)
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/genjvm.py
pypy/dist/pypy/translator/jvm/node.py
pypy/dist/pypy/translator/jvm/opcodes.py
pypy/dist/pypy/translator/jvm/src/PyPy.java
pypy/dist/pypy/translator/oosupport/metavm.py
Log:
further work towards working jvm tests --- this is an intermediate
check-in, still evolving the JVM TypeSystem and database.
Added: pypy/dist/pypy/translator/jvm/__init__.py
==============================================================================
Modified: pypy/dist/pypy/translator/jvm/conftest.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/conftest.py (original)
+++ pypy/dist/pypy/translator/jvm/conftest.py Sun Oct 22 01:36:59 2006
@@ -13,6 +13,10 @@
## help='View the graphs before they are generated'),
Option('--wd', action='store_true', dest='wd', default=False,
help='Output to current directory instead of /tmp'),
+ Option('--noassemble', action='store_true', dest="noasm", default=False,
+ help="don't assemble jasmin files"),
+ Option('--norun', action='store_true', dest="norun", default=False,
+ help="don't run the compiled executable"),
Option('--package', action='store', dest='package', default='pypy',
help='Package to output generated classes into')
#Option('--opt', action='XXX', dest='YYY', default=DEF, help='HELP')
Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py (original)
+++ pypy/dist/pypy/translator/jvm/database.py Sun Oct 22 01:36:59 2006
@@ -1,11 +1,85 @@
"""
-For now, a simple worklist abstraction.
+The database tracks which graphs have already been generated, and maintains
+a worklist. It also contains a pointer to the type system. It is passed
+into every node for generation along with the generator.
"""
+from cStringIO import StringIO
+from pypy.rpython.ootypesystem import ootype
+from pypy.translator.jvm.typesystem import jvm_method_desc
+from pypy.translator.jvm import node
+import pypy.translator.jvm.generator as jvmgen
+import pypy.translator.jvm.typesystem as jvmtypes
class Database:
- def __init__(self):
- self._pending = []
+ def __init__(self, ts):
+ # Public attributes:
+ self.type_system = ts
+
+ # Private attributes:
+ self._classes = {} # Maps ootype class objects to node.Class objects
+ self._counter = 0 # Used to create unique names
+ self._pending = [] # Worklist
+
+ def _make_unique_name(self, nm):
+ cnt = self._counter
+ self._counter += 1
+ return nm + "_" + str(cnt) + "_"
+
+ def get_class_for(self, ooclass):
+ """ Given an OOTypeSystem Instance object representing a user
+ defined class (ooclass), returns a node.Class object representing
+ its jvm counterpart. """
+
+ # Create class object if it does not already exist:
+ if ooclass in self._classes:
+ return self._classes[ooclass]
+ clname = self._make_unique_name(ooclass._name)
+ clobj = self._classes[ooclass] = node.Class(clname)
+
+ # Add fields:
+ for fieldnm, (fieldty, fielddef) in ooclass._fields.iteritems():
+ if ftype is ootype.Void: continue
+ fieldnm = self._make_unique_name(fieldnm)
+ fieldty = self.type_system.ootype_to_jvm(ftype)
+ clobj.add_field(fieldty, fieldnm) # TODO --- fielddef??
+
+ # Add methods:
+ for mname, mimpl in ooclass._methods.iteritems():
+ if not hasattr(mimpl, 'graph'):
+ # Abstract method
+ TODO
+ else:
+ # if the first argument's type is not a supertype of
+ # 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()
+ SELF = args[0].concretetype
+ if not ootype.isSubclass(ooclass, SELF): continue
+ mobj = _method_for_graph(clobj, False, mimpl.graph)
+ clobj.add_method(mobj)
+
+ return clobj
+
+ def _method_for_graph(self, classobj, is_static, graph):
+ """
+ Creates a node.Function object for a particular graph. Adds the
+ method to 'classobj', which should be a node.Class object.
+ """
+
+ # Build up a func object
+ func_name = self._make_unique_name(graph.name)
+ argtypes = [arg.concretetype for arg in graph.getargs()
+ if arg.concretetype is not ootype.Void]
+ jargtypes = [self.type_system.ootype_to_jvm(argty)
+ for argty in argtypes]
+ rettype = graph.getreturnvar().concretetype
+ jrettype = self.type_system.ootype_to_jvm(rettype)
+ funcobj = self._translated[cachekey] = node.Function(
+ classobj, func_name, jargtypes, jrettype, graph, is_static)
+ return funcobj
+
def pending_node(self, node):
self._pending.append(node)
@@ -14,6 +88,3 @@
def pop(self):
return self._pending.pop()
-
- def method_for_graph(self, graph):
-
Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py (original)
+++ pypy/dist/pypy/translator/jvm/generator.py Sun Oct 22 01:36:59 2006
@@ -1,6 +1,7 @@
+import os #
+from pypy.objspace.flow import model as flowmodel
from pypy.translator.oosupport.metavm import Generator
-
-__all__ = ['JasminGenerator']
+from pypy.translator.jvm.typesystem import JvmType
# ___________________________________________________________________________
# JVM Opcode Flags:
@@ -8,11 +9,11 @@
# Indicates certain properties of each opcode. Used mainly for debugging
# assertions
-NOFLAGS = 0
-BRANCH = 1 # Opcode is a branching opcode (implies a label argument)
-INTARG = 2 # Opcode has an integer argument
-CONST = 6 # Opcode has specialized variants (implies INTARG)
-INVOKE = 8 # Opcode is some kind of method invocation
+NOFLAGS = 0
+BRANCH = 1 # Opcode is a branching opcode (implies a label argument)
+INTARG = 2 # Opcode has an integer argument
+CONSTSPEC = 6 # Opcode has specialized variants (implies INTARG)
+INVOKE = 8 # Opcode is some kind of method invocation
# ___________________________________________________________________________
# JVM Opcodes:
@@ -27,6 +28,48 @@
"""
self.flags = flags
self.jvmstr = jvmstr
+
+class OpcodeFamily(object):
+ """
+ Many opcodes in JVM have variants that depend on the type of the
+ operands; for example, one must choose the correct ALOAD, ILOAD,
+ or DLOAD depending on whether one is loading a reference, integer,
+ or double variable respectively. Each instance of this class
+ defines one 'family' of opcodes, such as the LOAD family shown
+ above, and produces Opcode objects specific to a particular type.
+ """
+ def __init__(self, flags, suffix):
+ """
+ flags is a set of flags (see above) that describe opcode
+ jvmstr is the name for jasmin printouts
+ """
+ self.flags = flags
+ self.suffix = suffix
+ self.cache = {}
+
+ def _o(self, prefix):
+ try:
+ return self.cache[prefix]
+ except KeyError:
+ self.cache[prefix] = obj = Opcode(self.flags, prefix+self.suffix)
+ return obj
+
+ def for_type(self, argtype):
+ """ Returns a customized opcode of this family appropriate to
+ 'argtype', a JvmType object. """
+
+ # These are always true:
+ if self[0] == 'L': return self._o("A") # Objects
+ if self[0] == '[': return self._o("A") # Arrays
+ if self == 'I': return self._o("I") # Integers
+ if self == 'J': return self._o("L") # Integers
+ if self == 'D': return self._o("D") # Doubles
+ if self == 'C': return self._o("C") # Characters
+ if self == 'B': return self._o("B") # Bytes
+ if self == 'V': return self._o("") # Void [used by RETURN]
+
+ # TODO --- extend? etc
+ unimplemented
# Define the opcodes for IFNE, IFEQ, IFLT, IF_ICMPLT, etc. The IFxx
# variants compare a single integer arg against 0, and the IF_ICMPxx
@@ -48,11 +91,11 @@
# Other opcodes
GOTO = Opcode(BRANCH, 'goto')
-ICONST = Opcode(CONST, 'iconst')
-DCONST_0 = Opcode(CONST, 'dconst_0')
-DCONST_1 = Opcode(CONST, 'dconst_1')
-LCONST_0 = Opcode(CONST, 'lconst_0')
-LCONST_1 = Opcode(CONST, 'lconst_1')
+ICONST = Opcode(CONSTSPEC, 'iconst')
+DCONST_0 = Opcode(NOFLAGS, 'dconst_0')
+DCONST_1 = Opcode(NOFLAGS, 'dconst_1')
+LCONST_0 = Opcode(NOFLAGS, 'lconst_0')
+LCONST_1 = Opcode(NOFLAGS, 'lconst_1')
GETFIELD = Opcode(NOFLAGS, 'getfield')
PUTFIELD = Opcode(NOFLAGS, 'putfield')
GETSTATIC = Opcode(NOFLAGS, 'getstatic')
@@ -66,16 +109,56 @@
IDIV = Opcode(NOFLAGS, 'idiv')
IREM = Opcode(NOFLAGS, 'irem')
IAND = Opcode(NOFLAGS, 'iand')
+IOR = Opcode(NOFLAGS, 'ior')
ISHL = Opcode(NOFLAGS, 'ishl')
ISHR = Opcode(NOFLAGS, 'ishr')
+IUSHR = Opcode(NOFLAGS, 'iushr')
DCMPG = Opcode(NOFLAGS, 'dcmpg')
DCMPL = Opcode(NOFLAGS, 'dcmpl')
NOP = Opcode(NOFLAGS, 'nop')
I2D = Opcode(NOFLAGS, 'i2d')
I2L = Opcode(NOFLAGS, 'i2l')
+D2I= Opcode(NOFLAGS, 'd2i')
+L2I = Opcode(NOFLAGS, 'l2i')
+ATHROW = Opcode(NOFLAGS, 'athrow')
+DNEG = Opcode(NOFLAGS, 'dneg')
+DADD = Opcode(NOFLAGS, 'dadd')
+DSUB = Opcode(NOFLAGS, 'dsub')
+DMUL = Opcode(NOFLAGS, 'dmul')
+DDIV = Opcode(NOFLAGS, 'ddiv')
+DREM = Opcode(NOFLAGS, 'drem')
+LNEG = Opcode(NOFLAGS, 'lneg')
+LADD = Opcode(NOFLAGS, 'ladd')
+LSUB = Opcode(NOFLAGS, 'lsub')
+LMUL = Opcode(NOFLAGS, 'lmul')
+LDIV = Opcode(NOFLAGS, 'ldiv')
+LREM = Opcode(NOFLAGS, 'lrem')
+LAND = Opcode(NOFLAGS, 'land')
+LOR = Opcode(NOFLAGS, 'lor')
+LXOR = Opcode(NOFLAGS, 'lxor')
+LSHL = Opcode(NOFLAGS, 'lshl')
+LSHR = Opcode(NOFLAGS, 'lshr')
+LUSHR = Opcode(NOFLAGS, 'lushr')
+
+# Loading/storing local variables
+LOAD = OpcodeFamily(INTARG, "load")
+STORE = OpcodeFamily(INTARG, "store")
+RETURN = OpcodeFamily(NOFLAGS, "return")
+
+# Loading/storing from arrays
+# *NOTE*: This family is characterized by the type of the ELEMENT,
+# not the type of the ARRAY.
+#
+# Also: here I break from convention by naming the objects ARRLOAD
+# rather than ALOAD, even though the suffix is 'aload'. This is to
+# avoid confusion with the ALOAD opcode.
+ARRLOAD = OpcodeFamily(NOFLAGS, "aload")
+ARRSTORE = OpcodeFamily(NOFLAGS, "astore")
# ___________________________________________________________________________
# Helper Method Information
+#
+# These are used by code outside of this module as well.
class Method(object):
def __init__(self, classnm, methnm, desc, opcode=INVOKESTATIC):
@@ -95,15 +178,40 @@
PYPYUINTTODOUBLE = Method('pypy.PyPy', 'uint_to_double', '(I)D')
PYPYDOUBLETOUINT = Method('pypy.PyPy', 'double_to_uint', '(D)I')
PYPYLONGBITWISENEGATE = Method('pypy.PyPy', 'long_bitwise_negate', '(L)L')
+PYPYARRAYTOLIST = Method('pypy.PyPy', 'array_to_list',
+ '([Ljava/lang/Object;)Ljava/util/List;')
+PYPYSTRTOINT = Method('pypy.PyPy', 'str_to_int',
+ '([Ljava/lang/String;)I')
+PYPYSTRTOUINT = Method('pypy.PyPy', 'str_to_uint',
+ '([Ljava/lang/String;)I')
+PYPYSTRTOLONG = Method('pypy.PyPy', 'str_to_long',
+ '([Ljava/lang/String;)J')
+PYPYSTRTOULONG = Method('pypy.PyPy', 'str_to_ulong',
+ '([Ljava/lang/String;)J')
+PYPYSTRTOBOOL = Method('pypy.PyPy', 'str_to_bool',
+ '([Ljava/lang/String;)B')
+PYPYSTRTODOUBLE = Method('pypy.PyPy', 'str_to_double',
+ '([Ljava/lang/String;)D')
+PYPYSTRTOCHAR = Method('pypy.PyPy', 'str_to_char',
+ '([Ljava/lang/String;)C')
class JVMGenerator(Generator):
""" Base class for all JVM generators. Invokes a small set of '_'
methods which indicate which opcodes to emit; these can be
- translated by a subclass into Jasmin assembly, binary output, etc."""
+ translated by a subclass into Jasmin assembly, binary output, etc.
+ Must be inherited from to specify a particular output format;
+ search for the string 'unimplemented' to find the methods that
+ must be overloaded. """
+
+ def __init__(self, type_system):
+ self.type_system = type_system
# __________________________________________________________________
# JVM specific methods to be overloaded by a subclass
+ #
+ # If the name does not begin with '_', it will be called from
+ # outside the generator.
def begin_class(self, classnm):
"""
@@ -114,64 +222,213 @@
def end_class(self):
unimplemented
- def begin_function(self, funcname, argtypes, static=False):
+ def add_field(self, fname, ftype):
+ """
+ fname --- name of the field (a string)
+ ftype --- JvmType for the field
+ """
+ # TODO --- should fdesc be an ootype??
+ unimplemented
+
+ def begin_function(self, funcname, argvars, argtypes, rettype,
+ static=False):
"""
funcname --- name of the function
- argtypes --- types of each argument (in what format??)
+ 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
+ rettype --- JvmType for the return value
static --- keyword, if true then a static func is generated
+
+ This function also defines the scope for variables passed to
+ load()/store().
"""
- unimplemented
+ # 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 = {}
+ for idx, var in enumerate(argvars):
+ self.local_vars[var] = self.next_offset
+ self.next_offset += argtypes[idx].type_width()
+ # 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
+ self._begin_function(funcname, argtypes, rettype, static)
+
+ def _begin_function(self, funcname, argtypes, rettype, static):
+ """
+ Main implementation of begin_function. The begin_function()
+ does some generic handling of args.
+ """
+ unimplemented
def end_function(self):
+ del self.next_offset
+ del self.local_vars
+ self._end_function()
+
+ def _end_function(self):
+ unimplemented
+
+ def mark(self, lbl):
+ """ Marks the point that a label indicates. """
unimplemented
- def _unique_label(self, desc):
+ def _instr(self, opcode, *args):
+ """ Emits an instruction with the given opcode and arguments.
+ The correct opcode and their types depends on the opcode. """
+ unimplemented
+
+ def return_val(self, vartype):
+ """ Returns a value from top of stack of the JvmType 'vartype' """
+ self._instr(RETURN.for_type(vartype))
+
+ def load_jvm_var(self, vartype, varidx):
+ """ Loads from jvm slot #varidx, which is expected to hold a value of
+ type vartype """
+ self._instr(LOAD.for_type(vartype), varidx)
+
+ def store_jvm_var(self, vartype, varidx):
+ """ Loads from jvm slot #varidx, which is expected to hold a value of
+ type vartype """
+ self._instr(STORE.for_type(vartype), varidx)
+
+ def load_from_array(self, elemtype):
+ """ Loads something from an array; the result will be of type 'elemtype'
+ (and hence the array is of type 'array_of(elemtype)'), where
+ 'elemtype' is a JvmType. Assumes that the array ref and index are
+ already pushed onto stack (in that order). """
+ self._instr(ARRLOAD.for_type(elemtype))
+
+ def store_to_array(self, elemtype):
+ """ Stores something into an array; the result will be of type
+ 'elemtype' (and hence the array is of type
+ 'array_of(elemtype)'), where 'elemtype' is a JvmType. Assumes
+ that the array ref, index, and value are already pushed onto
+ stack (in that order)."""
+ self._instr(ARRLOAD.for_type(elemtype))
+
+ def unique_label(self, desc, mark=False):
""" Returns an opaque, unique label object that can be passed an
- argument for branching opcodes, or the _mark instruction.
+ argument for branching opcodes, or the mark instruction.
'desc' should be a comment describing the use of the label.
It is for decorative purposes only and should be a valid C
- identifier."""
+ identifier.
+
+ 'mark' --- if True, then also calls self.mark() with the new lbl """
labelnum = len(self._labels)
self._labels.append(desc)
- return ('Label', labelnum)
+ res = ('Label', labelnum)
+ if mark:
+ self.mark(res)
+ return res
+
+ # __________________________________________________________________
+ # Exception Handling
- def _mark(self, lbl):
- """ Marks the point that a label indicates. """
- unimplemented
+ def begin_try(self):
+ """
+ Begins a try/catch region. Must be followed by a call to end_try()
+ after the code w/in the try region is complete.
+ """
+ self.begintrylbl = self.unique_label("begin_try", mark=True)
- def _instr(self, opcode, *args):
- """ Emits an instruction with the given opcode and arguments.
- The correct opcode and their types depends on the opcode. """
- unimplemented
+ def end_try(self):
+ """
+ Ends a try/catch region. Must be followed immediately
+ by a call to begin_catch().
+ """
+ self.endtrylbl = self.unique_label("end_try", mark=True)
+
+ def begin_catch(self, excclsty):
+ """
+ Begins a catch region corresponding to the last try; there can
+ be more than one call to begin_catch, in which case the last
+ try region is reused.
+ 'excclsty' --- a JvmType for the class of exception to be caught
+ """
+ catchlbl = self.unique_label("catch")
+ self.mark(catchlbl, mark=True)
+ self.try_catch_region(
+ excclsty, self.begintrylbl, send.endtrylbl, catchlbl)
+ def end_catch(self):
+ """
+ Ends a catch region.
+ (Included for CLI compatibility)
+ """
+ return
+
+ def try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
+ """
+ Indicates a try/catch region:
+ 'excclsty' --- a JvmType for the class of exception to be caught
+ 'trystartlbl', 'tryendlbl' --- labels marking the beginning and end
+ of the try region
+ 'catchlbl' --- label marking beginning of catch region
+ """
+ unimplemented
+
# __________________________________________________________________
# Generator methods and others that are invoked by MicroInstructions
#
# These translate into calls to the above methods.
def emit(self, instr, *args):
- """ 'instr' in our case must be the name of another method, or
- a JVM opcode (as named above) """
-
- if hasattr(self, instr):
+ """ 'instr' in our case must be either a string, in which case
+ it is the name of a method to invoke, or an Opcode/Method
+ object (defined above)."""
+
+ if isinstance(instr, str):
return getattr(self, instr)(*args)
-
- glob = globals()
- if instr in glob:
- val = glob[instr]
- if isinstance(val, Opcode):
- self._instr(glob[opcode], *args)
- else if isinstance(val, Method):
- val.invoke(self)
- assert False
+ if isinstance(instr, Opcode):
+ return self._instr(instr, *args)
+
+ if isinstance(instr, Method):
+ return instr.invoke(self)
+
+ raise Exception("Unknown object in call to emit(): "+repr(instr))
+
+ def _var_data(self, v):
+ # Determine java type:
+ jty = self.type_system.ootype_to_jvm(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
def load(self, v):
- unimplemented
+ if isinstance(v, flowmodel.Variable):
+ jty, idx = _var_data(v)
+ return self.load_jvm_var(jty, idx)
+
+ if isinstance(v, flowmodel.Constant):
+ # TODO: Refactor and complete this code? Maybe more like cli code?
+ if TYPE is ootype.Void:
+ pass
+ elif TYPE is ootype.Bool:
+ self._instr(ICONST, int(value))
+ elif TYPE is ootype.Char or TYPE is ootype.UniChar:
+ self._instr(ICONST, ord(value))
+ elif isinstance(value, CDefinedIntSymbolic):
+ self._instr(ICONST, DEFINED_INT_SYMBOLICS[value.expr])
+ elif TYPE in (ootype.Signed, ootype.Unsigned):
+ self._instr(ICONST, value) # handle Unsigned better!
+
+ raise Exception('Unexpected type for v in load(): '+v)
def store(self, v):
- unimplemented
+ if isinstance(v, flowmodel.Variable):
+ jty, idx = _var_data(v)
+ return self.store_jvm_var(jty, idx)
+ raise Exception('Unexpected type for v in store(): '+v)
def set_field(self, concretetype, value):
self._instr(SETFIELD, concretetype, value)
@@ -185,6 +442,13 @@
# __________________________________________________________________
# Methods invoked directly by strings in jvm/opcode.py
+ def goto(self, lbl):
+ self._instr(GOTO, lbl)
+
+ def throw(self):
+ """ Throw the object from top of the stack as an exception """
+ self._instr(ATHROW)
+
def iabs(self):
MATHIABS.invoke(self)
@@ -196,6 +460,10 @@
self._instr(ICONST, -1)
self._instr(IXOR)
+ def goto_if_true(self, label):
+ """ Jumps if the top of stack is true """
+ self._instr(IFNE, label)
+
##### Comparison methods
def _compare_op(self, cmpopcode):
@@ -206,14 +474,14 @@
instruction equals zero]. Consumes as many operands from the
stack as the cmpopcode consumes, typically 1 or 2.
"""
- midlbl = self._unique_label()
- endlbl = self._unique_label()
+ midlbl = self.unique_label('cmpop')
+ endlbl = self.unique_label('cmpop')
self._instr(cmpopcode, midlbl)
self._instr(ICONST, 0)
self._instr(GOTO, endlbl)
- self._mark(midlbl)
+ self.mark(midlbl)
self._instr(ICONST, 1)
- self._mark(endlbl)
+ self.mark(endlbl)
logical_not = lambda self: self._compare_op(IFEQ)
equals_zero = logical_not
@@ -271,4 +539,72 @@
ulong_greater_equals = lambda self: self._ulong_compare_op(IFGE)
class JasminGenerator(JVMGenerator):
- pass
+
+ def __init__(self, outdir, package):
+ self.outdir = outdir
+
+ def begin_class(self, classnm):
+ """
+ classnm --- full Java name of the class (i.e., "java.lang.String")
+ """
+
+ 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')
+
+ # Write the JasminXT header
+ self.out.write(".bytecode XX\n")
+ #self.out.write(".source \n")
+ self.out.write(".class public %s\n" % iclassnm)
+ self.out.write(".super java/lang/Object\n") # ?
+
+ def end_class(self):
+ self.out.close()
+ self.out = None
+
+ def add_field(self, fname, fdesc):
+ # TODO --- Signature for generics?
+ # TODO --- these must appear before methods, do we want to buffer
+ # them up to allow out of order calls to add_field()?
+ assert isinstance(fdesc, JvmType)
+ self.out.write('.field public %s %s\n' % (fname, fdesc))
+
+ def _begin_function(self, funcname, argtypes, rettype, static):
+ # Throws clause? Only use RuntimeExceptions?
+ kw = ['public']
+ if static: kw.append('static')
+ self.out.write('.method %s %s (%s)%s\n' % (
+ funcname, " ".join(kw),
+ "".join(argtypes), rettype))
+
+ def _end_function(self):
+ self.out.write('.end method\n')
+
+ def mark(self, lbl):
+ """ Marks the point that a label indicates. """
+ _, lblnm = lbl
+ assert _ == "Label"
+ self.out.write(' %s:\n' % lblnm)
+
+ def _instr(self, opcode, *args):
+ jvmstr = opcode.jvmstr
+
+ # Hack: this should be somewhere else, just not sure where yet
+ if opcode.flags & CONSTSPEC:
+ if args[0] == -1:
+ jvmstr += "_m1"
+ elif args[0] >= 0 and args[0] <= 5:
+ jvmstr += "_%d" % args[0]
+
+ self.out.write(' %s %s\n' % (
+ jvmstr, " ".join([str(s) for s in args])))
+
+ def try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
+ self.out.write(' .catch %s from %s to %s using %s\n' % (
+ excclsty.int_class_name(), trystartlbl, tryendlbl, catchlbl))
+
Modified: pypy/dist/pypy/translator/jvm/genjvm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/genjvm.py (original)
+++ pypy/dist/pypy/translator/jvm/genjvm.py Sun Oct 22 01:36:59 2006
@@ -11,7 +11,9 @@
from pypy.translator.jvm.generator import JasminGenerator
from pypy.translator.jvm.option import getoption
from pypy.translator.jvm.database import Database
+from pypy.translator.jvm.typesystem import JvmTypeSystem
from pypy.translator.jvm.log import log
+from pypy.translator.jvm.node import EntryPoint
class JvmError(Exception):
""" Indicates an error occurred in the JVM runtime """
@@ -26,9 +28,13 @@
For those interested in the location of the files, the following
attributes exist:
- tmpdir --- root directory from which all files can be found
- javadir --- the directory containing *.java
- classdir --- the directory where *.class will be generated
+ tmpdir --- root directory from which all files can be found (py.path obj)
+ javadir --- the directory containing *.java (py.path obj)
+ classdir --- the directory where *.class will be generated (py.path obj)
+ package --- a string with the name of the package (i.e., 'java.util')
+
+ The following attributes also exist to find the state of the sources:
+ compiled --- True once the sources have been compiled successfully
"""
def __init__(self, tmpdir, package):
@@ -39,11 +45,12 @@
"""
self.tmpdir = tmpdir
self.package = package
+ self.compiled = False
# Compute directory where .java files are
self.javadir = self.tmpdir
for subpkg in package.split('.'):
- self.srcdir = os.path.join(self.srcdir, subpkg)
+ self.javadir = self.javadir.join(subpkg)
# Compute directory where .class files should go
self.classdir = self.javadir
@@ -51,12 +58,14 @@
def compile(self):
"""
Compiles the .java sources into .class files, ready for execution.
+ Raises a JvmError if compilation fails.
"""
javac = getoption('javac')
- javafiles = [f for f in os.listdir(self.javadir)
+ javafiles = [f for f in self.javadir.listdir()
if f.endswith('.java')]
res = subprocess.call([javac] + javafiles)
if res: raise JvmError('Failed to compile!')
+ else: self.compiled = True
def execute(self, args):
"""
@@ -64,6 +73,7 @@
output as a string. The 'args' are provided as arguments,
and will be converted to strings.
"""
+ assert self.compiled
strargs = [str(a) for a in args]
cmd = [getoption('java'), '%s.Main' % self.package]
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
@@ -81,12 +91,12 @@
t = TranslationContext()
ann = t.buildannotator()
ann.build_types(func, annotation)
- t.buildrtype(type_system="ootype").specialize()
+ t.buildrtyper(type_system="ootype").specialize()
main_graph = t.graphs[0]
if getoption('view'): t.view()
if getoption('wd'): tmpdir = py.path.local('.')
else: tmpdir = udir
- jvm = GenJvm(tmpdir, t)
+ jvm = GenJvm(tmpdir, t, entrypoint=EntryPoint(main_graph, True))
return jvm.generate_source()
class GenJvm(object):
@@ -104,19 +114,18 @@
'entrypoint' --- if supplied, an object with a render method
"""
self.jvmsrc = JvmGeneratedSource(tmpdir, getoption('package'))
- self.db = Database()
+ self.type_system = JvmTypeSystem()
+ self.db = Database(self.type_system)
if entrypoint:
self.db.pending_node(entrypoint)
+ else:
+ self.db.pending_node(EntryPoint(translator.graphs[0], False))
def generate_source(self):
""" Creates the sources, and returns a JvmGeneratedSource object
for manipulating them """
generator = self._create_generator()
- # Deal with entry point
- if not self.db.len_pending():
- # XXX default entry point
-
# Drain worklist
n = 0
while self.db.len_pending():
@@ -129,13 +138,12 @@
(n, total, n*100.0/total))
# Return the source object once we have finished
- generator.all_done()
return self.jvmsrc
def _create_generator(self):
""" Creates and returns a Generator object according to the
configuration. Right now, however, there is only one kind of
generator: JasminGenerator """
- return JasminGenerator(self.jvmsrc.javadir)
+ return JasminGenerator(self.jvmsrc.javadir, self.jvmsrc.package)
Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py (original)
+++ pypy/dist/pypy/translator/jvm/node.py Sun Oct 22 01:36:59 2006
@@ -1,3 +1,16 @@
+"""
+Rendering nodes for the JVM. I suspect that a lot of this could be
+made to be common between CLR and JVM.
+"""
+
+
+from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.ootypesystem import ootype
+from pypy.translator.jvm.typesystem import jStringArray, jVoid, jThrowable
+from pypy.translator.jvm.typesystem import jvm_for_class
+import pypy.translator.jvm.generator as jvmgen
+from pypy.translator.jvm.opcodes import opcodes
+
class Node(object):
def render(self, db, generator):
unimplemented
@@ -10,22 +23,256 @@
testing (see __init__)
"""
- def __init__(self, graph):
+ def __init__(self, graph, expandargs):
"""
'graph' --- The initial graph to invoke from main()
+ 'expandargs' --- controls whether the arguments passed to main()
+ are passed as a list, or expanded to match each argument to the graph
+
+ The 'expandargs' option deserves explanation:
+
+ it will be false for a standalone build, because in that
+ case we want to convert the String[] array that main() receives
+ into a corresponding python List of string objects.
+
+ it will (generally) be true when compiling individual
+ functions, in which case we might be compiling an entry
+ point with a signature like (a:int,b:float) in which case
+ argv[1] should be converted to an integer, and argv[2]
+ should be converted to a float.
"""
self.graph = graph
+ self.expand_arguments = expandargs
pass
+ # XXX --- perhaps this table would be better placed in typesystem.py
+ # so as to constrain the knowledge of lltype and ootype
+ _type_conversion_methods = {
+ ootype.Signed:jvmgen.PYPYSTRTOINT,
+ ootype.Unsigned:jvmgen.PYPYSTRTOUINT,
+ lltype.SignedLongLong:jvmgen.PYPYSTRTOLONG,
+ lltype.UnsignedLongLong:jvmgen.PYPYSTRTOULONG,
+ ootype.Bool:jvmgen.PYPYSTRTOBOOL,
+ ootype.Float:jvmgen.PYPYSTRTODOUBLE,
+ ootype.Char:jvmgen.PYPYSTRTOCHAR
+ }
+
def render(self, db, gen):
gen.begin_class('pypy.Main')
- gen.begin_function('main', 'String[]', static=True)
+ gen.begin_function('main', (), [jStringArray], jVoid, static=True)
- # XXX --- handle arguments somehow! (will probably need some options)
+ # Handle arguments:
+ if self.expand_arguments:
+ # Convert each entry into the array to the desired type by
+ # invoking an appropriate helper function on each one
+ for i, arg in enumerate(self.graph.getargs()):
+ gen.emit(jvmgen.ICONST, i)
+ gen.emit(self._type_conversion_methods[arg.concretetype])
+ else:
+ # Convert the array of strings to a List<String> as the
+ # python method expects
+ arg0 = self.graph.getargs()[0]
+ assert isinstance(arg0.concretetype, ootype.List), str(arg0.concretetype)
+ assert arg0._ITEMTYPE is ootype.String
+ gen.load_jvm_var(0)
+ gen.emit(jvmgen.PYPYARRAYTOLIST)
# Generate a call to this method
- db.method_for_graph(self.graph).invoke(gen)
+ gen.emit(db.method_for_graph(self.graph, static=True))
gen.end_function()
gen.end_class()
+class Function(object):
+
+ """ Represents a function to be emitted. *Note* that it is not a
+ descendant of Node: it cannot be entered into the database
+ worklist. This is because in Java, all functions occur w/in a
+ class: therefore classes as a whole must be placed on the
+ worklist. """
+
+ def __init__(self, classobj, name, jargtypes, jrettype, graph, is_static):
+ """
+ classobj: the Class object this is a part of (even static
+ functions have a class)
+ name: the name of the function
+ jargtypes: JvmType of each argument
+ jrettype: JvmType this function returns
+ graph: the graph representing the body of the function
+ is_static: boolean flag indicate whether func is static (!)
+ """
+ self.classnm = classnm
+ self.name = name
+ self.graph = graph
+ self.jargtypes = jargtypes
+ self.jrettype = jrettype
+ self.is_static = is_static
+
+ def method(self):
+ """ Returns a jvmgen.Method that can invoke this function """
+ if self.is_static: opcode = jvmgen.INVOKESTATIC
+ else: opcode = jvmgen.INVOKEVIRTUAL
+ mdesc = jvm_method_desc(self.jargtypes, self.jrettype)
+ return jvmgen.Method(classnm, self.func_name, mdesc, opcode=opcode)
+
+ def render_func(self, db, gen):
+ if getattr(self.graph.func, 'suggested_primitive', False):
+ assert False, 'Cannot render a suggested_primitive'
+
+ # Prepare argument lists for begin_function call
+ jargvars = []
+ jargtypes = []
+ for arg in self.graph.getargs():
+ if arg.concretetype is ootype.Void: continue
+ jargvars.append(arg)
+ jargtypes.append(db.type_system.ootype_to_jvm(arg.concretetype))
+
+ # Determine return type
+ jrettype = db.type_system.ootype_to_jvm(
+ self.graph.getreturnvar().concretetype)
+
+ # Start the function definition
+ gen.begin_function(self.name, jargvars, jargtypes, jrettype,
+ static=self.is_static)
+
+ # Go through each block and create a label for it; if the
+ # block will be used to catch an exception, add a second label
+ # to catch_labels
+ block_labels = {}
+ #catch_labels = {}
+ for ctr, block in enumerate(graph.iterblocks()):
+ blbl = gen.unique_label('Block_'+ctr)
+ block_labels[block] = blbl
+
+ ## Go through the blocks we may toss exceptions to
+ #if block.exitswitch == flowmodel.c_last_exception:
+ # for link in block.exits:
+ # if link.exitcase is None: continue # return
+ # if link.target not in catch_labels:
+ # catch_labels[link.target] = gen.unique_label('catch')
+
+ # Iterate through the blocks and generate code for them
+ return_blocks = []
+ for block in graph.iterblocks():
+
+ # Mark the beginning of the block, render all the ops, and
+ # then mark the end.
+ gen.mark(block_labels[block][0])
+
+ # Determine whether the last oper in this block may raise an exc
+ handle_exc = (block.exitswitch == flowmodel.c_last_exception)
+
+ # Render the operations; create labels for a try/catch
+ # region around the last operation
+ if block.operations:
+ for op in block.operations[:-1]:
+ self._render_op(op)
+ if handle_exc: trybeglbl = gen.unique_label('try', mark=True)
+ self._render_op(block.operations[-1])
+ if handle_exc: tryendlbl = gen.unique_label('try', mark=True)
+
+ # Handle 'return' blocks: in this case, we return the
+ # variable specified
+ if self._is_return_block(block):
+ return_var = block.inputargs[0]
+ return_ty = ootype_to_jvm(return_var.concretetype)
+ if return_var.concretetype is not Void:
+ self.load(return_var)
+ gen.return_val(return_ty)
+
+ # Handle 'raise' blocks: in this case, we just throw the
+ # variable specified
+ if self._is_raise_block(block):
+ exc = block.inputargs[1]
+ self.load(exc)
+ gen.throw()
+
+ if handle_exc:
+ # search for the "default" block to be executed when
+ # no exception is raised
+ for link in block.exits:
+ if link.exitcase is None:
+ self._copy_link_vars(gen, link)
+ gen.goto(block_labels[link.target])
+
+ # TODO: proper exception handling; we may not want to
+ # use the same model as CLR
+ else:
+ # no exception handling, determine correct link to follow
+ for link in block.exits:
+ self._copy_link_vars(gen, link)
+ target_label = block_labels[link.target]
+ if link.exitcase is None or link is block.exits[-1]:
+ gen.goto(target_label)
+ else:
+ assert type(link.exitcase is bool)
+ assert block.exitswitch is not None
+ gen.load(block.exitswitch)
+ gen.goto_if_true(target_label)
+
+ gen.end_function()
+
+ def _render_op(self, op):
+ instr_list = opcodes.get(op.opname, None)
+ assert getoption('nostop') or instr_list is not None
+ if instr_list: instr_list.render(self, op)
+
+ def _copy_link_vars(self, gen, link):
+ target = link.target
+ for to_load, to_store in zip(link.args, target.inputargs):
+ if to_load.concretetype is not Void:
+ gen.load(to_load)
+ gen.store(to_store)
+
+ def _is_return_block(self, block):
+ return (not block.exits) and len(block.inputargs) == 1
+
+ def _is_raise_block(self, block):
+ return (not block.exits) and len(block.inputargs) == 2
+
+class Class(Node):
+
+ """ Represents a class to be emitted. Note that currently, classes
+ are emitted all in one shot, not piecemeal. """
+
+ def __init__(self, name):
+ """
+ 'name' should be a fully qualified Java class name like
+ "java.lang.String"
+ """
+ self.name = name
+ self.fields = []
+ self.methods = {} # Maps graph -> Function
+ self.rendered = False
+
+ def jvm_type(self):
+ return jvm_for_class(self.name)
+
+ def add_field(self, fieldty, fieldnm):
+ """ Creates a new field in this with type 'fieldty' (a
+ JvmType) and with the name ;fieldnm; (a String). Must be called
+ before render()."""
+ assert not self.rendered
+ self.fields.append((fieldty, fieldnm))
+
+ def has_method_for(self, graph):
+ return graph in self.methods
+
+ 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."""
+ assert not self.rendered
+ self.methods[func.graph] = func
+
+ def render(self, db, gen):
+ self.rendered = True
+ gen.begin_class(self.name)
+
+ for fieldty, fieldnm in self.fields:
+ gen.add_field(fieldty, fieldnm)
+
+ for method in self.methods.values():
+ method.render_func(db, gen)
+
+ gen.end_class(self.name)
Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py (original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py Sun Oct 22 01:36:59 2006
@@ -5,41 +5,48 @@
"""
-from pypy.translator.cli.metavm import Call, CallMethod, RuntimeNew, \
- IndirectCall, GetField, SetField, CastTo, OOString, DownCast, NewCustomDict,\
- CastWeakAdrToPtr, MapException
-from pypy.translator.oosupport.metavm import PushArg, PushAllArgs, StoreResult, InstructionList,\
- New
-from pypy.translator.cli.cts import WEAKREF
+from pypy.translator.oosupport.metavm import \
+ PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing
+import pypy.translator.jvm.generator as jvmgen
+
+def _check_zer(op):
+ # TODO
+ return op
+
+def _check_ovf(op):
+ # TODO
+ return op
+# This table maps the opcodes to micro-ops for processing them.
+# It is post-processed by a function to be found below.
opcodes = {
# __________ object oriented operations __________
- 'new': [New],
- 'runtimenew': [RuntimeNew],
- 'oosetfield': [SetField],
- 'oogetfield': [GetField],
- 'oosend': [CallMethod],
- 'ooupcast': DoNothing,
- 'oodowncast': [DownCast],
- 'oois': 'ceq',
- 'oononnull': [PushAllArgs, 'ldnull', 'ceq']+Not,
- '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()'],
- 'oohash': [PushAllArgs, 'callvirt instance int32 object::GetHashCode()'],
- 'oostring': [OOString],
- 'ooparse_int': [PushAllArgs, 'call int32 [pypylib]pypy.runtime.Utils::OOParseInt(string, int32)'],
- 'oonewcustomdict': [NewCustomDict],
-
- 'same_as': DoNothing,
- 'hint': [PushArg(0), StoreResult],
- 'direct_call': [Call],
- 'indirect_call': [IndirectCall],
-
- 'cast_ptr_to_weakadr': [PushAllArgs, 'newobj instance void class %s::.ctor(object)' % WEAKREF],
- 'cast_weakadr_to_ptr': [CastWeakAdrToPtr],
- 'gc__collect': 'call void class [mscorlib]System.GC::Collect()',
- 'resume_point': Ignore,
+ #'new': [New],
+ #'runtimenew': [RuntimeNew],
+ #'oosetfield': [SetField],
+ #'oogetfield': [GetField],
+ #'oosend': [CallMethod],
+ #'ooupcast': DoNothing,
+ #'oodowncast': [DownCast],
+ #'oois': 'ceq',
+ #'oononnull': [PushAllArgs, 'ldnull', 'ceq']+Not,
+ #'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()'],
+ #'oohash': [PushAllArgs, 'callvirt instance int32 object::GetHashCode()'],
+ #'oostring': [OOString],
+ #'ooparse_int': [PushAllArgs, 'call int32 [pypylib]pypy.runtime.Utils::OOParseInt(string, int32)'],
+ #'oonewcustomdict': [NewCustomDict],
+ #
+ #'same_as': DoNothing,
+ #'hint': [PushArg(0), StoreResult],
+ #'direct_call': [Call],
+ #'indirect_call': [IndirectCall],
+ #
+ #'cast_ptr_to_weakadr': [PushAllArgs, 'newobj instance void class %s::.ctor(object)' % WEAKREF],
+ #'cast_weakadr_to_ptr': [CastWeakAdrToPtr],
+ #'gc__collect': 'call void class [mscorlib]System.GC::Collect()',
+ #'resume_point': Ignore,
# __________ numeric operations __________
@@ -56,126 +63,132 @@
'unichar_ne': 'not_equals',
'int_is_true': 'not_equals_zero',
- 'int_neg': 'INEG',
+ 'int_neg': jvmgen.INEG,
'int_neg_ovf': None, # How to handle overflow?
'int_abs': 'iabs',
'int_abs_ovf': _check_ovf('iabs'),
'int_invert': 'bitwise_negate',
- 'int_add': 'IADD',
- 'int_sub': 'ISUB',
- 'int_mul': 'IMUL',
- 'int_floordiv': 'IDIV',
- 'int_floordiv_zer': _check_zer('IDIV'),
- 'int_mod': 'IREM',
+ 'int_add': jvmgen.IADD,
+ 'int_sub': jvmgen.ISUB,
+ 'int_mul': jvmgen.IMUL,
+ 'int_floordiv': jvmgen.IDIV,
+ 'int_floordiv_zer': _check_zer(jvmgen.IDIV),
+ 'int_mod': jvmgen.IREM,
'int_lt': 'less_than',
'int_le': 'less_equals',
'int_eq': 'equals',
'int_ne': 'not_equals',
'int_gt': 'greater_than',
'int_ge': 'greater_equals',
- 'int_and': 'IAND',
- 'int_or': 'IOR',
- 'int_lshift': 'ISHL',
- 'int_rshift': 'ISHR',
- 'int_xor': 'IXOR',
- 'int_add_ovf': _check_ovf('IADD'),
- 'int_sub_ovf': _check_ovf('ISUB'),
- 'int_mul_ovf': _check_ovf('IMUL'),
- 'int_floordiv_ovf': 'IDIV', # these can't overflow!
- 'int_mod_ovf': 'IREM',
+ 'int_and': jvmgen.IAND,
+ 'int_or': jvmgen.IOR,
+ 'int_lshift': jvmgen.ISHL,
+ 'int_rshift': jvmgen.ISHR,
+ 'int_xor': jvmgen.IXOR,
+ 'int_add_ovf': _check_ovf(jvmgen.IADD),
+ 'int_sub_ovf': _check_ovf(jvmgen.ISUB),
+ 'int_mul_ovf': _check_ovf(jvmgen.IMUL),
+ 'int_floordiv_ovf': jvmgen.IDIV, # these can't overflow!
+ 'int_mod_ovf': jvmgen.IREM,
'int_lt_ovf': 'less_than',
'int_le_ovf': 'less_equals',
'int_eq_ovf': 'equals',
'int_ne_ovf': 'not_equals',
'int_gt_ovf': 'greater_than',
'int_ge_ovf': 'greater_equals',
- 'int_and_ovf': 'IAND',
- 'int_or_ovf': 'IOR',
+ 'int_and_ovf': jvmgen.IAND,
+ 'int_or_ovf': jvmgen.IOR,
- 'int_lshift_ovf': _check_ovf('ISHL'),
- 'int_lshift_ovf_val': _check_ovf('ISHL'), # VAL??
+ 'int_lshift_ovf': _check_ovf(jvmgen.ISHL),
+ 'int_lshift_ovf_val': _check_ovf(jvmgen.ISHL), # VAL??
- 'int_rshift_ovf': 'ISHR', # these can't overflow!
- 'int_xor_ovf': 'IXOR',
- 'int_floordiv_ovf_zer': _check_zer('IDIV'),
- 'int_mod_ovf_zer': _check_zer('IREM'),
+ 'int_rshift_ovf': jvmgen.ISHR, # these can't overflow!
+ 'int_xor_ovf': jvmgen.IXOR,
+ 'int_floordiv_ovf_zer': _check_zer(jvmgen.IDIV),
+ 'int_mod_ovf_zer': _check_zer(jvmgen.IREM),
'uint_is_true': 'not_equals_zero',
'uint_invert': 'bitwise_negate',
- 'uint_add': 'IADD',
- 'uint_sub': 'ISUB',
- 'uint_mul': 'IMUL',
- 'uint_div': 'IDIV', # valid?
+ 'uint_add': jvmgen.IADD,
+ 'uint_sub': jvmgen.ISUB,
+ 'uint_mul': jvmgen.IMUL,
+ 'uint_div': jvmgen.IDIV, # valid?
'uint_truediv': None, # TODO
- 'uint_floordiv': 'IDIV', # valid?
- 'uint_mod': 'IREM', # valid?
+ 'uint_floordiv': jvmgen.IDIV, # valid?
+ 'uint_mod': jvmgen.IREM, # valid?
'uint_lt': 'u_less_than',
'uint_le': 'u_less_equals',
'uint_eq': 'u_equals',
'uint_ne': 'u_not_equals',
'uint_gt': 'u_greater_than',
'uint_ge': 'u_greater_equals',
- 'uint_and': 'IAND',
- 'uint_or': 'IOR',
- 'uint_lshift': 'ISHL',
- 'uint_rshift': 'IUSHR',
- 'uint_xor': 'IXOR',
-
- 'float_is_true': [PushAllArgs, 'DCONST_0', 'dbl_not_equals'],
- 'float_neg': 'DNEG',
+ 'uint_and': jvmgen.IAND,
+ 'uint_or': jvmgen.IOR,
+ 'uint_lshift': jvmgen.ISHL,
+ 'uint_rshift': jvmgen.IUSHR,
+ 'uint_xor': jvmgen.IXOR,
+
+ 'float_is_true': [PushAllArgs,
+ jvmgen.DCONST_0,
+ 'dbl_not_equals'],
+ 'float_neg': jvmgen.DNEG,
'float_abs': 'dbl_abs',
- 'float_add': 'DADD',
- 'float_sub': 'DSUB',
- 'float_mul': 'DMUL',
- 'float_truediv': 'DDIV',
- 'float_mod': 'DREM', # use Math.IEEEremainder?
+ 'float_add': jvmgen.DADD,
+ 'float_sub': jvmgen.DSUB,
+ 'float_mul': jvmgen.DMUL,
+ 'float_truediv': jvmgen.DDIV,
+ 'float_mod': jvmgen.DREM, # use Math.IEEEremainder?
'float_lt': 'dbl_less_than',
'float_le': 'dbl_less_equals',
'float_eq': 'dbl_equals',
'float_ne': 'dbl_not_equals',
'float_gt': 'dbl_greater_than',
'float_ge': 'dbl_greater_equals',
- 'float_floor': 'MATHFLOOR',
- 'float_fmod': 'DREM', # DREM is akin to fmod() in C
+ 'float_floor': jvmgen.MATHFLOOR,
+ 'float_fmod': jvmgen.DREM, # DREM is akin to fmod() in C
- 'llong_is_true': [PushAllArgs, 'LCONST_0', 'long_not_equals'],
- 'llong_neg': 'LNEG',
- 'llong_neg_ovf': _check_ovf('LNEG'),
- 'llong_abs': 'MATHLABS',
- 'llong_invert': 'PYPYLONGBITWISENEGATE',
-
- 'llong_add': 'LADD',
- 'llong_sub': 'LSUB',
- 'llong_mul': 'LMUL',
- 'llong_div': 'LDIV',
+ 'llong_is_true': [PushAllArgs,
+ jvmgen.LCONST_0,
+ 'long_not_equals'],
+ 'llong_neg': jvmgen.LNEG,
+ 'llong_neg_ovf': _check_ovf(jvmgen.LNEG),
+ 'llong_abs': jvmgen.MATHLABS,
+ 'llong_invert': jvmgen.PYPYLONGBITWISENEGATE,
+
+ 'llong_add': jvmgen.LADD,
+ 'llong_sub': jvmgen.LSUB,
+ 'llong_mul': jvmgen.LMUL,
+ 'llong_div': jvmgen.LDIV,
'llong_truediv': None, # TODO
- 'llong_floordiv': 'LDIV',
- 'llong_mod': 'LREM',
+ 'llong_floordiv': jvmgen.LDIV,
+ 'llong_mod': jvmgen.LREM,
'llong_lt': 'long_less_than',
'llong_le': 'long_less_equals',
'llong_eq': 'long_equals',
'llong_ne': 'long_not_equals',
'llong_gt': 'long_greater_than',
'llong_ge': 'long_greater_equals',
- 'llong_and': 'LAND',
- 'llong_or': 'LOR',
- 'llong_lshift': 'LSHL',
- 'llong_rshift': 'LSHR',
- 'llong_xor': 'LXOR',
-
- 'ullong_is_true': [PushAllArgs, 'LCONST_0', 'long_not_equals'],
- 'ullong_invert': 'PYPYLONGBITWISENEGATE',
-
- 'ullong_add': 'LADD',
- 'ullong_sub': 'LSUB',
- 'ullong_mul': 'LMUL',
- 'ullong_div': 'LDIV', # valid?
+ 'llong_and': jvmgen.LAND,
+ 'llong_or': jvmgen.LOR,
+ 'llong_lshift': jvmgen.LSHL,
+ 'llong_rshift': jvmgen.LSHR,
+ 'llong_xor': jvmgen.LXOR,
+
+ 'ullong_is_true': [PushAllArgs,
+ jvmgen.LCONST_0,
+ 'long_not_equals'],
+ 'ullong_invert': jvmgen.PYPYLONGBITWISENEGATE,
+
+ 'ullong_add': jvmgen.LADD,
+ 'ullong_sub': jvmgen.LSUB,
+ 'ullong_mul': jvmgen.LMUL,
+ 'ullong_div': jvmgen.LDIV, # valid?
'ullong_truediv': None, # TODO
- 'ullong_floordiv': 'LDIV', # valid?
- 'ullong_mod': 'LREM', # valid?
+ 'ullong_floordiv': jvmgen.LDIV, # valid?
+ 'ullong_mod': jvmgen.LREM, # valid?
'ullong_lt': 'ulong_less_than',
'ullong_le': 'ulong_less_equals',
'ullong_eq': 'ulong_equals',
@@ -189,19 +202,24 @@
# trick.
'cast_bool_to_int': DoNothing,
'cast_bool_to_uint': DoNothing,
- 'cast_bool_to_float': [PushAllArgs, 'not_equals_zero', 'I2D'],
+ 'cast_bool_to_float': [PushAllArgs, 'not_equals_zero', jvmgen.I2D],
'cast_char_to_int': DoNothing,
'cast_unichar_to_int': DoNothing,
'cast_int_to_char': DoNothing,
'cast_int_to_unichar': DoNothing,
'cast_int_to_uint': DoNothing,
- 'cast_int_to_float': 'I2D',
- 'cast_int_to_longlong': 'I2L',
+ 'cast_int_to_float': jvmgen.I2D,
+ 'cast_int_to_longlong': jvmgen.I2L,
'cast_uint_to_int': DoNothing,
- 'cast_uint_to_float': PYPYUINTTODOUBLE,
- 'cast_float_to_int': 'D2I',
- 'cast_float_to_uint': PYPYDOUBLETOUINT,
- 'truncate_longlong_to_int': 'L2I',
+ 'cast_uint_to_float': jvmgen.PYPYUINTTODOUBLE,
+ 'cast_float_to_int': jvmgen.D2I,
+ 'cast_float_to_uint': jvmgen.PYPYDOUBLETOUINT,
+ 'truncate_longlong_to_int': jvmgen.L2I,
}
+
+for opc in opcodes:
+ val = opcodes[opc]
+ if not isinstance(val, list):
+ val = [PushAllArgs, val]
Added: pypy/dist/pypy/translator/jvm/option.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/option.py Sun Oct 22 01:36:59 2006
@@ -0,0 +1,4 @@
+from pypy.translator.jvm.conftest import option
+
+def getoption(name):
+ return getattr(option, 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 Sun Oct 22 01:36:59 2006
@@ -1,12 +1,35 @@
package pypy;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Class with a number of utility routines.
+ */
public class PyPy {
+ /**
+ * Compares two unsigned integers (value1 and value2) and returns
+ * a value greater than, equal to, or less than zero if value 1 is
+ * respectively greater than, equal to, or less than value2. The
+ * idea is that you can do the following:
+ *
+ * Call uint_cmp(value1, value2)
+ * IFLT ... // jumps if value1 < value2
+ * IFEQ ... // jumps if value1 == value2
+ * IFGT ... // jumps if value1 > value2
+ * etc
+ */
public static int uint_cmp(int value1, int value2) {
- final int VALUE2BIGGER = -1;
final int VALUE1BIGGER = 1;
+ final int VALUE2BIGGER = -1;
final int EQUAL = 0;
+ if (((value1 | value2) & Integer.MIN_VALUE) == 0) {
+ // neither is negative, presumably the common case
+ return value1 - value2;
+ }
+
if (value1 == value2)
return EQUAL;
@@ -23,13 +46,8 @@
return VALUE1BIGGER;
}
}
- else if (value2 < 0) {
- // value1 is not neg, value2 is neg
- return VALUE2BIGGER;
- }
-
- if (value1 > value2)
- return VALUE1BIGGER;
+
+ // value1 is not neg, value2 is neg
return VALUE2BIGGER;
}
@@ -92,4 +110,76 @@
public static long long_bitwise_negate(long value) {
return ~value;
}
+
+ public static List<?> array_to_list(Object[] array) {
+ List<?> l = new ArrayList();
+ for (Object o : array) {
+ l.add(o);
+ }
+ return l;
+ }
+
+ public static int str_to_int(String s) {
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException fe) {
+ throw new RuntimeException(fe);
+ }
+ }
+
+ public static int str_to_uint(String s) {
+ try {
+ long l = Long.parseLong(s);
+ if (l < Integer.MAX_VALUE)
+ return l;
+ int lowerword = l & 0xFFFF;
+ int upperword = l >> 16;
+ return lowerword + (upperword << 16);
+ } catch (NumberFormatException fe) {
+ throw new RuntimeException(fe);
+ }
+ }
+
+ public static long str_to_long(String s) {
+ try {
+ return Long.parseLong(s);
+ } catch (NumberFormatException fe) {
+ throw new RuntimeException(fe);
+ }
+ }
+
+ public static long str_to_ulong(String s) {
+ // oh bother
+ throw new RuntimeException("TODO--- str to ulong");
+ }
+
+ public static boolean str_to_bool(String s) {
+ // not sure what are considered valid boolean values...
+ // let's be very accepting and take both strings and numbers
+ if (s.equalsIgnoreCase("true"))
+ return true;
+ if (s.equalsIgnoreCase("false"))
+ return false;
+
+ try {
+ int i = Integer.parseInt(s);
+ return i != 0;
+ } catch (NumberFormatException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static double str_to_double(String s) {
+ try {
+ return Double.parseDouble(s);
+ } catch (NumberFormatException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static char str_to_char(String s) {
+ if (s.length() != 1)
+ throw new RuntimeException("String not single character: '"+s+"'");
+ return s.charAt(0);
+ }
}
\ No newline at end of file
Added: pypy/dist/pypy/translator/jvm/test/__init__.py
==============================================================================
Added: pypy/dist/pypy/translator/jvm/test/runtest.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/test/runtest.py Sun Oct 22 01:36:59 2006
@@ -0,0 +1,119 @@
+import os
+import platform
+
+import py
+from py.compat import subprocess
+from pypy.tool.udir import udir
+from pypy.rpython.test.tool import BaseRtypingTest, OORtypeMixin
+from pypy.rpython.lltypesystem.lltype import typeOf
+from pypy.rpython.ootypesystem import ootype
+from pypy.annotation.model import lltype_to_annotation
+from pypy.translator.translator import TranslationContext
+from pypy.translator.jvm.genjvm import generate_source_for_function
+from pypy.translator.jvm.option import getoption
+
+FLOAT_PRECISION = 8
+
+# CLI duplicate
+class StructTuple(tuple):
+ def __getattr__(self, name):
+ if name.startswith('item'):
+ i = int(name[len('item'):])
+ return self[i]
+ else:
+ raise AttributeError, name
+
+# CLI duplicate
+class OOList(list):
+ def ll_length(self):
+ return len(self)
+
+ def ll_getitem_fast(self, i):
+ return self[i]
+
+# CLI duplicate
+class ExceptionWrapper:
+ def __init__(self, class_name):
+ self.class_name = class_name
+
+ def __repr__(self):
+ return 'ExceptionWrapper(%s)' % repr(self.class_name)
+
+# CLI could-be duplicate
+class JvmGeneratedSourceWrapper(object):
+ def __init__(self, gensrc):
+ """ gensrc is an instance of JvmGeneratedSource """
+ self.gensrc = gensrc
+
+ def __call__(self, *args):
+ if not self.gensrc.compiled:
+ py.test.skip("Assembly disabled")
+
+ if getoption('norun'):
+ py.test.skip("Execution disabled")
+
+ resstr = self.gensrc.execute(args)
+ res = eval(resstr)
+ if isinstance(res, tuple):
+ res = StructTuple(res) # so tests can access tuple elements with .item0, .item1, etc.
+ elif isinstance(res, list):
+ res = OOList(res)
+ return res
+
+class JvmTest(BaseRtypingTest, OORtypeMixin):
+ def __init__(self):
+ self._func = None
+ self._ann = None
+ self._jvm_src = None
+
+ def _compile(self, fn, args, ann=None):
+ if ann is None:
+ ann = [lltype_to_annotation(typeOf(x)) for x in args]
+ if self._func is fn and self._ann == ann:
+ return JvmGeneratedSourceWrapper(self._jvm_src)
+ else:
+ self._func = fn
+ self._ann = ann
+ self._jvm_src = generate_source_for_function(fn, ann)
+ if not getoption('noasm'):
+ self._jvm_src.compile()
+ return JvmGeneratedSourceWrapper(self._jvm_src)
+
+ def _skip_win(self, reason):
+ if platform.system() == 'Windows':
+ py.test.skip('Windows --> %s' % reason)
+
+ def interpret(self, fn, args, annotation=None):
+ py.test.skip("jvm tests don't work yet")
+ src = self._compile(fn, args, annotation)
+ res = src(*args)
+ if isinstance(res, ExceptionWrapper):
+ raise res
+ return res
+
+ def interpret_raises(self, exception, fn, args):
+ import exceptions # needed by eval
+ try:
+ self.interpret(fn, args)
+ except ExceptionWrapper, ex:
+ assert issubclass(eval(ex.class_name), exception)
+ else:
+ assert False, 'function did not raise any exception at all'
+
+ def float_eq(self, x, y):
+ return round(x, FLOAT_PRECISION) == round(y, FLOAT_PRECISION)
+
+ def ll_to_string(self, s):
+ return s
+
+ def ll_to_list(self, l):
+ return l
+
+ def class_name(self, value):
+ return value.class_name.split(".")[-1]
+
+ def is_of_instance_type(self, val):
+ return isinstance(val, InstanceWrapper)
+
+ def read_attr(self, obj, name):
+ py.test.skip('read_attr not supported on genjvm tests')
Added: pypy/dist/pypy/translator/jvm/test/test_bool.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/test/test_bool.py Sun Oct 22 01:36:59 2006
@@ -0,0 +1,7 @@
+import py
+from pypy.translator.jvm.test.runtest import JvmTest
+from pypy.rpython.test.test_rbool import BaseTestRbool
+
+class TestJvmBool(JvmTest, BaseTestRbool):
+ pass
+
Added: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/typesystem.py Sun Oct 22 01:36:59 2006
@@ -0,0 +1,157 @@
+"""
+Translation between PyPy ootypesystem and JVM type system.
+
+Here are some tentative non-obvious decisions:
+
+Signed scalar types mostly map as is.
+
+Unsigned scalar types are a problem; the basic idea is to store them
+as signed values, but execute special code when working with them. Another
+option would be to use classes, or to use the "next larger" type and remember to use appropriate modulos. The jury is out on
+this. Another idea would be to add a variant type system that does
+not have unsigned values, and write the required helper and conversion
+methods in RPython --- then it could be used for multiple backends.
+
+Python strings are mapped to byte arrays, not Java Strings, since
+Python strings are really sets of bytes, not unicode code points.
+Jury is out on this as well; this is not the approach taken by cli,
+for example.
+
+Python Unicode strings, on the other hand, map directly to Java Strings.
+
+WeakRefs can hopefully map to Java Weak References in a straight
+forward fashion.
+
+Collections can hopefully map to Java collections instances. Note
+that JVM does not have an idea of generic typing at its lowest level
+(well, they do have signature attributes, but those don't really count
+for much).
+
+"""
+from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.ootypesystem import ootype
+from pypy.translator.jvm.option import getoption
+from pypy.translator.jvm.log import log
+
+class JvmType(str):
+ """
+ The class we use to represent JVM types; it is just a string with
+ the JVM type descriptor at the moment. Using JvmType allows us to
+ use isinstance, however. The grammar for type descriptors can be
+ read about here:
+ http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html
+ """
+ def is_scalar(self):
+ return self[0] != 'L' and self[0] != '['
+ def is_reference(self):
+ return not self.is_scalar()
+ def is_array(self):
+ return self[0] == '['
+ def int_class_name(self):
+ """ Converts a descriptor like Ljava/lang/Object; to
+ java/lang/Object """
+ assert self[0] == 'L' and self[-1] == ';'
+ return self[1:-1]
+ def type_width(self):
+ """ Returns number of JVM words this type takes up. JVM words
+ are a theoretically abstract quantity that basically
+ represents 32 bits; so most types are 1, but longs and doubles
+ are 2. """
+ if self[0] == 'J' or self[0] == 'D':
+ return 2
+ return 1
+
+# JVM type functions
+
+def jvm_array_of(jtype):
+ """ Returns a JvmType representing an array of 'jtype', which must be
+ another JvmType """
+ assert isinstance(jtype, JvmType)
+ return JvmType('['+str(jtype))
+
+def jvm_for_class(classnm):
+ """ Returns a JvmType representing a particular class 'classnm', which
+ should be a fully qualified java class name (i.e., 'java.lang.String') """
+ return JvmType('L%s;' % classnm.replace('.','/'))
+
+# Common JVM types
+jVoid = JvmType('V')
+jInt = JvmType('I')
+jLong = JvmType('J')
+jBool = JvmType('Z')
+jDouble = JvmType('D')
+jByte = JvmType('B')
+jByteArray = jvm_array_of(jByte)
+jChar = JvmType('C')
+jThrowable = jvm_for_class('java.lang.Throwable')
+jObject = jvm_for_class('java.lang.Object')
+jString = jvm_for_class('java.lang.String')
+jStringArray = jvm_array_of(jString)
+jArrayList = jvm_for_class('java.util.ArrayList')
+jHashMap = jvm_for_class('java.util.HashMap')
+jIterator = jvm_for_class('java.util.Iterator')
+jClass = jvm_for_class('java.lang.Class')
+jStringBuilder = jvm_for_class('java.lang.StringBuilder')
+
+# Map from OOType to an internal JVM type descriptor
+_lltype_to_jvm = {
+ ootype.Void: jVoid,
+ ootype.Signed: jInt,
+ ootype.Unsigned: jInt,
+ lltype.SignedLongLong: jLong,
+ lltype.UnsignedLongLong: jLong,
+ ootype.Bool: jBool,
+ ootype.Float: jDouble,
+ ootype.Char: jByte,
+ ootype.UniChar: jChar,
+ ootype.String: jByteArray,
+ ootype.ROOT: jObject,
+
+ # We may want to use PyPy wrappers here later:
+ llmemory.WeakGcAddress: jObject, # XXX
+ ootype.StringBuilder: jStringBuilder,
+ ootype.Class: jClass,
+ ootype.List: jArrayList,
+ ootype.Dict: jHashMap,
+ ootype.DictItemsIterator:jIterator
+ }
+
+# Method descriptor construction
+def jvm_method_desc(argtypes, rettype):
+ """ A Java method has a descriptor, which is a string specified
+ its argument and return types. This function converts a list of
+ argument types (JvmTypes) and the return type (also a JvmType),
+ into one of these descriptor strings. """
+ return "(%s)%s" % ("".join(argtypes), rettype)
+
+class JvmTypeSystem(object):
+
+ """ This object translates between the OOTypeSystem and JVM type
+ descriptors. """
+
+ def enforce_jvm(self, typ):
+ if isinstance(typ, JvmType):
+ return typ
+ return self.ootype_to_jvm(typ)
+
+ def ootype_to_jvm(self, oot):
+ """ Returns an instance of JvmType corresponding to the given
+ OOType """
+
+ # Check the easy cases
+ if oot in _lltype_to_jvm:
+ return _lltype_to_jvm[oot]
+
+ # Now handle the harder ones
+ if isinstance(oot, lltype.Ptr) and isinstance(t.TO, lltype.OpaqueType):
+ return jObject
+ if isinstance(oot, ootype.Instance):
+ return XXX
+ if isinstance(oot, ootype.Record):
+ return XXX
+ if isinstance(oot, ootype.StaticMethod):
+ return XXX
+
+ # Uh-oh
+ unhandled_case
+
Modified: pypy/dist/pypy/translator/oosupport/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/metavm.py (original)
+++ pypy/dist/pypy/translator/oosupport/metavm.py Sun Oct 22 01:36:59 2006
@@ -98,6 +98,10 @@
def __call__(self, *args):
return self.render(*args)
+
+class _DoNothing(MicroInstruction):
+ def render(self, generator, op):
+ pass
class PushArg(MicroInstruction):
""" Pushes a given operand onto the stack. """
@@ -227,3 +231,4 @@
SetField = _SetField()
GetField = _GetField()
DownCast = _DownCast()
+DoNothing = _DoNothing()
More information about the Pypy-commit
mailing list