[pypy-svn] r37135 - in pypy/dist/pypy/translator/jvm: . src/pypy test

niko at codespeak.net niko at codespeak.net
Mon Jan 22 14:23:58 CET 2007


Author: niko
Date: Mon Jan 22 14:23:57 2007
New Revision: 37135

Added:
   pypy/dist/pypy/translator/jvm/prebuiltnodes.py
   pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java
Modified:
   pypy/dist/pypy/translator/jvm/builtin.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/metavm.py
   pypy/dist/pypy/translator/jvm/node.py
   pypy/dist/pypy/translator/jvm/opcodes.py
   pypy/dist/pypy/translator/jvm/src/pypy/PyPy.java
   pypy/dist/pypy/translator/jvm/test/test_string.py
   pypy/dist/pypy/translator/jvm/typesystem.py
Log:
Add the code needed for java code to throw exceptions.

We generate an instance of the Interlink interface which
has the various methods required, and assign it to a static
variable in the PyPy class.  This has the downside of only
allowing one concurrent PyPy interpreter: we could use thread-local
data instead (or pass the object as a parameter, but that would be
more of a pain).



Modified: pypy/dist/pypy/translator/jvm/builtin.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/builtin.py	(original)
+++ pypy/dist/pypy/translator/jvm/builtin.py	Mon Jan 22 14:23:57 2007
@@ -89,9 +89,6 @@
     (ootype.String.__class__, "ll_strlen"):
     jvmgen.Method.v(jString, "length", (), jInt),
     
-    (ootype.String.__class__, "ll_stritem_nonneg"):
-    jvmgen.Method.v(jString, "charAt", (jInt,), jChar),
-
     (ootype.String.__class__, "ll_startswith"):
     jvmgen.Method.v(jString, "startsWith", (jString,), jBool),
 

Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py	(original)
+++ pypy/dist/pypy/translator/jvm/database.py	Mon Jan 22 14:23:57 2007
@@ -76,6 +76,25 @@
     #
     # Creates nodes that represents classes, functions, simple constants.
 
+    def create_interlink_node(self, methods):
+        """ This is invoked by create_interlinke_node() in
+        jvm/prebuiltnodes.py.  It creates a Class node that will
+        be an instance of the Interlink interface, which is used
+        to allow the static java code to throw PyPy exceptions and the
+        like.
+
+        The 'methods' argument should be a dictionary whose keys are
+        method names and whose entries are jvmgen.Method objects which
+        the corresponding method should invoke. """
+
+        nm = self._pkg(self._uniq('InterlinkImplementation'))
+        cls = node.Class(nm, supercls=jObject)
+        for method_name, helper in methods.items():
+            cls.add_method(node.InterlinkFunction(cls, method_name, helper))
+        cls.add_interface(jvmtype.jPyPyInterlink)
+        self.interlink_class = cls
+        self.pending_node(cls)
+
     def types_for_graph(self, graph):
         """
         Given a graph, returns a tuple like so:
@@ -98,7 +117,7 @@
         the method to 'classobj', which should be a node.Class object.
         """
         jargtypes, jrettype = self.types_for_graph(graph)
-        funcobj = node.Function(
+        funcobj = node.GraphFunction(
             self, classobj, funcnm, jargtypes, jrettype, graph, is_static)
         return funcobj
     

Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py	(original)
+++ pypy/dist/pypy/translator/jvm/generator.py	Mon Jan 22 14:23:57 2007
@@ -10,7 +10,7 @@
      jPyPy, jVoid, jMath, desc_for_method, jPrintStream, jClass, jChar, \
      jObject, jByteArray, jPyPyExcWrap, jIntegerClass, jLongClass, \
      jDoubleClass, jCharClass, jStringBuilder, JvmScalarType, jArrayList, \
-     jObjectArray
+     jObjectArray, jPyPyInterlink
 
 # ___________________________________________________________________________
 # Miscellaneous helper functions
@@ -411,6 +411,8 @@
 DOUBLEPOSINF = Field('java.lang.Double', 'POSITIVE_INFINITY', jDouble, True)
 DOUBLENEGINF = Field('java.lang.Double', 'NEGATIVE_INFINITY', jDouble, True)
 
+PYPYINTERLINK= Field(jPyPy.name, 'interlink', jPyPyInterlink, True)
+
 EXCWRAPOBJ =   Field(jPyPyExcWrap.name, 'object', jObject, False)
 
 # ___________________________________________________________________________
@@ -479,7 +481,8 @@
         looks like:
 
         begin_class()
-        [add_field()]
+        {implements()}
+        {add_field()}
         begin_constructor()...end_constructor()
         [begin_function()...end_function()]
         end_class()
@@ -511,6 +514,13 @@
         """ Main implementation of end_class """
         raise NotImplementedError    
 
+    def implements(self, jinterface):
+        """
+        Indicates that the current class implements the interface
+        jinterface, which should be a JvmType.
+        """
+        raise NotImplementedError
+
     def add_field(self, fobj):
         """
         fobj: a Field object
@@ -728,7 +738,7 @@
         'excclsty' --- a JvmType for the class of exception to be caught
         """
         catchlbl = self.unique_label("catch", mark=True)
-        self._try_catch_region(
+        self.try_catch_region(
             jPyPyExcWrap, self.begintrylbl, self.endtrylbl, catchlbl)
 
         # emit the code to unwrap the exception, check the type
@@ -754,9 +764,13 @@
         """
         return
         
-    def _try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
+    def try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
         """
-        Indicates a try/catch region:
+        Indicates a try/catch region.
+
+        Either invoked directly, or from the begin_catch() routine:
+        the latter is invoked by the oosupport code.
+        
         'excclsty' --- a JvmType for the class of exception to be caught
         'trystartlbl', 'tryendlbl' --- labels marking the beginning and end
         of the try region
@@ -1165,6 +1179,10 @@
     def add_comment(self, comment):
         self.curclass.out("  ; %s\n" % comment)
 
+    def implements(self, jinterface):
+        self.curclass.out(
+            '.implements ' + jinterface.descriptor.int_class_name() + '\n')
+        
     def add_field(self, fobj):
         kw = ['public']
         if fobj.is_static: kw.append('static')
@@ -1208,11 +1226,10 @@
         strargs = [jasmin_syntax(arg) for arg in args]
         instr_text = '%s %s' % (jvmstr, " ".join(strargs))
         self.curclass.out('    .line %d\n' % self.curfunc.instr_counter)
-        self.curclass.out('    %-60s\n' % (
-            instr_text,))
+        self.curclass.out('    %-60s\n' % (instr_text,))
         self.curfunc.instr_counter+=1
 
-    def _try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
+    def try_catch_region(self, excclsty, trystartlbl, tryendlbl, catchlbl):
         self.curclass.out('  .catch %s from %s to %s using %s\n' % (
             excclsty.descriptor.int_class_name(),
             trystartlbl.jasmin_syntax(),

Modified: pypy/dist/pypy/translator/jvm/genjvm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/genjvm.py	(original)
+++ pypy/dist/pypy/translator/jvm/genjvm.py	Mon Jan 22 14:23:57 2007
@@ -2,9 +2,10 @@
 Backend for the JVM.
 """
 
-import os, os.path, subprocess, sys
+import os, os.path, sys
 
 import py
+from py.compat import subprocess
 from pypy.tool.udir import udir
 from pypy.translator.translator import TranslationContext
 from pypy.translator.oosupport.genoo import GenOO
@@ -18,6 +19,7 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.translator.jvm.constant import \
      JVMConstantGenerator, JVMStaticMethodConst
+from pypy.translator.jvm.prebuiltnodes import create_interlink_node
 
 class JvmError(Exception):
     """ Indicates an error occurred in JVM backend """
@@ -128,7 +130,8 @@
         self.compiled = True
         self._compile_helper(('DictItemsIterator',
                               'PyPy',
-                              'ExceptionWrapper'))
+                              'ExceptionWrapper',
+                              'Interlink'))
 
     def _make_str(self, a):
         if isinstance(a, ootype._string):
@@ -206,6 +209,7 @@
         'entrypoint' --- if supplied, an object with a render method
         """
         GenOO.__init__(self, tmpdir, translator, entrypoint)
+        create_interlink_node(self.db)
         self.jvmsrc = JvmGeneratedSource(tmpdir, getoption('package'))
 
     def generate_source(self):

Modified: pypy/dist/pypy/translator/jvm/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/metavm.py	(original)
+++ pypy/dist/pypy/translator/jvm/metavm.py	Mon Jan 22 14:23:57 2007
@@ -49,5 +49,43 @@
         self._invoke_method(gen, gen.db, jmethod,
                             jactargs, op.args[2:],
                             jmethod.return_type, op.result)
-
 JvmCallMethod = _JvmCallMethod()
+
+class TranslateException(MicroInstruction):
+    """ Translates an exception into a call of a method on the PyPy object """
+    def __init__(self, jexc, pexcmthd, inst):
+        """
+        jexc: the JvmType of the exception
+        pexcmthd: the name of the method on the PyPy object to call.
+        The PyPy method must take no arguments, return void, and must
+        always throw an exception in practice.
+        """
+        self.java_exc = jexc
+        self.pypy_method = jvmgen.Method.s(
+            jvmtype.jPyPy, pexcmthd, [], jvmtype.jVoid)
+        self.instruction = inst
+        
+    def render(self, gen, op):
+        trylbl = gen.unique_label('translate_exc_begin')
+        catchlbl = gen.unique_label('translate_exc_catch')
+        donelbl = gen.unique_label('translate_exc_done')
+
+        # try {
+        gen.mark(trylbl)
+        self.instruction.render(gen, op)
+        gen.goto(donelbl)
+        # } catch (JavaExceptionType) {
+        gen.mark(catchlbl)
+        gen.emit(jvmgen.POP)
+        gen.emit(self.pypy_method)
+        # Note: these instructions will never execute, as we expect
+        # the pypy_method to throw an exception and not to return.  We
+        # need them here to satisfy the Java verifier, however, as it
+        # does not know that the pypy_method will never return.
+        gen.emit(jvmgen.ACONST_NULL)
+        gen.emit(jvmgen.ATHROW)
+        # }
+        gen.mark(donelbl)
+
+        gen.try_catch_region(self.java_exc, trylbl, catchlbl, catchlbl)
+        

Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py	(original)
+++ pypy/dist/pypy/translator/jvm/node.py	Mon Jan 22 14:23:57 2007
@@ -22,7 +22,7 @@
      ootype, rclass
 from pypy.translator.jvm.typesystem import \
      JvmClassType, jString, jStringArray, jVoid, jThrowable, jInt, jPyPyMain, \
-     jObject, JvmType, jStringBuilder
+     jObject, JvmType, jStringBuilder, jPyPyInterlink
 from pypy.translator.jvm.opcodes import \
      opcodes
 from pypy.translator.jvm.option import \
@@ -92,6 +92,12 @@
         gen.begin_function(
             'main', (), [jStringArray], jVoid, static=True)
 
+        # First thing we do is setup the PyPy helper.  For now this is
+        # a static variable of the PyPy class, though that precludes
+        # running multiple translations.
+        gen.new_with_jtype(gen.db.interlink_class)
+        jvmgen.PYPYINTERLINK.store(gen)
+
         if self.print_result:
             gen.begin_try()
 
@@ -147,9 +153,28 @@
         gen.end_function()
         gen.end_class()
 
-class Function(OOFunction):
+class Function(object):
+
+    """ A generic interface for Function objects; these objects can
+    be added as methods of classes and rendered.  This class serves
+    only as documentation. """
+
+    # A "name" attribute must be defined
+    name = None                       
+    
+    def render(self, gen):
+        """ Uses the gen argument, a jvmgen.Generator, to create the
+        appropriate JVM assembly for this method. """
+        raise NotImplementedError
+    
+    def method(self):
+        """ Returns a jvmgen.Method object that would allow this
+        function to be invoked. """
+        raise NotImplementedError
+    
+class GraphFunction(OOFunction, Function):
     
-    """ Represents a function to be emitted. """
+    """ Represents a function that is generated from a graph. """
     
     def __init__(self, db, classty, name, jargtypes,
                  jrettype, graph, is_static):
@@ -418,12 +443,17 @@
         "java.lang.String", supercls is a Class object
         """
         JvmClassType.__init__(self, name)
-        self.super_class = supercls # can also be set later with set_super_class
-        self.fields = {}
-        self.rendered = False
-        self.abstract = False
-        self.methods = {}
-        self.abstract_methods = {}
+        self.super_class = supercls # JvmType; if None, must use set_super_class
+        self.rendered = False       # has rendering occurred?
+        self.abstract = False       # is this an abstract class?
+        self.fields = {}            # maps field name to jvmgen.Field object
+        self.interfaces = []        # list of JvmTypes
+        self.methods = {}           # maps method name to a Function object*
+        self.abstract_methods = {}  # maps method name to jvmgen.Method object
+
+        # * --- actually maps to an object that defines the
+        # attributes: name, method() and render().  Usually, this is a
+        # Function object, but in some subclasses it is not.
 
     def set_super_class(self, supercls):
         self.super_class = supercls
@@ -434,6 +464,10 @@
         assert not self.rendered and isinstance(fieldobj, jvmgen.Field)
         self.fields[fieldobj.field_name] = (fieldobj, fielddef)
 
+    def add_interface(self, inter):
+        assert not self.rendered and isinstance(inter, JvmType)
+        self.interfaces.append(inter)
+
     def lookup_field(self, fieldnm):
         """ Given a field name, returns a jvmgen.Field object """
         if fieldnm in self.fields:
@@ -466,6 +500,9 @@
         self.rendered = True
         gen.begin_class(self, self.super_class, abstract=self.abstract)
 
+        for inter in self.interfaces:
+            gen.implements(inter)
+
         for field, fielddef in self.fields.values():
             gen.add_field(field)
 
@@ -490,3 +527,32 @@
             gen.end_function()
         
         gen.end_class()
+
+class InterlinkFunction(Function):
+
+    """
+    Used for methods of the interlink helper class that we generate.
+    Generates a method which takes no arguments and which invokes
+    a given static helper function.
+    """
+
+    def __init__(self, interlink, name, helper):
+        """
+        interlink:  the JvmType of the Interlink implementation
+        name:       the name of the method
+        helper:     a jvmgen.Method object for the helper func we should invoke
+        """
+        self.interlink = interlink
+        self.name = name
+        self.helper = helper
+        self.method_obj = jvmgen.Method.v(interlink, self.name, [], jVoid)
+
+    def method(self):
+        return self.method_obj
+
+    def render(self, gen):
+        gen.begin_function(self.name, (), [self.interlink], jVoid)
+        gen.emit(self.helper)
+        gen.return_val(jVoid)
+        gen.end_function()
+

Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py	Mon Jan 22 14:23:57 2007
@@ -9,20 +9,34 @@
      PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing, Call,\
      SetField, GetField, DownCast, RuntimeNew, OOString, CastTo
 from pypy.translator.jvm.metavm import \
-     IndirectCall, JvmCallMethod
+     IndirectCall, JvmCallMethod, TranslateException
+
 import pypy.translator.jvm.generator as jvmgen
+import pypy.translator.jvm.typesystem as jvmtype
+
+def _proc(val):
+    """ Function which is used to post-process each entry in the
+    opcodes table; it adds a PushAllArgs and StoreResult by default,
+    unless the entry is a list already. """
+    if not isinstance(val, list):
+        val = InstructionList((PushAllArgs, val, StoreResult))
+    else:
+        val = InstructionList(val)
+    return val
 
 def _check_zer(op):
-    # TODO
-    return op
+    return [TranslateException(
+        jvmtype.jArithmeticException,
+        'throwZeroDivisionError',
+        _proc(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 = {
+# It is post-processed by _proc.
+_opcodes = {
     # __________ object oriented operations __________
     'new':                      [New, StoreResult],
     'runtimenew':               [RuntimeNew, StoreResult],
@@ -221,10 +235,7 @@
     
 }
 
-for opc in opcodes:
-    val = opcodes[opc]
-    if not isinstance(val, list):
-        val = InstructionList((PushAllArgs, val, StoreResult))
-    else:
-        val = InstructionList(val)
-    opcodes[opc] = val
+opcodes = {}
+for opc, val in _opcodes.items():
+    opcodes[opc] = _proc(val)
+del _opcodes

Added: pypy/dist/pypy/translator/jvm/prebuiltnodes.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/prebuiltnodes.py	Mon Jan 22 14:23:57 2007
@@ -0,0 +1,30 @@
+from pypy.translator.translator import graphof
+
+# ___________________________________________________________________________
+
+def throwZeroDivisionError():
+    raise ZeroDivisionError
+
+def throwIndexError():
+    raise IndexError
+
+# ___________________________________________________________________________
+
+def create_interlink_node(db):
+    """ Translates the create_interlink_impl() function and returns
+    a jvmgen.Method object that allows it to be called. """
+    translator = db.genoo.translator
+
+    HELPERS = [val for nm, val in globals().items() if nm.startswith('throw')]
+    
+    for func in HELPERS:
+        translator.annotator.build_types(func, [])        
+    translator.rtyper.specialize_more_blocks()
+
+    helpers = {}
+    for func in HELPERS:
+        graph = graphof(translator, func)
+        helpers[func.func_name] = db.pending_function(graph)
+
+    db.create_interlink_node(helpers)
+

Added: pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java	Mon Jan 22 14:23:57 2007
@@ -0,0 +1,15 @@
+package pypy;
+
+/**
+ * This interface allows code written in Java to invoke methods
+ * generated by the PyPy translator.  An instance of this interface is
+ * created in the generated main method and the static variable of the
+ * PyPy class is set.  The methods do not involve state, so there are
+ * no threading issues with using a global variable; however, we could
+ * not use multiple PyPy interpreters simultaneously, as each would
+ * require their own version of Interlink.
+ */
+public interface Interlink {
+    public void throwZeroDivisionError();
+    public void throwIndexError();
+}
\ No newline at end of file

Modified: pypy/dist/pypy/translator/jvm/src/pypy/PyPy.java
==============================================================================
--- pypy/dist/pypy/translator/jvm/src/pypy/PyPy.java	(original)
+++ pypy/dist/pypy/translator/jvm/src/pypy/PyPy.java	Mon Jan 22 14:23:57 2007
@@ -13,6 +13,9 @@
  * Python mere minutes before.
  */
 public class PyPy {
+    
+    public static Interlink interlink;
+
     /** 
      * Compares two unsigned integers (value1 and value2) and returns
      * a value greater than, equal to, or less than zero if value 1 is
@@ -180,6 +183,15 @@
         return s.charAt(0);
     }
 
+    public static char ll_stritem_nonneg(String s, int idx) {
+        try {
+            return s.charAt(idx);
+        } catch (StringIndexOutOfBoundsException exc) {
+            throwIndexError();
+        }
+        throw new RuntimeException("Should not get here");
+    }
+
     // Used in testing:
 
     public static void dump(String text) {
@@ -554,7 +566,22 @@
             _ll_resize_ge(self, length);
         else if (length < self.size())
             _ll_resize_le(self, length); 
-   }
+    }
+
+    // ----------------------------------------------------------------------
+    // Convenient Helpers for throwing exceptions
+    //
+    // Also, an abstraction barrier: at a later date we may want to
+    // switch to using thread-local data rather than a global variable,
+    // and if so we can easily do it in these functions here.
+
+    public static void throwZeroDivisionError() {
+        interlink.throwZeroDivisionError();
+    }
+
+    public static void throwIndexError() {
+        interlink.throwIndexError();
+    }
 
     // ----------------------------------------------------------------------
     // Self Test

Modified: pypy/dist/pypy/translator/jvm/test/test_string.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/test/test_string.py	(original)
+++ pypy/dist/pypy/translator/jvm/test/test_string.py	Mon Jan 22 14:23:57 2007
@@ -18,6 +18,6 @@
 
     def test_float(self):
         py.test.skip("JVM does not yet support ooparse_float")
-        
+
     def test_getitem_exc(self):
-        py.test.skip("JVM has trouble with exceptions")
+        py.test.skip("TODO: Appears to be a bug in test_rstr.py??")

Modified: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/typesystem.py	(original)
+++ pypy/dist/pypy/translator/jvm/typesystem.py	Mon Jan 22 14:23:57 2007
@@ -168,6 +168,9 @@
 jPyPyConst = JvmClassType('pypy.Constant')
 jPyPyMain = JvmClassType('pypy.Main')
 jPyPyDictItemsIterator = JvmClassType('pypy.DictItemsIterator')
+jPyPyInterlink = JvmClassType('pypy.Interlink')
+
+jArithmeticException = JvmClassType('java.lang.ArithmeticException')
 
 class JvmScalarType(JvmType):
     """



More information about the Pypy-commit mailing list