[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