[pypy-svn] r51813 - in pypy/dist/pypy/translator: cli/test jvm jvm/src/pypy jvm/test oosupport oosupport/test_template

niko at codespeak.net niko at codespeak.net
Fri Feb 22 23:21:00 CET 2008


Author: niko
Date: Fri Feb 22 23:20:59 2008
New Revision: 51813

Added:
   pypy/dist/pypy/translator/jvm/test/test_extreme.py
   pypy/dist/pypy/translator/oosupport/test_template/extreme.py
Modified:
   pypy/dist/pypy/translator/cli/test/test_snippet.py
   pypy/dist/pypy/translator/jvm/database.py
   pypy/dist/pypy/translator/jvm/generator.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/prebuiltnodes.py
   pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java
   pypy/dist/pypy/translator/jvm/test/test_snippet.py
   pypy/dist/pypy/translator/jvm/typesystem.py
   pypy/dist/pypy/translator/oosupport/function.py
   pypy/dist/pypy/translator/oosupport/test_template/snippets.py
Log:
fix two bugs in JVM backend: 
	* llshl takes a long, not an int, for # of bits to shift
	* rejigger exc. handling so we translate from CLI exceptions
	in the catch block rather than at the site they are thrown.
	This allows us to handle StackOverflow and (in theory) OutOfMemory
	exceptions.



Modified: pypy/dist/pypy/translator/cli/test/test_snippet.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/test_snippet.py	(original)
+++ pypy/dist/pypy/translator/cli/test/test_snippet.py	Fri Feb 22 23:20:59 2008
@@ -1,7 +1,11 @@
+import py
 from pypy.translator.cli.test.runtest import CliTest
 from pypy.translator.oosupport.test_template.snippets import BaseTestSnippets
 
 class TestSnippets(BaseTestSnippets, CliTest):
+    def test_llshl(self):
+        py.test.skip('llshl currently broken on CLI')
+
     def test_link_SSA(self):
         def fn():
             lst = [42, 43, 44]

Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py	(original)
+++ pypy/dist/pypy/translator/jvm/database.py	Fri Feb 22 23:20:59 2008
@@ -53,15 +53,23 @@
 
         # Create information about the Main class we will build:
         #
-        #    note that it will have a static field called 'pypy' that
-        #    points to a PyPy instance.  This PyPy instance has been
-        #    paired with the appropriate Interlink implementation
-        #    which allows it to create generated structures.
+        #    It will have two static fields, 'ilink' and 'pypy'.  The
+        #    first points to an instance of the interface pypy.Interlink
+        #    which we will be generated.  The second points to an instance
+        #    of pypy.PyPy which was created with this Interlink instance.
+        #
+        #    The Interlink class provides the bridge between static helper
+        #    code and dynamically generated classes.  Since there is one
+        #    Main per set of translated code, this also allows multiple
+        #    PyPy interpreters to overlap with one another.
         #
         #    These are public attributes that are referenced from
-        #    elsewhere in the code.
+        #    elsewhere in the code using
+        #    jvmgen.Generator.push_interlink() and .push_pypy().
         self.jPyPyMain = JvmClassType(self._pkg('Main'))
         self.pypy_field = jvmgen.Field.s(self.jPyPyMain, 'pypy', jPyPy)
+        self.interlink_field = jvmgen.Field.s(self.jPyPyMain, 'ilink',
+                                              jvmtype.jPyPyInterlink)
 
     # _________________________________________________________________
     # Java String vs Byte Array
@@ -509,7 +517,8 @@
         # Handle built-in types:
         if OOT in self.ootype_to_scalar:
             return self.ootype_to_scalar[OOT]
-        if isinstance(OOT, lltype.Ptr) and isinstance(t.TO, lltype.OpaqueType):
+        if (isinstance(OOT, lltype.Ptr) and
+            isinstance(OOT.TO, lltype.OpaqueType)):
             return jObject
         if OOT in self.ootype_to_builtin:
             return JvmBuiltInType(self, self.ootype_to_builtin[OOT], OOT)

Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py	(original)
+++ pypy/dist/pypy/translator/jvm/generator.py	Fri Feb 22 23:20:59 2008
@@ -733,15 +733,11 @@
         if jvartype is jVoid:
             return
         opc = LOAD.for_type(jvartype)
-        self.add_comment("     load_jvm_jar: jvartype=%s varidx=%s" % (
-            repr(jvartype), repr(varidx)))
         self._instr(opc, varidx)
 
     def store_jvm_var(self, vartype, varidx):
         """ Loads from jvm slot #varidx, which is expected to hold a value of
         type vartype """
-        self.add_comment("     store_jvm_jar: vartype=%s varidx=%s" % (
-            repr(vartype), repr(varidx)))
         self._instr(STORE.for_type(vartype), varidx)
 
     def load_from_array(self, elemtype):
@@ -831,37 +827,30 @@
 
     # __________________________________________________________________
     # Exception Handling
+    #
+    # You can demarcate regions of code as "try/catch" regions using
+    # the various functions included here.  Either invoke
+    # try_catch_region(), in which case you must supply all the
+    # relevant labels, or use the begin_try()/end_try()/begin_catch()
+    # methods.  In the latter case, you define the 3 needed labels as
+    # you go.  Both begin_try() and end_try() must have been invoked
+    # before begin_catch() is invoked (i.e., the try region must
+    # appear before the corresponding catch regions).  Note that
+    # end_try() can be called again to reset the end of the try
+    # region.
 
     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 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, jexcclsty):
-        """
-        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.
-        'jexcclsty' --- a JvmType for the class of exception to be caught
-        """
         catchlbl = self.unique_label("catch", mark=True)
         self.try_catch_region(
             jexcclsty, self.begintrylbl, self.endtrylbl, catchlbl)
-
+ 
     def end_catch(self):
-        """
-        Ends a catch region.
-        (Included for CLI compatibility)
-        """
         return
         
     def try_catch_region(self, jexcclsty, trystartlbl, tryendlbl, catchlbl):
@@ -985,6 +974,11 @@
         onto the stack """
         self.db.pypy_field.load(self)
 
+    def push_interlink(self):
+        """ Pushes the Interlink object which contains the methods
+        from prebuildnodes.py onto the stack """
+        self.db.interlink_field.load(self)
+
     def get_field(self, CONCRETETYPE, fieldname):
         clsobj = self.db.pending_class(CONCRETETYPE)
         fieldobj = clsobj.lookup_field(fieldname)

Modified: pypy/dist/pypy/translator/jvm/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/metavm.py	(original)
+++ pypy/dist/pypy/translator/jvm/metavm.py	Fri Feb 22 23:20:59 2008
@@ -62,47 +62,6 @@
                             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.  It would be better to
-        just find the class to throw normally, but I don't know how.
-        """
-        self.java_exc = jexc
-        self.pypy_method = jvmgen.Method.v(
-            jvmtype.jPyPyInterlink, 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)            # throw away the exception object
-        gen.push_pypy()                 # load the PyPy object
-        gen.emit(jvmgen.PYPYINTERLINK)  # load the interlink field from it
-        gen.emit(self.pypy_method)      # invoke the 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)
-        
 class _NewCustomDict(MicroInstruction):
     def _load_func(self, gen, fn, obj, method_name):
         db = gen.db
@@ -129,23 +88,6 @@
         generator.emit(jvmgen.CUSTOMDICTMAKE)
 NewCustomDict = _NewCustomDict()
 
-#XXX These classes have been adapted to the new
-#XXX WeakRef methods, but don't appear to be needed.
-#class _CastPtrToWeakAddress(MicroInstruction):
-#    def render(self, generator, op):
-#        arg = op.args[0]
-#        generator.load(arg)
-#        generator.create_weakref(arg.concretetype)
-#        generator.store(op.result)
-#CastPtrToWeakAddress = _CastPtrToWeakAddress()
-        
-#class _CastWeakAddressToPtr(MicroInstruction):
-#    def render(self, generator, op):
-#        RESULTTYPE = op.result.concretetype
-#        generator.deref_weakref(RESULTTYPE)
-#CastWeakAddressToPtr = _CastWeakAddressToPtr()
-
-
 CASTS = {
 #   FROM                      TO
     (ootype.Signed,           ootype.UnsignedLongLong): jvmgen.I2L,

Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py	(original)
+++ pypy/dist/pypy/translator/jvm/node.py	Fri Feb 22 23:20:59 2008
@@ -35,6 +35,7 @@
      push_constant
 
 import pypy.translator.jvm.generator as jvmgen
+import pypy.translator.jvm.typesystem as jvmtype
 from pypy.translator.jvm.log import log
 
 class Node(object):
@@ -93,11 +94,12 @@
     def render(self, gen):
         gen.begin_class(gen.db.jPyPyMain, jObject)
         gen.add_field(gen.db.pypy_field)
+        gen.add_field(gen.db.interlink_field)
 
         # Initialization:
         # 
-        #    1. Create a PyPy helper class, passing in an appropriate
-        #    interlink instance.
+        #    1. Create an Interlink instance and a PyPy helper class, and
+        #    store them in the appropriate static fields.
         #
         #    2. Run the initialization method for the constant class.
         #
@@ -106,6 +108,8 @@
         gen.emit(jvmgen.NEW, jPyPy)
         gen.emit(jvmgen.DUP)
         gen.new_with_jtype(gen.db.jInterlinkImplementation)
+        gen.emit(jvmgen.DUP)
+        gen.db.interlink_field.store(gen)
         gen.emit(jvmgen.Method.c(jPyPy, [jPyPyInterlink]))
         gen.db.pypy_field.store(gen)
         gen.db.constant_generator.runtime_init(gen)
@@ -314,12 +318,54 @@
         if cond:
             self.ilasm.end_try()
 
+    def introduce_exception_conversions(self, llexitcases):
+
+        # These are exceptions thrown internally by the JVM.
+        # If the user is catching an RPython exception that corresponds
+        # to one of these cases, we introduce a translation block which
+        # catches the corresponding JVM exception and re-throwns the
+        # RPython one.  A better solution would be to find a way to
+        # make the RPython class the same as the JVM class, but that is
+        # currently hindered by the presence of a few fields (meta) on
+        # the Object class.
+        translation_table = [
+            (ZeroDivisionError, jvmtype.jArithmeticException),
+            (RuntimeError, jvmtype.jStackOverflowError),
+            (MemoryError, jvmtype.jOutOfMemoryError),
+            ]
+
+        for pyexccls, jexcty in translation_table:
+            for llexitcase in llexitcases:
+                assert issubclass(llexitcase, BaseException)
+                if issubclass(llexitcase, pyexccls):
+                    # Generate some converter code like:
+                    #   try { ... }
+                    #   catch (OutOfStackError e) {
+                    #     jPyPyMain.ilink.throwRuntimeError();
+                    #     throw null;
+                    #   }
+                    # The "throw null" will never execute, it's just
+                    # there to make the verifier happy, since it doesn't
+                    # realize that Interlink's throwXyzError() methods
+                    # never return.  At the end we invoke end_try() again
+                    # so as to extend the encompassing try/catch region
+                    # to include this code, thus allowing the RPython
+                    # exception to be caught by the normal handlers.
+                    self.ilasm.begin_catch(jexcty)
+                    self.ilasm.push_interlink()
+                    interlink_method = jvmgen.Method.v(
+                        jPyPyInterlink, "throw"+pyexccls.__name__, [], jVoid)
+                    self.ilasm.emit(interlink_method)
+                    self.ilasm.emit(jvmgen.ACONST_NULL)
+                    self.ilasm.emit(jvmgen.ATHROW)
+                    self.ilasm.end_try()        
+
     def begin_catch(self, llexitcase):
         ll_meta_exc = llexitcase
         ll_exc = ll_meta_exc._inst.class_._INSTANCE
         jtype = self.cts.lltype_to_cts(ll_exc)
         assert jtype.throwable # SHOULD only try to catch subtypes of Exception
-        self.ilasm.begin_catch(jtype)        
+        self.ilasm.begin_catch(jtype)
 
     def end_catch(self, exit_lbl):
         self.ilasm.goto(exit_lbl)

Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py	Fri Feb 22 23:20:59 2008
@@ -10,7 +10,7 @@
      SetField, GetField, DownCast, RuntimeNew, OOString, OOUnicode, \
      CastTo, PushPrimitive
 from pypy.translator.jvm.metavm import \
-     IndirectCall, JvmCallMethod, TranslateException, NewCustomDict, \
+     IndirectCall, JvmCallMethod, NewCustomDict, \
      CastPrimitive, PushPyPy
 from pypy.rpython.ootypesystem import ootype
 
@@ -47,10 +47,11 @@
     return res
 
 def _check_zer(op):
-    return [TranslateException(
-        jvmtype.jArithmeticException,
-        'throwZeroDivisionError',
-        _proc(op))]
+    # Note: we convert from Java's ArithmeticException to RPython's
+    # ZeroDivisionError in the *catch* code, not here where the
+    # exception is generated.  See introduce_exception_conversions()
+    # in node.py for details.
+    return op
 
 def _check_ovf(op):
     return op
@@ -213,7 +214,7 @@
     'llong_ge':                 'long_greater_equals',
     'llong_and':                jvmgen.LAND,
     'llong_or':                 jvmgen.LOR,
-    'llong_lshift':             jvmgen.LSHL,
+    'llong_lshift':             [PushAllArgs, jvmgen.L2I, jvmgen.LSHL, StoreResult], # XXX - do we care about shifts of >(1<<32) bits??
     'llong_rshift':             [PushAllArgs, jvmgen.L2I, jvmgen.LSHR, StoreResult],
     'llong_xor':                jvmgen.LXOR,
     'llong_floordiv_ovf':       jvmgen.LDIV, # these can't overflow!

Modified: pypy/dist/pypy/translator/jvm/prebuiltnodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/prebuiltnodes.py	(original)
+++ pypy/dist/pypy/translator/jvm/prebuiltnodes.py	Fri Feb 22 23:20:59 2008
@@ -22,6 +22,10 @@
     raise OverflowError
 
 @with_types([])
+def throwRuntimeError():
+    raise RuntimeError
+
+ at with_types([])
 def throwValueError():
     raise ValueError
 

Modified: pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java
==============================================================================
--- pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java	(original)
+++ pypy/dist/pypy/translator/jvm/src/pypy/Interlink.java	Fri Feb 22 23:20:59 2008
@@ -13,6 +13,7 @@
     public void throwZeroDivisionError();
     public void throwIndexError();
     public void throwOverflowError();
+    public void throwRuntimeError();
     public void throwValueError();
     public void throwUnicodeDecodeError();
     public void throwOSError(int errCode);

Added: pypy/dist/pypy/translator/jvm/test/test_extreme.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/test/test_extreme.py	Fri Feb 22 23:20:59 2008
@@ -0,0 +1,5 @@
+from pypy.translator.jvm.test.runtest import JvmTest
+from pypy.translator.oosupport.test_template.extreme import BaseTestExtreme
+
+class TestExtreme(BaseTestExtreme, JvmTest):
+    pass

Modified: pypy/dist/pypy/translator/jvm/test/test_snippet.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/test/test_snippet.py	(original)
+++ pypy/dist/pypy/translator/jvm/test/test_snippet.py	Fri Feb 22 23:20:59 2008
@@ -1,3 +1,4 @@
+import sys
 from pypy.translator.jvm.test.runtest import JvmTest
 from pypy.translator.oosupport.test_template.snippets import BaseTestSnippets
 

Modified: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/typesystem.py	(original)
+++ pypy/dist/pypy/translator/jvm/typesystem.py	Fri Feb 22 23:20:59 2008
@@ -201,6 +201,8 @@
 jPyPyRecordFloatFloat = JvmClassType('pypy.RecordFloatFloat')
 jPyPyAbstractMethodException = JvmClassType('pypy.AbstractMethodException')
 
+jStackOverflowError = JvmClassType('java.lang.StackOverflowError', throwable=True)
+jOutOfMemoryError = JvmClassType('java.lang.OutOfMemoryError', throwable=True)
 jArithmeticException = JvmClassType('java.lang.ArithmeticException', throwable=True)
 
 class JvmScalarType(JvmType):

Modified: pypy/dist/pypy/translator/oosupport/function.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/function.py	(original)
+++ pypy/dist/pypy/translator/oosupport/function.py	Fri Feb 22 23:20:59 2008
@@ -176,6 +176,13 @@
         else:
             assert False, "No non-exceptional case from exc_handling block"
 
+        # give the backend a chance to see all the exceptions that might
+        # be caught here.  For ex., JVM uses this to convert between
+        # built-in JVM exceptions to their RPython equivalents
+        if anyHandler:
+            self.introduce_exception_conversions(
+                [link.exitcase for link in block.exits if link.exitcase])
+
         # catch the exception and dispatch to the appropriate block
         for link in block.exits:
             if link.exitcase is None:
@@ -192,6 +199,11 @@
 
         self.after_except_block()
 
+    def introduce_exception_conversions(self, llexitcases):
+        """ Called before any catch blocks are emitted with the full set of
+        exceptions that might be caught """        
+        return
+
     def after_except_block(self):
         pass
 

Added: pypy/dist/pypy/translator/oosupport/test_template/extreme.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/oosupport/test_template/extreme.py	Fri Feb 22 23:20:59 2008
@@ -0,0 +1,24 @@
+import sys, py
+
+class BaseTestExtreme:
+
+    def test_memoryerror_due_to_oom(self):
+        py.test.skip("can't get MemoryError except block to show up")
+        def fn():
+            try:
+                lst = []
+                for i in range(sys.maxint): lst.append(i)
+            except MemoryError:
+                return "OK"
+        assert self.interpret(fn, []) == "OK"
+        
+    def test_runtimeerror_due_to_stack_overflow(self):
+        def loop():
+            loop()
+        def fn():
+            try:
+                loop()
+            except RuntimeError, e:
+                return "OK"
+        assert self.interpret(fn, []) == "OK"
+

Modified: pypy/dist/pypy/translator/oosupport/test_template/snippets.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/test_template/snippets.py	(original)
+++ pypy/dist/pypy/translator/oosupport/test_template/snippets.py	Fri Feb 22 23:20:59 2008
@@ -1,4 +1,5 @@
 from pypy.translator.test import snippet as s
+from pypy.rlib.rarithmetic import r_longlong
 
 # -----------------------------------------------------------------
 
@@ -42,6 +43,12 @@
             return x+y
         assert self.interpret(fn, [4,7]) == 11
 
+    def test_llshl(self):
+        def fn(a, b):
+            return a << b
+        assert self.interpret(fn, [r_longlong(1), 52]) == (1<<52)
+        assert self.interpret(fn, [r_longlong(1), r_longlong(52)]) == (1<<52)
+
     def test_manipulate(self):
         def fn(x,y):
             obj = SimplestObject()



More information about the Pypy-commit mailing list