This is the mail archive of the
kawa@sources.redhat.com
mailing list for the Kawa project.
invoke-special patch
- From: Chris Dean <Chris dot Dean at sokitomi dot com>
- To: Kawa List <kawa at sources dot redhat dot com>
- Date: Tue, 08 Jul 2003 12:44:17 -0700
- Subject: 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)