00 | Previous | Up | Next
Reflecting Java into SchemeKAnderson@bbn.com Timothy J. Hickey, Brandeis University, Waltham, MA
Presented at Reflection '99 Software available at
http://openmap.bbn.com/~kanderso/silk/jlib/
|
01 | Previous | Up | Next
Overview
|
02 | Previous | Up | Next
History of Lisp and reflection at BBN
|
03 | Previous | Up | Next
Quick Review
|
04 | Previous | Up | Next
Java
|
05 | Previous | Up | Next
Java Reflection
|
06 | Previous | Up | Next
Java reflection example
package elf;
import java.lang.reflect.*;
import java.util.Hashtable;
public class Reflect {
public static void main(String[] args) {
try {
// Hashtable ht = new Hashtable(10);
Constructor c = Hashtable.class.getConstructor
(new Class[] { Integer.TYPE });
Hashtable ht = (Hashtable) c.newInstance
(new Object[] { new Integer(10) });
// ht.put("Three", new Integer(3));
Method m = Hashtable.class.getMethod
("put", new Class[] { Object.class, Object.class });
m.invoke(ht, new Object[] { "Three", new Integer(3) });
System.out.println(ht); // Prints: {Three=3}
} catch (Exception e) { e.printStackTrace(); }}}
|
07 | Previous | Up | Next
Scheme
|
08 | Previous | Up | Next
Scheme DatatypesScheme Java boolean Boolean symbol silk.Symbol char Character vector Object[] pair silk.Pair procedure silk.Procedure exact Integer inexact Double string char[] inputport silk.InputPort outputport java.io.PrintWriter |
09 | Previous | Up | Next
The power of Scheme
|
10 | Previous | Up | Next
Silk started out small
|
11 | Previous | Up | Next
Primitive access to Java is easy Try demo D1(constructor CLASSNAME ARGTYPE1 ...) (method METHODNAME CLASSNAME ARGTYPE1 ...) Example using BigInteger package: is 12345678987654321 probably prime? > (define isProbablePrime
(method "isProbablePrime" "java.math.BigInteger"
"int"))
isProbablePrime
> (define BigInteger
(constructor "java.math.BigInteger"
"java.lang.String"))
BigInteger
> (isProbablePrime (BigInteger "12345678987654321") 10)
#f
>
|
12 | Previous | Up | Next
User can define additional access to Java's reflection layer
> (define class (method "forName" "java.lang.Class"
"java.lang.String"))
class
> (define HT (class "java.util.Hashtable"))
HT
> HT
class java.util.Hashtable
> (define get-field
(method "getField" "java.lang.Class" "java.lang.String"))
get-field
> (define get-field-value
(method "get" "java.lang.reflect.Field" "java.lang.Object"))
get-field-value
>(define (get-static-value class field-name)
(get-field-value (get-field class field-name) '())) ; '() is Java null.
get-static-value
> (get-static-value (class "java.lang.Void") "TYPE") ; In Java: Void.TYPE
void
>
|
13 | Previous | Up | Next
But procedures aren't generic enough
|
14 | Previous | Up | Next
Obvious Scheme solutionUse Runtime type checks to choose the right method:
(define get
(let ((hashtable-class (class "java.util.Hashtable"))
(hashtable-get-method
(method "get" "java.util.Hashtable" "java.lang.Object"))
(field-class (class "java.lang.reflect.Field"))
(field-get-method
(method "get" "java.lang.reflect.Field" "java.lang.Object"))
(isInstance
(method "isInstance" "java.lang.Class" "java.lang.Object")))
(lambda (table key)
((cond
((isInstance hashtable-class table) hashtable-get-method)
((isInstance field-class table) field-get-method)
(else (lambda (table key) (error "no method for " table))))
table key))))
Macros can help generate this code automatically. |
15 | Previous | Up | Next
(import) lifts Java classes into SILK wholesale
|
16 | Previous | Up | Next
Example: using Hashtable
> (define ht (Hashtable 20))
ht
> ht
{}
> (put ht 'clone 1)
()
> ht
{clone=1}
> (put ht 'zone 2)
()
> ht
{clone=1, zone=2}
> (get ht 'clone)
1
>
|
17 | Previous | Up | Next
Example continued Try demo D4A generic function can have several methods:
> get
{silk.Generic get[3]}
> (for-each print (methods get))
{silk.InstanceMethod Object Map.get(Object)}
{silk.InstanceMethod Object silk.GlobalEnv.get(Symbol)}
{silk.InstanceMethod Object Field.get(Object)}
#t
>
Here's an example with static methods:
> (import "java.lang.Float") importing java.lang.Float in 81 ms. #t > (Float.parseFloat "17.42") 17.42 > |
18 | Previous | Up | Next
Example using (import) - Newton's method |
19 | Previous | Up | Next
Newton's method code(import "java.lang.Double") (import "java.awt.Color")
(import "java.awt.Button") (import "java.awt.TextField")
(import "jlib.EasyWin")
(define (test1)
;; Construct a test window.
(let ((win (EasyWin "test1.scm" this-interpreter))
(g (TextField "1" 20))
(x (TextField "16" 20))
(go (Button "Iterate")))
(define (action e) ; Define call back.
(setText g
(toString (f (Double.valueOf (getText g))
(Double.valueOf (getText x))))))
(define (f g x) ; Newton's method.
(/ (+ g (/ x g)) 2.0))
(resize win 200 200) ; Size the window.
(add win g) ; Add the components.
(add win x)
(add win go)
(addActionCallback win action)
(setBackground win (Color 200 200 255))
(setVisible win #t)))
(test1) ; Try it out.
|
20 | Previous | Up | Next
Simple generic function protocol
Object
Procedure
apply(Pair, Engine)
Generic
addMethod(GenericMethod)
findMethod(GenericMethod)
computeDiscriminator()
GenericMethod
match(GenericMehod)
isApplicable(Pair)
moreApplicableMethod(GenericMethod)
|
21 | Previous | Up | Next
Simple generic function protocol
public abstract class Procedure extends SchemeUtils {
// Apply the Procedure to a list of arguments.
public abstract Object apply(Pair args, Engine eng);
}
public class Generic extends Procedure {
// Add a method to the generic.
public void addMethod(GenericMethod m);
}
public abstract class GenericMethod extends Procedure {
// Two GenericMethod's match if they have equal lists of parameterTypes.
public boolean match(GenericMethod x);
// Is the method applicable to the list of arguments?
public boolean isApplicable(Pair args);
// Returns the method that is more applicable.
public GenericMethod moreApplicableMethod (GenericMethod m2);
}
|
22 | Previous | Up | Next
>GenericMethod subclassesCurrently, four types of GenericMethod
|
23 | Previous | Up | Next
Choosing the applicable method
;;; After importing java.lang.File and javax.swing.JFrame:
> list#
{silk.Generic list#[7]}
> (for-each print (methods list#))
{InstanceMethod String[] File.list(FilenameFilter)}
{InstanceMethod String[] File.list()} ****
{InstanceMethod void Component.list(PrintStream, int)}
{InstanceMethod void Component.list(PrintWriter, int)}
{InstanceMethod void Component.list()}
{InstanceMethod void Component.list(PrintWriter)}
{InstanceMethod void Component.list(PrintStream)}
|
24 | Previous | Up | Next
Remaining complications
|
25 | Previous | Up | Next
Remaining complications (2)
|
26 | Previous | Up | Next
Use only the most general method
|
27 | Previous | Up | Next
Feasibility studyStatistics from 494 classes reachable from javax.swing.Jframe: Count What 494 classes 458 public classes 52 Exception classes 176 static most general methods 2759 instance most general methods. 2935 total most general methods.There were 134 generic functions that contain only static methods. 93% of them have two or fewer methods:
Count Methods Cumulative % and examples
110 1 82.1%
15 2 93.3%
6 3
1 4 createPackedRaster
1 5 getKeyStroke
1 9 valueOf
|
28 | Previous | Up | Next
Statistics continued
Count Methods Cumulative % and examples
1058 1 69.4%
255 2 86.1%
75 3 91.0%
39 4
24 5
23 6
17 7
10 8
6 9
... ...
1 17 get
1 18 contains
3 20 clone insert print
1 22 println
1 24 remove
1 36 add
|
29 | Previous | Up | Next
A few discriminator states are adequate
|
30 | Previous | Up | Next
Scheme methods Try demo D6(import "java.util.Iterator") (import "java.util.Collection")
(import "java.util.Map") (import "java.util.Vector")
(import "java.util.Hashtable")
(define-method iterate ((items java.util.Iterator) action)
(if (hasNext items)
(begin (action (next items))
(iterate items action))))
(define-method iterate ((items java.util.Collection) action)
(iterate (iterator items) action))
(define-method iterate ((items java.util.Map) action)
(iterate (entrySet items) action))
(define-method iterate ((items silk.Pair) action)
(action (car items))
(let ((items (cdr items)))
(if (pair? items) (iterate items action))))
(define-method iterate ((items java.lang.Object[]) action)
(let loop ((i 0)
(L (vector-length items)))
(if (< i L) (begin (action (vector-ref items i))
(loop (+ i 1) L)))))
|
31 | Previous | Up | Next
Sample usage> (define h (Hashtable 50)) h > (put h 'fred 3) () > (put h 'mary 4) () > (iterate h print) fred=3 mary=4 ()
|
32 | Previous | Up | Next
Meta scripting - creating new code from old
|
33 | Previous | Up | Next
Example: Tracing a method Try demo D7.1 - D7.3To trace a method, Hashtable.put():
> m2
public synchronized Object Hashtable.put(Object, Object)
> (emit (gen-trace-method m2))
public synchronized java.lang.Object
put(java.lang.Object a1, java.lang.Object a0) {
if(trace) Trace.enter(this + ".put(" + a1 + ", " + a0 + "}");
java.lang.Object result = super.put(a1, a0);
if(trace) Trace.exit(result);
return result;
}
#t
|
34 | Previous | Up | Next
Required Scheme code See demo D7.2
(define-method gen-trace-method (m java.lang.reflect.Method)
`(,(gen-method-signature m) { ,(gen-trace-method-body m) }))
(define-method gen-method-signature (m java.lang.reflect.Method)
`(,(Modifier.toString (getModifiers m))
,(method-return-type m)
,(getName m) "("
,(map-args (lambda (arg) `(,(arg-type arg) ,(arg-name arg)))
"," (arg-types m))
")"))
(define-method gen-trace-method-body (m java.lang.reflect.Method)
`(if "(" trace ")"
Trace.enter "(" this + ,(quotify "." (getName m) "(")
,(map-args (lambda (arg) `(+ ,(arg-name arg)))
"+ \", \"" (arg-types m))
+ ,(quotify "}") ")" ";"
,(method-return-type m) result = super.
,(getName m) "("
,(map-args arg-name "," (arg-types m))
")" ";"
if "(" trace ")" "Trace.exit(result)" ";"
return result ";"))
(define-method method-return-type (m java.lang.reflect.Method)
(getName (getReturnType m)))
(define arg-type car) ; Accessors
(define arg-name cadr)
(define (arg-types m) ; method -> ((type name) ...)
(let ((c 0))
(map* (lambda (a) (list (getName a) (make-arg-name (set! c (+ c 1)))))
(getParameterTypes m))))
(define (make-arg-name c) ; (make-arg-name 1) -> "a1"
(string->symbol (string-append "a" (number->string c))))
(define (quotify . args) ; (quotify 'fred 3) -> "\"fred3\""
(let ((double-quote "\""))
(apply string-append double-quote (append args (list double-quote)))))
|
35 | Previous | Up | Next
Compiling Java from SILK(import "java.io.File") (import "java.lang.String")
(import "sun.tools.javac.Main") (import "java.lang.System")
(define (compile-file file)
;; Compile the *.java file, file using the current CLASSPATH.
(let* ((file (if (string? file) file (toString file)))
(as (java-vector String.class
"-deprecation"
"-classpath"
(System.getProperty "java.class.path")
file))
(main (Main (get-static System.class 'out) 'silkc)))
(compile main as)))
To compile the generated file: > (compile-file "elf/TracedHashtable.java") #t > |
36 | Previous | Up | Next
Other Scheme implementations
|
37 | Previous | Up | Next
Examples of the meta helix
|
38 | Previous | Up | Next
Java Paranoid quiz
package elf;
/**
The class Holder holds a secret int value.
Once created, it will not reveal its secret to anyone. Trust Java!
However, isHappy() can be used to check if the secret is above 25.
Holder has other private methods that are of no concern to us.
QUIZ: Can you write a class that can view or alter the secret? **/
public final class Holder {
private int secret;
public Holder(int secret) { this.secret = secret;}
public final boolean isHappy() { return secret > 25; }
private final Object booster() {
return new Object() {
private final void boost() { secret++; }}; }
public static boolean isHolder(Object x) {
return x.getClass() == Holder.class; }
// ...
}
|
39 | Previous | Up | Next
Reflection reveals some additional methods> (describe (class "elf.Holder")) public final class elf.Holder extends Object HashCode: 1491376 ClassLoader: sun.misc.Launcher$AppClassLoader@4d3343 Name: elf.Holder isArray: #f Constructors: public elf.Holder(int) Fields: private int secret static Class class$elf$Holder *** Methods: static int access$0(elf.Holder) *** static void access$1(elf.Holder,int) *** static Class class$(String) *** private final Object booster() public final boolean isHappy() public static boolean isHolder(Object) #f |
40 | Previous | Up | Next
Winner of the quiz
package elf;
public class Sneaky
{
public static void main(String[] args) {
// Create a new Holder with a "secret" value
Holder h = new Holder(42);
// Retrieve secret value and print it
System.out.println("i = " + Holder.access$0(h));
}
}
|
41 | Previous | Up | Next
Describing an object is easy
(define-method describe ((x java.lang.Object))
(show "an instance of " (getName (getClass x)))
(describe-fields x (getClass x)))
(define (describe-fields x superclass)
(let ((fs (getDeclaredFields superclass)))
(AccessibleObject.setAccessible fs #t) ; Make them all accessible.
(for-each* (lambda (f) ; Not static fields.
(if (not (Modifier.isStatic (getModifiers f)))
(describe-field f x)))
fs)
(let ((superclass (getSuperclass superclass)))
(if (not (null? superclass)) (describe-fields x superclass)))))
(define (describe-field f x) (show " " (getName f)": " (get f x)))
(define (show . items)
(apply displays items)
(newline))
(define (displays . items) (for-each display items))
|
42 | Previous | Up | Next
Describe reveals flaws in SILK's reflection> (describe h) an instance of java.util.Hashtable table: #(Fred=3 () () () () () () () () Mary=4) count: 2 threshold: 7 loadFactor: 0.75 modCount: 2 keySet: () entrySet: () values: () () > (describe 'hello) an instance of silk.Symbol ; Not turned into a String. name: hello globalIndex: -1 () > (describe "hello") ; "hello" is really a char[]! an instance of java.lang.String value: hello ; really a char[] offset: 0 count: 5 () |
43 | Previous | Up | Next
I can do that in JavaDo what?
How do you identify the object?
public static void main(String[] args) {
Object x = Magic.parse(args[0]);
Describe.describe(x);
}
You have to write a specialized main() or Magic.parse() for everything. Even an introspective object browser would have limitations if it isn't scriptable. This is why scripting languages are powerful.
|
44 | Previous | Up | Next
The need for scripting languages
|
45 | Previous | Up | Next
Scheme makes lots of little languages easy
|
46 | Previous | Up | Next
Conclusions
|
47 | Previous | Up | Next
OpenMap Memo
|
|
48 | Previous | Up | Next
|