This is the mail archive of the kawa@sources.redhat.com mailing list for the Kawa project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

invoke-special patch


Attached is a patch to implement invoke-special.  This procedure is very
similar to invoke and invoke-static but is used to invoke a method in
certain special ways.  One interesting use is to invoke a method in your
super-class like the Java language super keyword.

This patch should be considered highly experimental: apply at your own
risk.  Really, you should wait for Per to examine it before using the
feature.

Regards,
Chris Dean

Index: doc/kawa.texi
===================================================================
RCS file: /cvs/kawa/kawa/doc/kawa.texi,v
retrieving revision 1.135
diff -u -r1.135 kawa.texi
--- doc/kawa.texi	3 Jul 2003 05:51:09 -0000	1.135
+++ doc/kawa.texi	8 Jul 2003 19:33:44 -0000
@@ -3887,6 +3887,35 @@
 @end example
 @end defun
 
+@defun invoke-special class receiver-object name arg ...
+The @var{class} can be a @code{<java.lang.Class>}, a
+@code{<gnu.bytecode.ClassType>}, or a @code{<symbol>} or @code{<string>}
+that names a Java class.  
+The @var{name} can be @code{<symbol>} or
+@code{<string>} that names one or more methods in the Java class.
+The name is "mangled" (@pxref{Mangling}) into a valid Java name.
+
+This procedure is very similar to @code{invoke} and @code{invoke-static}
+but is used to invoke a method in certain special ways.  One interesting
+use is to invoke a method in your super-class like the Java language
+@code{super} keyword.
+
+Any methods in the specified @var{class} that match "@var{name}" or
+"@var{name}$V" collectively form a generic procedure.  That generic
+procedure is then applied as in @code{invoke} using the
+@code{receiver-object} and the arguments (if any).
+
+This procedure is only available at compile-time.
+
+@example
+(define-simple-class <MyClass> (<java.util.Date>)
+  ((get-year) :: <int>
+   (+ (invoke-special <java.util.Date> (this) 'get-year)) 1900)
+  ((set-year (year :: <int>)) :: <void>
+   (invoke-special <java.util.Date> (this) 'set-year (- year 1900))))
+@end example
+@end defun
+
 @defun class-methods class name
 Return a generic function containing those methods of @var{class}
 that match the name @var{name}, in the sense of @code{invoke-static}.
Index: gnu/expr/PrimProcedure.java
===================================================================
RCS file: /cvs/kawa/kawa/gnu/expr/PrimProcedure.java,v
retrieving revision 1.37
diff -u -r1.37 PrimProcedure.java
--- gnu/expr/PrimProcedure.java	14 Jun 2003 03:35:15 -0000	1.37
+++ gnu/expr/PrimProcedure.java	8 Jul 2003 19:33:45 -0000
@@ -14,7 +14,8 @@
   Type[] argTypes;
   Method method;
   int op_code;
-
+  boolean is_special = false;
+  
   /** If non-null, the LambdaExp that this PrimProcedure implements. */
   LambdaExp source;
 
@@ -220,6 +221,16 @@
       interpreter.getTypeFor(method.getReturnType().getReflectClass());
   }
 
+  public PrimProcedure(Method method, boolean is_special,
+                       Interpreter interpreter)
+  {
+    this(method, interpreter);
+    if (is_special) {
+      this.is_special = true;
+      op_code = 183;
+    }
+  }
+  
   private void init(Method method)
   {
     this.method = method;
@@ -281,7 +292,9 @@
 
   public final boolean getStaticFlag()
   {
-    return method == null || method.getStaticFlag() || op_code == 183;
+    return method == null 
+      || method.getStaticFlag()
+      || (op_code == 183 && ! is_special);
   }
 
   public final Type[] getParameterTypes() { return argTypes; }
@@ -323,17 +336,21 @@
           }
         if (i >= args.length)
           break;
+        
         if (i >= fix_arg_count)
           {
             code.emitDup(1); // dup array.
             code.emitPushInt(i - fix_arg_count);
           }
         else
-          arg_type = argDecl != null && (is_static || i > 0) ? argDecl.getType()
-	    : is_static ? argTypes[i + skipArg]
-            : i==0 ? thisType
-            : argTypes[i-1];
-	if (comp.immediate && arg_type instanceof ClassType)
+          arg_type = (argDecl != null && (is_static || i > 0) 
+                      ? argDecl.getType() 
+                      : (is_static 
+                         ? argTypes[i + skipArg]
+                         : (i==0 
+                            ? thisType
+                            : argTypes[i-1])));
+        if (comp.immediate && arg_type instanceof ClassType)
 	  comp.usedClass((ClassType) arg_type);
 	Target target =
 	  source == null ? CheckedTarget.getInstance(arg_type, name, i)
@@ -364,11 +381,13 @@
 	  }
       }
 
-    if (opcode() == 183) // invokespecial == primitive-constructor
+    // invokespecial == primitive-constructor
+    if (opcode() == 183 && ! is_special) 
       {
-	code.emitNew(mclass);
-	code.emitDup(mclass);
+        code.emitNew(mclass);
+        code.emitDup(mclass);
       }
+    
 
     Expression[] args = exp.getArgs();
     String arg_error = WrongArguments.checkArgCount(this, args.length);
Index: gnu/kawa/reflect/ClassMethods.java
===================================================================
RCS file: /cvs/kawa/kawa/gnu/kawa/reflect/ClassMethods.java,v
retrieving revision 1.18
diff -u -r1.18 ClassMethods.java
--- gnu/kawa/reflect/ClassMethods.java	16 May 2003 18:01:30 -0000	1.18
+++ gnu/kawa/reflect/ClassMethods.java	8 Jul 2003 19:33:45 -0000
@@ -70,62 +70,83 @@
     return result;
   }
 
+  public static int removeMethods(Vector methods)
+  {
+    // Remove over-ridden methods.
+    int mlength = methods.size();
+  loopi:
+    for (int i = 1;  i < mlength; )
+      {
+        Method method1 = (Method) methods.elementAt(i);
+        ClassType class1 = method1.getDeclaringClass();
+        Type[] types1 = method1.getParameterTypes();
+        int tlen = types1.length;
+        for (int j = 0;  j < i;  j++)
+          {
+            Method method2 = (Method) methods.elementAt(j);
+            Type[] types2 = method2.getParameterTypes();
+            if (tlen != types2.length)
+              continue;
+            int k;
+            for (k = tlen;  --k >= 0;  )
+              {
+                if (types1[k] != types2[k])
+                  break;
+              }
+            if (k >= 0)
+              continue;
+            if (class1.isSubtype(method2.getDeclaringClass()))
+                methods.setElementAt(method1, j);
+            methods.setElementAt(methods.elementAt(mlength - 1), i);
+            mlength--;
+            // Re-do current i, since methods[i] replaced.
+            continue loopi;
+          }
+        i++;
+      }
+
+    return mlength;
+  }
+
+
+  /** Return the methods of a class with the specified name and flag.
+   * @return an array containing the methods.
+   */
+  public static PrimProcedure[] getMethods(ClassType dtype, String mname,
+                                           int modifiers, int modmask,
+                                           Interpreter interpreter)
+  {
+    return getMethods(dtype,mname,modifiers,modmask,false,interpreter);
+  }
+  
+    
   /** Return the methods of a class with the specified name and flag.
    * @return an array containing the methods.
    */
   public static PrimProcedure[] getMethods(ClassType dtype, String mname,
                                            int modifiers, int modmask,
+                                           boolean is_special,
                                            Interpreter interpreter)
   {
-    MethodFilter filter = new MethodFilter(mname, modifiers, modmask);
+    MethodFilter filter = new MethodFilter(mname, modifiers, modmask, 
+                                           is_special ? dtype : null);
     Vector methods = new Vector();
     dtype.getMethods(filter, "<init>".equals(mname) ? 0 : 2,
 		     methods,
 		     "-"); // Should be more specific - FIXME
 
-    // Remove over-ridden methods.
-    int mlength = methods.size();
-  loopi:
-    for (int i = 1;  i < mlength; )
-    {
-      Method method1 = (Method) methods.elementAt(i);
-      ClassType class1 = method1.getDeclaringClass();
-      Type[] types1 = method1.getParameterTypes();
-      int tlen = types1.length;
-      for (int j = 0;  j < i;  j++)
-	{
-	  Method method2 = (Method) methods.elementAt(j);
-	  Type[] types2 = method2.getParameterTypes();
-	  if (tlen != types2.length)
-	    continue;
-	  int k;
-	  for (k = tlen;  --k >= 0;  )
-	    {
-	      if (types1[k] != types2[k])
-		break;
-	    }
-	  if (k >= 0)
-	    continue;
-	  if (class1.isSubtype(method2.getDeclaringClass()))
-	    methods.setElementAt(method1, j);
-	  methods.setElementAt(methods.elementAt(mlength - 1), i);
-	  mlength--;
-	  // Re-do current i, since methods[i] replaced.
-	  continue loopi;
-	}
-      i++;
-    }
-
+    int mlength = removeMethods(methods);
     PrimProcedure[] result = new PrimProcedure[mlength];
     int count = 0;
     for (int i = mlength;  --i >= 0; )
-    {
-      Method method = (Method) methods.elementAt(i);
-      if ((method.getModifiers() & modmask) != modifiers)
-	continue;
-      PrimProcedure pproc = new PrimProcedure(method, interpreter);
-      result[count++] = pproc;
-    }
+      {
+        Method method = (Method) methods.elementAt(i);
+        if ((method.getModifiers() & modmask) != modifiers)
+          continue;
+        PrimProcedure pproc = new PrimProcedure(method, is_special, 
+                                                interpreter);
+        result[count++] = pproc;
+      }
     return result;
   }
 
@@ -243,18 +264,22 @@
   String nameV;
   int modifiers;
   int modmask;
+  ClassType ctype;
 
-  public MethodFilter(String name, int modifiers, int modmask)
+  public MethodFilter(String name, int modifiers, int modmask, ClassType ctype)
   {
     this.name = name;
     this.nameV = name+"$V";
     this.modifiers = modifiers;
     this.modmask = modmask;
+    this.ctype = ctype;
   }
 
   public boolean select(Object value)
   {
     gnu.bytecode.Method method = (gnu.bytecode.Method) value;
+    if (ctype != null && method.getDeclaringClass().compare( ctype ) != 0)
+      return false;
     String mname = method.getName();
     return ((method.getModifiers() & modmask) == modifiers)
       && (mname.equals(name) || mname.equals(nameV));
Index: gnu/kawa/reflect/Invoke.java
===================================================================
RCS file: /cvs/kawa/kawa/gnu/kawa/reflect/Invoke.java,v
retrieving revision 1.31
diff -u -r1.31 Invoke.java
--- gnu/kawa/reflect/Invoke.java	6 Jun 2003 21:17:06 -0000	1.31
+++ gnu/kawa/reflect/Invoke.java	8 Jul 2003 19:33:45 -0000
@@ -14,6 +14,7 @@
 
   public static final Invoke invoke = new Invoke("invoke", 'V');
   public static final Invoke invokeStatic = new Invoke("invoke-static", 'S');
+  public static final Invoke invokeSpecial = new Invoke("invoke-special", 'P');
   public static final Invoke make = new Invoke("make", 'N');
 
   public Invoke(String name, char kind)
@@ -53,10 +54,14 @@
   protected static Object applyN (Invoke thisProc, Object[] args)
     throws Throwable
   {
+    int kind = thisProc.kind;
+    if (kind == 'P')
+      throw new RuntimeException(thisProc.getName() 
+                                 + ": invoke-special not allowed at run time");
+    
     int nargs = args.length;
     Procedure.checkArgCount(thisProc, nargs);
     Object arg0 = args[0];
-    int kind = thisProc.kind;
     ClassType dtype;
     String mname;
     if (kind == 'V')
@@ -163,23 +168,29 @@
   private int cachePossiblyApplicableMethodCount;
 
   protected PrimProcedure[] getMethods(ClassType ctype, String mname,
-                                       Expression[] args, int argsToSkip)
+                                       Expression[] args, int margsLength, 
+                                       int argsStartIndex, int objIndex)
   {
     if (args == cacheArgs)
       return cacheMethods;
-    int nargs = args.length;
-    Type[] atypes = new Type[nargs - argsToSkip];
-    int i = 0;
-    if (kind == 'V')
-      atypes[i++] = ctype;
-    for ( ; i < atypes.length;  i++)
-      atypes[i] = args[i+argsToSkip].getType();
-    PrimProcedure[] methods
-    = ClassMethods.getMethods(ctype, mname,
-                              kind == 's' ? Access.STATIC : 0,
-			      kind=='S' ? 0 : Access.STATIC,
-                              interpreter);
 
+    Type[] atypes = new Type[margsLength];
+
+    int dst = 0;
+    if (objIndex >= 0)
+      atypes[dst++] = ctype;
+    for (int src = argsStartIndex; 
+         src < args.length && dst < atypes.length; 
+         src++, dst++)
+      atypes[dst] = args[src].getType();
+
+    PrimProcedure[] methods
+      = ClassMethods.getMethods(ctype, mname,
+                                kind == 's' ? Access.STATIC : 0,
+                                kind == 'S' ? 0 : Access.STATIC,
+                                kind == 'P',
+                                interpreter);
+    
     long num = ClassMethods.selectApplicable(methods, atypes);
     cacheArgs = args;
     cacheDefinitelyApplicableMethodCount = (int) (num >> 32);
@@ -247,12 +258,54 @@
     return exp;
   }
 
+  private int calcMargsLength (int nargs) 
+  {
+    if (kind == 'V')
+      return nargs - 1;
+    if (kind == 'N')
+      return nargs - 1;
+    if (kind == 'S' || kind == 's' )
+      return nargs - 2;
+    if (kind == 'P')
+      return nargs - 2;
+    return -1;
+  }
+  
+  private int calcArgsStartIndex() 
+  {
+    if (kind == 'V')
+      return 2;
+    if (kind == 'N')
+      return 1;
+    if (kind == 'S' || kind == 's' )
+      return 2;
+    if (kind == 'P')
+      return 3;
+    return -1;
+  }
+
+  private int calcObjIndex( ) 
+  {
+    if (kind == 'V')
+      return 0;
+    if (kind == 'P')
+      return 1;
+    return -1;
+  }
+
   public Expression inline (ApplyExp exp, ExpWalker walker)
   {
     Expression[] args = exp.getArgs();
     int nargs = args.length;
     ClassType type = getClassType(args);
     String name = getMethodName(args);
+
+    int margsLength = calcMargsLength(nargs);
+    int argsStartIndex = calcArgsStartIndex();
+    int objIndex = calcObjIndex();
+    if (margsLength < 0 || argsStartIndex < 0)
+      walker.error('e', "unknown invoke kind: " + kind);
+                
     if (type != null && name != null
 	// We can't generate <init> until we know whether it needs
 	// a lexical link, which we don't know until FindCapturedVars is run.
@@ -265,8 +318,11 @@
           {
             try
               {
-                methods = getMethods(type, name, args,
-				     kind == 'S' || kind == 's' ? 2 : 1);
+                if (type.isValid())
+                  methods = getMethods(type, name, args, 
+                                       margsLength, argsStartIndex, objIndex);
+                else
+                  methods = null;
               }
             catch (Exception ex)
               {
@@ -280,7 +336,10 @@
           {
             int index = -1;
             if (methods.length == 0)
-              walker.error('w', "no method `"+name+"' in "+type.getName());
+              {
+                if (type != Type.getType( "java.lang.Object" ))
+                  walker.error('w', "no method `"+name+"' in "+type.getName());
+              }
             else if (okCount + maybeCount == 0)
               {
                 Object[] slots;
@@ -383,14 +442,14 @@
 	      }
             if (index >= 0)
               {
-                Expression[] margs
-                  = new Expression[nargs-(kind == 'S' || kind == 's' ? 2 : 1)];
-                int i = 0;
-                if (kind == 'V')
-		  margs[i++] = args[0];
-                System.arraycopy(args, kind == 'N' ? 1 : 2,
-                                 margs, i,
-                                 nargs - (kind == 'N' ? 1 : 2));
+                Expression[] margs = new Expression[margsLength];
+                int dst = 0;
+                if (objIndex >= 0)
+                  margs[dst++] = args[objIndex];
+                for (int src = argsStartIndex; 
+                     src < args.length && dst < margs.length; 
+                     src++, dst++)
+                  margs[dst] = args[src];
                 PrimProcedure method = methods[index];
                 return new ApplyExp(method, margs).setLine(exp);
               }
@@ -404,7 +463,7 @@
     if (args.length > 0)
       {
         Expression arg0 = args[0];
-        Type type = (kind == 'V' ? arg0.getType()
+        Type type = ((kind == 'V') ? arg0.getType()
                      : interpreter.getTypeFor(arg0));
 	if (type instanceof PairClassType)
 	  return ((PairClassType) type).instanceType;
@@ -418,8 +477,9 @@
   {
     if (kind == 'N')
       return "<init>";
-    if (args.length >= 2)
-      return ClassMethods.checkName(args[1], false);
+    int nameIndex = (kind == 'P' ? 2 : 1);
+    if (args.length >= nameIndex + 1)
+      return ClassMethods.checkName(args[nameIndex], false);
     return null;
   }
 
@@ -442,7 +502,8 @@
   public static synchronized PrimProcedure
   getStaticMethod(ClassType type, String name, Expression[] args)
   {
-    PrimProcedure[] methods = invokeStatic.getMethods(type, name, args, 0);
+    PrimProcedure[] methods = invokeStatic.getMethods(type, name, args, 
+                                                      args.length, 0, -1);
     int okCount = invokeStatic.cacheDefinitelyApplicableMethodCount;
     int maybeCount = invokeStatic.cachePossiblyApplicableMethodCount;
     int index;
Index: kawa/standard/Scheme.java
===================================================================
RCS file: /cvs/kawa/kawa/kawa/standard/Scheme.java,v
retrieving revision 1.137
diff -u -r1.137 Scheme.java
--- kawa/standard/Scheme.java	1 Jun 2003 16:53:30 -0000	1.137
+++ kawa/standard/Scheme.java	8 Jul 2003 19:33:45 -0000
@@ -471,6 +471,7 @@
 		   "staticField");
       define_proc("invoke", gnu.kawa.reflect.Invoke.invoke);
       define_field("invoke-static", "gnu.kawa.reflect.Invoke", "invokeStatic");
+      define_field("invoke-special", "gnu.kawa.reflect.Invoke", "invokeSpecial");
 
       define_proc("file-exists?", "kawa.lib.files");
       define_proc("file-directory?", "kawa.lib.files");
Index: gnu/bytecode/Access.java
===================================================================
RCS file: /cvs/kawa/kawa/gnu/bytecode/Access.java,v
retrieving revision 1.9
diff -u -r1.9 Access.java
--- gnu/bytecode/Access.java	4 Apr 2003 17:23:00 -0000	1.9
+++ gnu/bytecode/Access.java	8 Jul 2003 19:33:44 -0000
@@ -5,7 +5,7 @@
 import java.io.*;
 
 /** Access flags. */
-/* When using JDK 1.1, replace this class by java.lang.reflec.Modifiers. */
+/* When using JDK 1.1, replace this class by java.lang.reflect.Modifiers. */
 
 public class Access {
   static public final short PUBLIC      = 0x0001;
Index: gnu/bytecode/ClassType.java
===================================================================
RCS file: /cvs/kawa/kawa/gnu/bytecode/ClassType.java,v
retrieving revision 1.50
diff -u -r1.50 ClassType.java
--- gnu/bytecode/ClassType.java	17 May 2003 16:35:44 -0000	1.50
+++ gnu/bytecode/ClassType.java	8 Jul 2003 19:33:45 -0000
@@ -856,6 +856,11 @@
     return -3;
   }
 
+  public boolean isValid()
+  {
+    return getName() != null;
+  }
+
   public String toString()
   {
     return "ClassType " + getName();
Index: testsuite/classes1.scm
===================================================================
RCS file: /cvs/kawa/kawa/testsuite/classes1.scm,v
retrieving revision 1.4
diff -u -r1.4 classes1.scm
--- testsuite/classes1.scm	5 Apr 2003 22:30:28 -0000	1.4
+++ testsuite/classes1.scm	8 Jul 2003 19:33:45 -0000
@@ -22,3 +22,8 @@
   (syntax-rules ()
 		((define-class-using-syntax-rules name super parts ...)
 		 (define-simple-class name (super) parts ...))))
+
+(define-simple-class <SimpleDateTest> (<java.util.Date>)
+  ((get-year) :: <int>
+   (+ (invoke-special <java.util.Date> (this) 'get-year) 1900)))
+
Index: testsuite/obj-test.scm
===================================================================
RCS file: /cvs/kawa/kawa/testsuite/obj-test.scm,v
retrieving revision 1.34
diff -u -r1.34 obj-test.scm
--- testsuite/obj-test.scm	16 May 2003 18:04:35 -0000	1.34
+++ testsuite/obj-test.scm	8 Jul 2003 19:33:45 -0000
@@ -1,4 +1,4 @@
-(test-init "Objects" 77)
+(test-init "Objects" 78)
 
 ;; Force procedure to be applied without being inlined:
 (define-syntax force-eval
@@ -250,4 +250,9 @@
         )))
 (define main-frame (create-main-frame))
 (test (format #f "this: ~a\n" main-frame) field main-frame 'myField)
+
+(define simple-date (make <SimpleDateTest>))
+(define-namespace date "class:java.util.Date")
+(test (+ 1900 (date:get-year (date:new)))
+      invoke simple-date 'get-year)
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]