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

niko at codespeak.net niko at codespeak.net
Wed Apr 4 09:52:52 CEST 2007


Author: niko
Date: Wed Apr  4 09:52:50 2007
New Revision: 41882

Added:
   pypy/dist/pypy/translator/jvm/src/pypy/Callback.java
   pypy/dist/pypy/translator/jvm/src/pypy/CustomDict.java
   pypy/dist/pypy/translator/jvm/src/pypy/Equals.java
   pypy/dist/pypy/translator/jvm/src/pypy/Filter.java
   pypy/dist/pypy/translator/jvm/src/pypy/FilterIterator.java
   pypy/dist/pypy/translator/jvm/src/pypy/FilterSet.java
   pypy/dist/pypy/translator/jvm/src/pypy/HashCode.java
   pypy/dist/pypy/translator/jvm/test/test_objectmodel.py
Modified:
   pypy/dist/pypy/translator/jvm/   (props changed)
   pypy/dist/pypy/translator/jvm/builtin.py
   pypy/dist/pypy/translator/jvm/constant.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_dict.py
   pypy/dist/pypy/translator/jvm/test/test_snippet.py
   pypy/dist/pypy/translator/jvm/typesystem.py
Log:
Implement custom dicts (rdicts) in the JVM backend.  All tests in objectmodel
now pass.

The technique is to find all functions which could be used
as hashCode() or equals() functions, and to make any static functions
that are defined implement either pypy.Equals or pypy.HashCode, then
allowing them to be passed to pypy.CustomDict.




Modified: pypy/dist/pypy/translator/jvm/builtin.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/builtin.py	(original)
+++ pypy/dist/pypy/translator/jvm/builtin.py	Wed Apr  4 09:52:50 2007
@@ -3,7 +3,8 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.translator.jvm.typesystem import \
      jInt, jVoid, jStringBuilder, jString, jPyPy, jChar, jArrayList, jObject, \
-     jBool, jHashMap, jPyPyDictItemsIterator, Generifier, jCharSequence
+     jBool, jHashMap, jPyPyDictItemsIterator, Generifier, jCharSequence, \
+     jPyPyCustomDict
 
 # ______________________________________________________________________
 # Mapping of built-in OOTypes to JVM types
@@ -128,6 +129,21 @@
     (ootype.Dict, "ll_clear"):
     jvmgen.Method.v(jHashMap, "clear", (), jVoid),
 
+    (ootype.CustomDict, "ll_set"):
+    jvmgen.Method.v(jPyPyCustomDict, "put", (jObject, jObject), jObject),
+    
+    (ootype.CustomDict, "ll_get"):
+    jvmgen.Method.v(jPyPyCustomDict, "get", (jObject,), jObject),
+
+    (ootype.CustomDict, "ll_contains"):
+    jvmgen.Method.v(jPyPyCustomDict, "containsKey", (jObject,), jBool),
+
+    (ootype.CustomDict, "ll_length"):
+    jvmgen.Method.v(jPyPyCustomDict, "size", (), jInt),
+    
+    (ootype.CustomDict, "ll_clear"):
+    jvmgen.Method.v(jPyPyCustomDict, "clear", (), jVoid),
+
     (ootype.List, "ll_length"):
     jvmgen.Method.v(jArrayList, "size", (), jInt),
 

Modified: pypy/dist/pypy/translator/jvm/constant.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/constant.py	(original)
+++ pypy/dist/pypy/translator/jvm/constant.py	Wed Apr  4 09:52:50 2007
@@ -1,9 +1,9 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.translator.jvm.generator import \
-     Field, Method
+     Field, Method, CUSTOMDICTMAKE
 from pypy.translator.oosupport.constant import \
      BaseConstantGenerator, RecordConst, InstanceConst, ClassConst, \
-     StaticMethodConst
+     StaticMethodConst, CustomDictConst
 from pypy.translator.jvm.typesystem import \
      jPyPyConst, jObject, jVoid
 
@@ -63,7 +63,8 @@
             self.delegate_impl = None
             return
         StaticMethodConst.record_dependencies(self)
-        self.delegate_impl = self.db.record_delegate_impl(self.value.graph)
+        self.delegate_impl = self.db.record_delegate_standalone_func_impl(
+            self.value.graph)
 
     def create_pointer(self, gen):
         if self.delegate_impl:
@@ -74,3 +75,20 @@
     def initialize_data(self, ilasm):
         return
     
+class JVMCustomDictConst(CustomDictConst):
+
+    def record_dependencies(self):
+        # Near as I can tell, self.value is an ootype._custom_dict,
+        # key_eq is a Python function and graph is, well, a method
+        # graph that seems to be added to the function pointer
+        # somewhere.  Adapted from cli/constant.py
+        self.eq_jcls = self.db.record_delegate_standalone_func_impl(
+            self.value._dict.key_eq.graph)
+        self.hash_jcls = self.db.record_delegate_standalone_func_impl(
+            self.value._dict.key_hash.graph)
+        
+    def create_pointer(self, gen):
+        gen.new_with_jtype(self.eq_jcls)
+        gen.new_with_jtype(self.hash_jcls)
+        gen.emit(CUSTOMDICTMAKE)
+        

Modified: pypy/dist/pypy/translator/jvm/database.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/database.py	(original)
+++ pypy/dist/pypy/translator/jvm/database.py	Wed Apr  4 09:52:50 2007
@@ -32,7 +32,10 @@
         self._functions = {}      # graph -> jvmgen.Method
 
         # (jargtypes, jrettype) -> node.StaticMethodInterface
-        self._delegates = {} 
+        self._delegates = {}
+
+        # (INSTANCE, method_name) -> node.StaticMethodImplementation
+        self._bound_methods = {}
 
         self._function_names = {} # graph --> function_name
 
@@ -235,12 +238,27 @@
         return res
 
     def record_delegate(self, TYPE):
-        """ TYPE is a StaticMethod """
+        """
+        Creates and returns a StaticMethodInterface type; this type
+        represents an abstract base class for functions with a given
+        signature, represented by TYPE, a ootype.StaticMethod
+        instance.
+        """
 
         # Translate argument/return types into java types, check if
         # we already have such a delegate:
-        jargs = tuple([self.lltype_to_cts(ARG) for ARG in TYPE.ARGS])
+        jargs = tuple([self.lltype_to_cts(ARG) for ARG in TYPE.ARGS
+                       if ARG is not ootype.Void])
         jret = self.lltype_to_cts(TYPE.RESULT)
+        return self.record_delegate_sig(jargs, jret)
+
+    def record_delegate_sig(self, jargs, jret):
+        """
+        Like record_delegate, but the signature is in terms of java
+        types.  jargs is a list of JvmTypes, one for each argument,
+        and jret is a JvmType.  Note that jargs does NOT include an
+        entry for the this pointer of the resulting object.  
+        """
         key = (jargs, jret)
         if key in self._delegates:
             return self._delegates[key]
@@ -250,19 +268,42 @@
         name = self._pkg(self._uniq('Delegate'))
 
         # Create a new one if we do not:
-        interface = node.StaticMethodInterface(name, TYPE, jargs, jret)
+        interface = node.StaticMethodInterface(name, jargs, jret)
         self._delegates[key] = interface
         self.pending_node(interface)
         return interface
     
-    def record_delegate_impl(self, graph):
-        """ TYPE is a StaticMethod """
+    def record_delegate_standalone_func_impl(self, graph):
+        """
+        Creates a class with an invoke() method that invokes the given
+        graph.  This object can be used as a function pointer.  It
+        will extend the appropriate delegate for the graph's
+        signature.
+        """
         jargtypes, jrettype = self.types_for_graph(graph)
-        key = (jargtypes, jrettype)
-        assert key in self._delegates
+        super_class = self.record_delegate_sig(jargtypes, jrettype)
         pfunc = self.pending_function(graph)
         implnm = self._pkg(self._uniq(graph.name+'_delegate'))
-        n = node.StaticMethodImplementation(implnm, self._delegates[key], pfunc)
+        n = node.StaticMethodImplementation(implnm, super_class, None, pfunc)
+        self.pending_node(n)
+        return n
+
+    def record_delegate_bound_method_impl(self, INSTANCE, method_name):
+        """
+        Creates an object with an invoke() method which invokes
+        a method named method_name on an instance of INSTANCE.
+        """
+        key = (INSTANCE, method_name)
+        if key in self._bound_methods:
+            return self._bound_methods[key]
+        METH_TYPE = INSTANCE._lookup(method_name)[1]._TYPE
+        super_class = self.record_delegate(METH_TYPE)
+        self_class = self.lltype_to_cts(INSTANCE)
+        mthd_obj = self_class.lookup_method(method_name)
+        implnm = self._pkg(self._uniq(
+            self_class.simple_name()+"_"+method_name+"_delegate"))
+        n = self._bound_methods[key] = node.StaticMethodImplementation(
+            implnm, super_class, self_class, mthd_obj)
         self.pending_node(n)
         return n
 
@@ -338,6 +379,7 @@
         ootype.List:             jvmtype.jArrayList,
         ootype.Dict:             jvmtype.jHashMap,
         ootype.DictItemsIterator:jvmtype.jPyPyDictItemsIterator,
+        ootype.CustomDict:       jvmtype.jPyPyCustomDict,
         }
 
     def lltype_to_cts(self, OOT):

Modified: pypy/dist/pypy/translator/jvm/generator.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/generator.py	(original)
+++ pypy/dist/pypy/translator/jvm/generator.py	Wed Apr  4 09:52:50 2007
@@ -10,7 +10,8 @@
      jPyPy, jVoid, jMath, desc_for_method, jPrintStream, jClass, jChar, \
      jObject, jByteArray, jPyPyExcWrap, jIntegerClass, jLongClass, \
      jDoubleClass, jCharClass, jStringBuilder, JvmScalarType, jArrayList, \
-     jObjectArray, jPyPyInterlink
+     jObjectArray, jPyPyInterlink, jPyPyCustomDict, jPyPyEquals, \
+     jPyPyHashCode, jMap
 
 # ___________________________________________________________________________
 # Miscellaneous helper functions
@@ -176,6 +177,7 @@
 INVOKESTATIC = Opcode('invokestatic')
 INVOKEVIRTUAL = Opcode('invokevirtual')
 INVOKESPECIAL = Opcode('invokespecial')
+INVOKEINTERFACE = Opcode('invokeinterface')
 
 # Other opcodes
 LDC =       Opcode('ldc')       # single-word types
@@ -293,11 +295,15 @@
         not the this ptr
         'rettype' - JvmType for return type
         """
-        assert isinstance(classty, JvmType)
         classnm = classty.name
-        return Method(classnm, methnm, argtypes, rettype, opcode=INVOKEVIRTUAL)
+        if isinstance(classty, jvmtype.JvmInterfaceType):
+            opc = INVOKEINTERFACE
+        else:
+            assert isinstance(classty, jvmtype.JvmClassType)
+            opc = INVOKEVIRTUAL
+        return Method(classnm, methnm, argtypes, rettype, opcode=opc)
     v = staticmethod(v)
-    
+
     # Create a static method:
     def s(classty, methnm, argtypes, rettype):
         """
@@ -329,9 +335,14 @@
     def is_static(self):
         return self.opcode == INVOKESTATIC
     def jasmin_syntax(self):
-        return "%s/%s%s" % (self.class_name.replace('.','/'),
-                            self.method_name,
-                            self.descriptor)
+        res = "%s/%s%s" % (self.class_name.replace('.','/'),
+                           self.method_name,
+                           self.descriptor)
+        # A weird, inexplicable quirk of Jasmin syntax is that it requires
+        # the number of arguments after an invokeinterface call:
+        if self.opcode == INVOKEINTERFACE:
+            res += " %d" % (len(self.argument_types),)
+        return res
 
 OBJHASHCODE =           Method.v(jObject, 'hashCode', (), jInt)
 OBJTOSTRING =           Method.v(jObject, 'toString', (), jString)
@@ -375,6 +386,8 @@
 OBJECTGETCLASS =        Method.v(jObject, 'getClass', (), jClass)
 CLASSGETNAME =          Method.v(jClass, 'getName', (), jString)
 EXCWRAPWRAP =           Method.s(jPyPyExcWrap, 'wrap', (jObject,), jPyPyExcWrap)
+CUSTOMDICTMAKE =        Method.s(jPyPyCustomDict, 'make',
+                                 (jPyPyEquals, jPyPyHashCode), jPyPyCustomDict)
 
 # ___________________________________________________________________________
 # Fields
@@ -594,9 +607,9 @@
         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 return_val(self, jtype):
+        """ Returns a value from top of stack of the JvmType 'jtype' """
+        self._instr(RETURN.for_type(jtype))
 
     def load_class_name(self):
         """ Loads the name of the *Java* class of the object on the top of
@@ -681,6 +694,9 @@
 
     def prepare_generic_argument(self, ITEMTYPE):
         jty = self.db.lltype_to_cts(ITEMTYPE)
+        self.prepare_generic_argument_with_jtype(jty)
+        
+    def prepare_generic_argument_with_jtype(self, jty):
         if jty is jvmtype.jVoid:
             self.emit(ACONST_NULL)
         elif isinstance(jty, JvmScalarType):
@@ -688,6 +704,9 @@
 
     def prepare_generic_result(self, ITEMTYPE):
         jresty = self.db.lltype_to_cts(ITEMTYPE)
+        self.prepare_generic_result_with_jtype(jresty)
+        
+    def prepare_generic_result_with_jtype(self, jresty):
         if jresty is jvmtype.jVoid:
             self.emit(POP)
         elif isinstance(jresty, JvmScalarType):
@@ -696,7 +715,7 @@
             self.unbox_value(jresty)
         else:
             # Perform any casting required:
-            self.downcast(ITEMTYPE)
+            self.downcast_jtype(jresty)
 
     def box_value(self, jscalartype):
         """ Assuming that an value of type jscalartype is on the stack,

Modified: pypy/dist/pypy/translator/jvm/genjvm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/genjvm.py	(original)
+++ pypy/dist/pypy/translator/jvm/genjvm.py	Wed Apr  4 09:52:50 2007
@@ -18,7 +18,7 @@
 from pypy.translator.jvm.opcodes import opcodes
 from pypy.rpython.ootypesystem import ootype
 from pypy.translator.jvm.constant import \
-     JVMConstantGenerator, JVMStaticMethodConst
+     JVMConstantGenerator, JVMStaticMethodConst, JVMCustomDictConst
 from pypy.translator.jvm.prebuiltnodes import create_interlink_node
 
 class JvmError(Exception):
@@ -128,10 +128,18 @@
         print "... completed!"
                            
         self.compiled = True
-        self._compile_helper(('DictItemsIterator',
-                              'PyPy',
+        self._compile_helper(('Callback',
+                              'CustomDict',
+                              'DictItemsIterator',
+                              'Equals',
                               'ExceptionWrapper',
-                              'Interlink'))
+                              'Filter',
+                              'FilterIterator',
+                              'FilterSet',
+                              'HashCode',
+                              'Interlink',
+                              'PyPy',
+                              ))
 
     def _make_str(self, a):
         if isinstance(a, ootype._string):
@@ -199,6 +207,7 @@
     log = log
 
     ConstantGenerator = JVMConstantGenerator
+    CustomDictConst   = JVMCustomDictConst
     StaticMethodConst = JVMStaticMethodConst
     
     def __init__(self, tmpdir, translator, entrypoint):

Modified: pypy/dist/pypy/translator/jvm/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/metavm.py	(original)
+++ pypy/dist/pypy/translator/jvm/metavm.py	Wed Apr  4 09:52:50 2007
@@ -89,3 +89,28 @@
 
         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
+        if fn.value:
+            # Standalone function: find the delegate class and
+            # instantiate it.
+            assert method_name.value is None
+            smimpl = fn.value.concretize().value   # ootype._static_meth
+            db.record_delegate(smimpl._TYPE)       # _TYPE is a StaticMethod
+            ty = db.record_delegate_standalone_func_impl(smimpl.graph)
+            gen.new_with_jtype(ty)
+        else:
+            # Bound method: create a wrapper bound to the given
+            # object, using the "bind()" static method that bound
+            # method wrapper classes have.
+            INSTANCE = obj.concretetype
+            method_name = method_name.value
+            ty = db.record_delegate_bound_method_impl(INSTANCE, method_name)
+            gen.load(obj)
+            gen.emit(ty.bind_method)
+    def render(self, generator, op):
+        self._load_func(generator, *op.args[1:4])
+        self._load_func(generator, *op.args[4:7])
+        generator.emit(jvmgen.CUSTOMDICTMAKE)
+NewCustomDict = _NewCustomDict()

Modified: pypy/dist/pypy/translator/jvm/node.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/node.py	(original)
+++ pypy/dist/pypy/translator/jvm/node.py	Wed Apr  4 09:52:50 2007
@@ -13,7 +13,6 @@
 interface defined by database.JvmType.
 """
 
-
 from pypy.objspace.flow import \
      model as flowmodel
 from pypy.rpython.lltypesystem import \
@@ -22,7 +21,7 @@
      ootype, rclass
 from pypy.translator.jvm.typesystem import \
      JvmClassType, jString, jStringArray, jVoid, jThrowable, jInt, jPyPyMain, \
-     jObject, JvmType, jStringBuilder, jPyPyInterlink
+     jObject, JvmType, jStringBuilder, jPyPyInterlink, jCallbackInterfaces
 from pypy.translator.jvm.opcodes import \
      opcodes
 from pypy.translator.jvm.option import \
@@ -359,20 +358,27 @@
     abstract class Foo {
       public abstract ReturnType invoke(Arg1, Arg2, ...);
     }
-    
+
+    Depending on the signature of Arg1, Arg2, and ReturnType, this
+    abstract class may have additional methods and may implement
+    interfaces such as PyPy.Equals or PyPy.HashCode.  This is to allow
+    it to interface with the the standalone Java code.  See
+    the pypy.Callback interface for more information.    
     """
-    def __init__(self, name, STATIC_METHOD, jargtypes, jrettype):
+    def __init__(self, name, jargtypes, jrettype):
         """
         argtypes: list of JvmTypes
         rettype: JvmType
         """
         JvmClassType.__init__(self, name)
-        self.STATIC_METHOD = STATIC_METHOD
         assert isinstance(jrettype, JvmType)
         self.java_argument_types = [self] + list(jargtypes)
         self.java_return_type = jrettype
         self.dump_method = ConstantStringDumpMethod(
             self, "StaticMethodInterface")
+        self.invoke_method_obj = jvmgen.Method.v(
+                self, 'invoke',
+                self.java_argument_types[1:], self.java_return_type)
         
     def lookup_field(self, fieldnm):
         """ Given a field name, returns a jvmgen.Field object """
@@ -381,18 +387,58 @@
         """ Given the method name, returns a jvmgen.Method object """
         assert isinstance(self.java_return_type, JvmType)
         if methodnm == 'invoke':
-            return jvmgen.Method.v(
-                self, 'invoke',
-                self.java_argument_types[1:], self.java_return_type)
+            return self.invoke_method_obj
         raise KeyError(methodnm) # only one method
     def render(self, gen):
         assert isinstance(self.java_return_type, JvmType)
+
+        # Scan through the jCallbackInterfaces and look for any
+        # that apply.
+        for jci in jCallbackInterfaces:
+            if jci.matches(self.java_argument_types[1:], self.java_return_type):
+                break
+        else:
+            jci = None
+        
         gen.begin_class(self, jObject, abstract=True)
+        if jci: gen.implements(jci)
         gen.begin_constructor()
         gen.end_constructor()
+        
         gen.begin_function('invoke', [], self.java_argument_types,
                            self.java_return_type, abstract=True)
         gen.end_function()
+
+        # Because methods in the JVM are identified by both their name
+        # and static signature, we need to create a dummy "invoke"
+        # method if the Java callback interface argument types don't
+        # match the actual types for this method.  For example, the
+        # equals interface has the static signature
+        # "(Object,Object)=>boolean", but there may be static methods
+        # with some signature "(X,Y)=>boolean" where X and Y are other
+        # types.  In that case, we create an adaptor method like:
+        #
+        #   boolean invoke(Object x, Object y) {
+        #     return invoke((X)x, (Y)y);
+        #   }
+        if (jci and
+            (jci.java_argument_types != self.java_argument_types[1:] or
+             jci.java_return_type != self.java_return_type)):
+
+            jci_jargs = [self] + list(jci.java_argument_types)
+            jci_ret = jci.java_return_type
+            gen.begin_function('invoke', [], jci_jargs, jci_ret)
+            idx = 0
+            for jci_arg, self_arg in zip(jci_jargs, self.java_argument_types):
+                gen.load_jvm_var(jci_arg, idx)
+                if jci_arg != self_arg:
+                    gen.prepare_generic_result_with_jtype(self_arg)
+                idx += jci_arg.descriptor.type_width()
+            gen.emit(self.invoke_method_obj)
+            assert jci_ret == self.java_return_type # no variance here currently
+            gen.return_val(jci_ret)
+            gen.end_function()
+            
         gen.end_class()
 
 class StaticMethodImplementation(Node, JvmClassType):
@@ -406,26 +452,81 @@
           return SomeStaticClass.StaticMethod(Arg1, Arg2);
         }
     }
+
+    If the bound_to_jty argument is not None, then this class
+    represents a bound method, and looks something like:
+
+    class Bar extends Foo {
+        Qux bound_to;
+        public static Bar bind(Qux to) {
+          Bar b = new Bar();
+          b.bound_to = to;
+          return b;
+        }
+        public ReturnType invoke(Arg1, Arg2) {
+          return bound_to.SomeMethod(Arg1, Arg2);
+        }
+    }
     """
-    def __init__(self, name, interface, impl_method):
-        JvmClassType.__init__(self, name)        
-        self.super_class = interface
+    def __init__(self, name, super_class, bound_to_jty, impl_method):
+        JvmClassType.__init__(self, name)
+        self.super_class = super_class
         self.impl_method = impl_method
         self.dump_method = ConstantStringDumpMethod(
             self, "StaticMethodImplementation")
+
+        if bound_to_jty:
+            self.bound_to_jty = bound_to_jty
+            self.bound_to_fld = jvmgen.Field(
+                self.name, 'bound_to', bound_to_jty, False)
+            self.bind_method = jvmgen.Method.s(
+                self, 'bind', (self.bound_to_jty,), self)                
+        else:
+            self.bound_to_jty = None
+            self.bound_to_fld = None
+            self.bind_method = None
+            
     def lookup_field(self, fieldnm):
-        """ Given a field name, returns a jvmgen.Field object """
+        if self.bound_to_fld and fieldnm == self.bound_to_fld.name:
+            return self.bound_to_fld
         return self.super_class.lookup_field(fieldnm)
     def lookup_method(self, methodnm):
-        """ Given the method name, returns a jvmgen.Method object """
+        if self.bind_method and methodnm == 'bind':
+            return self.bind_method
         return self.super_class.lookup_method(methodnm)
     def render(self, gen):
         gen.begin_class(self, self.super_class)
+
+        if self.bound_to_fld:
+            gen.add_field(self.bound_to_fld)
+            
         gen.begin_constructor()
         gen.end_constructor()
+
+        # Emit the "bind" function which creates an instance if there is
+        # a bound field:
+        if self.bound_to_jty:
+            assert self.bound_to_fld and self.bind_method
+            gen.begin_function(
+                'bind', [], (self.bound_to_jty,), self, static=True)
+            gen.new_with_jtype(self)
+            gen.emit(jvmgen.DUP)
+            gen.load_jvm_var(self.bound_to_jty, 0)
+            self.bound_to_fld.store(gen)
+            gen.return_val(self)
+            gen.end_function()
+
+        # Emit the invoke() function, which just re-pushes the
+        # arguments and then invokes either the (possibly static)
+        # method self.impl_method.  Note that if we are bound to an
+        # instance, we push that as the this pointer for
+        # self.impl_method.
         gen.begin_function('invoke', [],
                            self.super_class.java_argument_types,
                            self.super_class.java_return_type)
+        if self.bound_to_fld:
+            gen.load_jvm_var(self, 0)
+            gen.emit(self.bound_to_fld)
         for i in range(len(self.super_class.java_argument_types)):
             if not i: continue # skip the this ptr
             gen.load_function_argument(i)
@@ -457,6 +558,11 @@
         # attributes: name, method() and render().  Usually, this is a
         # Function object, but in some subclasses it is not.
 
+    def simple_name(self):
+        dot = self.name.rfind('.')
+        if dot == -1: return self.name
+        return self.name[dot+1:]
+
     def set_super_class(self, supercls):
         self.super_class = supercls
 

Modified: pypy/dist/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/dist/pypy/translator/jvm/opcodes.py	Wed Apr  4 09:52:50 2007
@@ -9,7 +9,7 @@
      PushArg, PushAllArgs, StoreResult, InstructionList, New, DoNothing, Call,\
      SetField, GetField, DownCast, RuntimeNew, OOString, CastTo
 from pypy.translator.jvm.metavm import \
-     IndirectCall, JvmCallMethod, TranslateException
+     IndirectCall, JvmCallMethod, TranslateException, NewCustomDict
 
 import pypy.translator.jvm.generator as jvmgen
 import pypy.translator.jvm.typesystem as jvmtype
@@ -53,10 +53,10 @@
     'oohash':                   [PushAllArgs, jvmgen.OBJHASHCODE, StoreResult], 
     'oostring':                 [OOString, StoreResult],
     #'ooparse_int':              [PushAllArgs, 'call int32 [pypylib]pypy.runtime.Utils::OOParseInt(string, int32)'],
-    #'oonewcustomdict':          [NewCustomDict],
+    'oonewcustomdict':          [NewCustomDict, StoreResult],
     #
     'same_as':                  DoNothing,
-    #'hint':                     [PushArg(0), StoreResult],
+    'hint':                     [PushArg(0), StoreResult],
     'direct_call':              [Call, StoreResult],
     'indirect_call':            [PushAllArgs, IndirectCall, StoreResult],
     #

Added: pypy/dist/pypy/translator/jvm/src/pypy/Callback.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/Callback.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,26 @@
+package pypy;
+
+/**
+ * This interface has no function but to serve as a documentation
+ * point and marker.  All the interfaces which inherit from it are
+ * "callback" interfaces: that means that they are used to allow the
+ * standalone Java code to invoke methods defined in the RPython code.
+ * 
+ * Whenever a standalone RPython module has a signature that might
+ * fit as one of the appropriate callbacks, it simply implements required
+ * interface.  Note that these callback interfaces are simply hard-coded
+ * into database.  A more flexible mechanism may eventually be required.
+ * 
+ * As an example, consider {@link HashCode}.  Any time that we generate
+ * a standalone method which returns a signed integer and takes a single
+ * object, we cause it to implement {@link HashCode}, which then allows it
+ * to be used in an {@link #RDict}. 
+ * 
+ * These callback functions are hard-coded in jvm/typesystem.py.  To add
+ * new ones simply add items to the list.
+ * 
+ * @author Niko Matsakis <niko at alum.mit.edu>
+ */
+public interface Callback {
+
+}

Added: pypy/dist/pypy/translator/jvm/src/pypy/CustomDict.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/CustomDict.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,145 @@
+package pypy;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * An implementation of the Map interface where the hashcode and 
+ * equals methods are supplied in the constructor.  It is backed
+ * by a standard Java hashmap, and uses adapter classes to bridge
+ * between them.
+ * @author Niko Matsakis <niko at alum.mit.edu>
+ */
+public class CustomDict<K,V> implements Map<K,V>
+{
+    public final Equals equals;
+    public final HashCode hashCode;
+    public final Map<Adapter<K>, V> backingMap = 
+        new HashMap<Adapter<K>, V>();
+    
+    public static <K,V> CustomDict<K,V> make(
+            final Equals equals, final HashCode hashCode)
+    {
+        return new CustomDict<K,V>(equals, hashCode);
+    }
+    
+    public CustomDict(final Equals equals, final HashCode hashCode) {
+        this.hashCode = hashCode;
+        this.equals = equals;
+        System.err.println("Equals: "+equals.getClass());
+        System.err.println("HashCode: "+hashCode.getClass());
+    }
+
+    public class Adapter<AK> {
+        public final AK object;
+        public Adapter(AK object) {
+            this.object = object;
+        }
+
+        public int hashCode() {
+            return hashCode.invoke(this.object);
+        }
+
+        public boolean equals(Object other) {
+            return equals.invoke(this.object, ((Adapter<?>)other).object);
+        }
+    }
+    
+    private <AK> Adapter<AK> w(AK k) {
+        return new Adapter<AK>(k);
+    }
+
+    public void clear() {
+        this.backingMap.clear();
+    }
+
+    public boolean containsKey(Object arg0) {
+        return this.backingMap.containsKey(w(arg0));
+    }
+
+    public boolean containsValue(Object value) {
+        return this.backingMap.containsValue(value);
+    }
+
+    public Set<Map.Entry<K, V>> entrySet() {
+        return new FilterSet<Map.Entry<Adapter<K>, V>, Map.Entry<K,V>>(
+                this.backingMap.entrySet(),
+                new Filter<Map.Entry<Adapter<K>, V>, Map.Entry<K,V>>() {
+                    public Map.Entry<Adapter<K>, V> from(final Map.Entry<K, V> to) {
+                        return new Map.Entry<Adapter<K>, V>() {
+                            public Adapter<K> getKey() {
+                                return new Adapter<K>(to.getKey());
+                            }
+                            public V getValue() {
+                                return to.getValue();
+                            }
+                            public V setValue(V value) {
+                                return to.setValue(value);
+                            }                            
+                        };
+                    }
+
+                    public Map.Entry<K, V> to(final Map.Entry<Adapter<K>, V> from) {
+                        return new Map.Entry<K, V>() {
+                            public K getKey() {
+                                return from.getKey().object;
+                            }
+                            public V getValue() {
+                                return from.getValue();
+                            }
+                            public V setValue(V value) {
+                                return from.setValue(value);
+                            }                            
+                        };                    
+                    }
+                });
+    }
+
+    public V get(Object key) {
+        return this.backingMap.get(w(key));
+    }
+
+    public boolean isEmpty() {
+        return this.backingMap.isEmpty();
+    }
+
+    public Set<K> keySet() {
+        return new FilterSet<Adapter<K>, K>(
+                this.backingMap.keySet(),
+                new Filter<Adapter<K>, K>() {
+                    public Adapter<K> from(K to) {
+                        return new Adapter<K>(to);
+                    }
+                    public K to(Adapter<K> from) {
+                        return from.object;
+                    }                    
+                });
+    }
+
+    public V put(K key, V value) {
+        return this.backingMap.put(w(key), value);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> t) {
+        for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) {
+            this.put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    public V remove(Object key) {
+        return this.backingMap.remove(w(key));
+    }
+
+    public int size() {
+        return this.backingMap.size();
+    }
+
+    public Collection<V> values() {
+        return this.backingMap.values();
+    }
+
+}
\ No newline at end of file

Added: pypy/dist/pypy/translator/jvm/src/pypy/Equals.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/Equals.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,6 @@
+package pypy;
+
+/** @see Callback */
+public interface Equals extends Callback {
+    public boolean invoke(Object one, Object two);
+}

Added: pypy/dist/pypy/translator/jvm/src/pypy/Filter.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/Filter.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,6 @@
+package pypy;
+
+public interface Filter<F, T> {
+    public T to(F from);
+    public F from(T to);
+}

Added: pypy/dist/pypy/translator/jvm/src/pypy/FilterIterator.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/FilterIterator.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,27 @@
+package pypy;
+
+import java.util.Iterator;
+
+public class FilterIterator<F, T> implements Iterator<T> {
+    
+    public final Iterator<F> iter;
+    public final Filter<F,T> filter;      
+    
+    public FilterIterator(final Iterator<F> iter, final Filter<F, T> filter) {
+        this.iter = iter;
+        this.filter = filter;
+    }
+
+    public boolean hasNext() {
+        return this.iter.hasNext();        
+    }
+
+    public T next() {
+        return filter.to(this.iter.next());
+    }
+
+    public void remove() {
+        this.iter.remove();
+    }
+
+}

Added: pypy/dist/pypy/translator/jvm/src/pypy/FilterSet.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/FilterSet.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,85 @@
+package pypy;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+public class FilterSet<F,T> implements Set<T> {
+    
+    public final Set<F> base;    
+    public final Filter<F,T> filter;
+
+    public FilterSet(final Set<F> base, final Filter<F, T> filter) {
+        this.base = base;
+        this.filter = filter;
+    }
+
+    public boolean add(T o) {
+        return this.base.add(filter.from(o));
+    }
+
+    public boolean addAll(Collection<? extends T> c) {
+        boolean res = false;
+        for (T t : c) {
+            res = add(t) || res;
+        }
+        return res;
+    }
+
+    public void clear() {
+        this.base.clear();
+    }
+
+    public boolean contains(Object o) {
+        return this.base.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return this.base.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return this.base.isEmpty();
+    }
+
+    public Iterator<T> iterator() {
+        return new FilterIterator<F,T>(this.base.iterator(), filter);
+    }
+
+    public boolean remove(Object o) {
+        return this.base.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return this.base.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        return this.base.retainAll(c);
+    }
+
+    public int size() {
+        return this.base.size();
+    }
+
+    @SuppressWarnings("unchecked")
+    public Object[] toArray() {        
+        Object[] froms = this.base.toArray();
+        Object[] tos = new Object[froms.length];
+        for (int i = 0; i < froms.length; i++)
+            tos[i] = filter.to((F)tos[i]);
+        return tos;
+    }
+
+    public <X> X[] toArray(X[] a) {
+        Object[] arr = toArray();
+        if (a.length == arr.length) {
+            System.arraycopy(arr, 0, a, 0, a.length);
+            return a;
+        }
+        // can't be bothered to navigate reflection apis right now
+        throw new RuntimeException("TODO"); 
+    }
+
+}

Added: pypy/dist/pypy/translator/jvm/src/pypy/HashCode.java
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/src/pypy/HashCode.java	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,6 @@
+package pypy;
+
+/** @see Callback */
+public interface HashCode extends Callback {
+    public int invoke(Object obj);
+}

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	Wed Apr  4 09:52:50 2007
@@ -4,6 +4,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * Class with a number of utility routines.
@@ -521,15 +522,34 @@
 
     // ----------------------------------------------------------------------
     // Dicts
+    //
+    // Note: it's easier to cut and paste a few methods here than
+    // make the code generator smarter to avoid the duplicate code.
 
     public static boolean ll_remove(HashMap map, Object key) {
         return map.remove(key) != null;
     }
 
+    public static boolean ll_remove(CustomDict map, Object key) {
+        return map.remove(key) != null;
+    }
+    
     public static DictItemsIterator ll_get_items_iterator(HashMap map) {
         return new DictItemsIterator(map);
     }
+    
+    public static DictItemsIterator ll_get_items_iterator(CustomDict map) {
+        return new DictItemsIterator(map);
+    }
 
+    public static <K,V> CustomDict<K,V> ll_copy(CustomDict<K,V> map) {
+        CustomDict<K,V> cd = new CustomDict<K,V>(map.equals, map.hashCode);
+        for (Map.Entry<K,V> me : map.entrySet()) {
+            cd.put(me.getKey(), me.getValue());
+        }
+        return cd;
+    }
+    
     // ----------------------------------------------------------------------
     // Lists
 

Modified: pypy/dist/pypy/translator/jvm/test/test_dict.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/test/test_dict.py	(original)
+++ pypy/dist/pypy/translator/jvm/test/test_dict.py	Wed Apr  4 09:52:50 2007
@@ -17,5 +17,4 @@
         py.test.skip("Iteration over empty dict is not supported, yet")
 
 class TestJvmConstantDict(JvmTest, oodict.BaseTestConstantDict):
-    def test_constant_r_dict(self):
-        py.test.skip("JVM doesn't support r_dict so far")
+    pass

Added: pypy/dist/pypy/translator/jvm/test/test_objectmodel.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/jvm/test/test_objectmodel.py	Wed Apr  4 09:52:50 2007
@@ -0,0 +1,9 @@
+import py
+from pypy.translator.jvm.test.runtest import JvmTest
+from pypy.rlib.test.test_objectmodel import BaseTestObjectModel
+
+from pypy.rlib.objectmodel import cast_object_to_weakgcaddress,\
+     cast_weakgcaddress_to_object
+
+class TestJvmObjectModel(JvmTest, BaseTestObjectModel):
+    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	Wed Apr  4 09:52:50 2007
@@ -1,6 +1,26 @@
 from pypy.translator.jvm.test.runtest import JvmTest
 from pypy.translator.oosupport.test_template.snippets import BaseTestSnippets
 
-class TestSnippets(BaseTestSnippets, JvmTest):
+class Foo:
     pass
 
+class TestSnippets(BaseTestSnippets, JvmTest):
+
+    def test_equals_func(self):
+        def equals(x, y):
+            return x == y
+        def unequals(x, y):
+            return x != y
+        def base_func(op):
+            res = 0
+            a = Foo()
+            b = Foo()
+            if op: func = equals
+            else:  func = unequals
+            if func(a,b): res += 1
+            if func(a,a): res += 10
+            if func(b,b): res += 100
+            return res
+        assert self.interpret(base_func, [True])  == 110
+        assert self.interpret(base_func, [False]) == 001
+

Modified: pypy/dist/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/dist/pypy/translator/jvm/typesystem.py	(original)
+++ pypy/dist/pypy/translator/jvm/typesystem.py	Wed Apr  4 09:52:50 2007
@@ -127,7 +127,7 @@
 
     def __repr__(self):
         return "%s<%s>" % (self.__class__.__name__, self.descriptor)
-
+    
 class JvmClassType(JvmType):
     """
     Base class used for all class instances.  Kind of an abstract class;
@@ -143,6 +143,9 @@
     def lookup_method(self, methodnm):
         raise KeyError(fieldnm) # we treat as opaque type
 
+class JvmInterfaceType(JvmClassType):
+    pass
+
 jIntegerClass = JvmClassType('java.lang.Integer')
 jLongClass = JvmClassType('java.lang.Long')
 jDoubleClass = JvmClassType('java.lang.Double')
@@ -153,15 +156,15 @@
 jObject = JvmClassType('java.lang.Object')
 jString = JvmClassType('java.lang.String')
 jCharSequence = JvmClassType('java.lang.CharSequence')
-jArrayList = JvmClassType('java.util.ArrayList')
 jArrays = JvmClassType('java.util.Arrays')
+jMap = JvmInterfaceType('java.util.Map')
 jHashMap = JvmClassType('java.util.HashMap')
 jIterator = JvmClassType('java.util.Iterator')
 jClass = JvmClassType('java.lang.Class')
 jStringBuilder = JvmClassType('java.lang.StringBuilder')
 jPrintStream = JvmClassType('java.io.PrintStream')
 jMath = JvmClassType('java.lang.Math')
-jList = JvmClassType('java.util.List')
+jList = JvmInterfaceType('java.util.List')
 jArrayList = JvmClassType('java.util.ArrayList')
 jPyPy = JvmClassType('pypy.PyPy')
 jPyPyExcWrap = JvmClassType('pypy.ExceptionWrapper')
@@ -169,6 +172,7 @@
 jPyPyMain = JvmClassType('pypy.Main')
 jPyPyDictItemsIterator = JvmClassType('pypy.DictItemsIterator')
 jPyPyInterlink = JvmClassType('pypy.Interlink')
+jPyPyCustomDict = JvmClassType('pypy.CustomDict')
 
 jArithmeticException = JvmClassType('java.lang.ArithmeticException')
 
@@ -184,6 +188,7 @@
         raise KeyError(fieldnm)        # Scalar objects have no fields
     def lookup_method(self, methodnm): 
         raise KeyError(methodnm)       # Scalar objects have no methods
+
 jVoid = JvmScalarType('V', None, None)
 jInt = JvmScalarType('I', jIntegerClass, 'intValue')
 jLong = JvmScalarType('J', jLongClass, 'longValue')
@@ -240,9 +245,9 @@
     def full_types(self, method_name):
         """
         Returns a tuple of argument and return types for the method
-        named 'method_name'.  These are the actual generic types.  The set method for
-        a list of strings, for example, might return:
-          ( [INT, STRING], VOID )
+        named 'method_name'.  These are the actual generic types.  The
+        set method for a list of strings, for example, might return:
+        ( [INT, STRING], VOID )
         """
         GENMETH = self.OOTYPE._GENERIC_METHODS[method_name]
         ARGS, RESULT = (GENMETH.ARGS, GENMETH.RESULT)
@@ -253,9 +258,9 @@
     def erased_types(self, method_name):
         """
         Returns a tuple of argument and return types for the method
-        named 'method_name'.  These are the erased generic types.  The set method for
-        a list of strings, for example, might return:
-          ( [INT, OBJECT], VOID )
+        named 'method_name'.  These are the erased generic types.  The
+        set method for a list of strings, for example, might return:
+        ( [INT, OBJECT], VOID )
         """
         GENMETH = self.OOTYPE._GENERIC_METHODS[method_name]
         ARGS, RESULT = (GENMETH.ARGS, GENMETH.RESULT)
@@ -263,5 +268,35 @@
         RESULT = self.generics.get(RESULT, (None,RESULT))[1]
         return (ARGS, RESULT)
 
+# ______________________________________________________________________
+# Java Callback Interfaces
+#
+# A list of interfaces which static functions that we generate will
+# automatically implement if application.  See the pypy/Callback.java,
+# node.py/StaticMethodInterface for more information.
+
+jCallbackInterfaces = [] # collects all of the defined JvmCallbackInterfaces
+
+class JvmCallbackInterface(JvmInterfaceType):
+    def __init__(self, name, jargtypes, jrettype):
+        JvmInterfaceType.__init__(self, name)
+        self.java_argument_types = jargtypes
+        self.java_return_type = jrettype
+        jCallbackInterfaces.append(self)  # add to global list
+    def matches(self, jargtypes, jrettype):
+        """ Given a set of argument types and a return type for some
+        static function defined by the user, returns true if this
+        JvmCallbackInterface applies.  Note that the types don't have
+        to match exactly: we assume that (in the list of arguments)
+        jObject is used as a wildcard, and some adaptation code may
+        have to be inserted."""
+        if len(self.java_argument_types) != len(jargtypes):
+            return False
+        for expjarg, actjarg in zip(self.java_argument_types, jargtypes):
+            if expjarg == jObject: continue # hack: assume obj means any type
+            if expjarg != actjarg: return False
+        return jrettype == self.java_return_type
     
-    
+jPyPyHashCode = JvmCallbackInterface('pypy.HashCode', [jObject], jInt)
+jPyPyEquals = JvmCallbackInterface('pypy.Equals', [jObject, jObject], jBool)
+



More information about the Pypy-commit mailing list