[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