Can invoke methods with parameters via reflection
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 08 Jan 2013 16:01:25 +0100
changeset 418c0bbf144c2c6
parent 416 b2464b3fd015
child 419 73479ebdcf5d
Can invoke methods with parameters via reflection
emul/src/main/java/java/lang/reflect/Method.java
emul/src/main/java/org/apidesign/bck2brwsr/emul/MethodImpl.java
vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java
vm/src/test/java/org/apidesign/vm4brwsr/Classes.java
vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java
vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java
     1.1 --- a/emul/src/main/java/java/lang/reflect/Method.java	Mon Jan 07 18:40:20 2013 +0100
     1.2 +++ b/emul/src/main/java/java/lang/reflect/Method.java	Tue Jan 08 16:01:25 2013 +0100
     1.3 @@ -26,6 +26,7 @@
     1.4  package java.lang.reflect;
     1.5  
     1.6  import java.lang.annotation.Annotation;
     1.7 +import java.util.Enumeration;
     1.8  import org.apidesign.bck2brwsr.core.JavaScriptBody;
     1.9  import org.apidesign.bck2brwsr.emul.AnnotationImpl;
    1.10  import org.apidesign.bck2brwsr.emul.MethodImpl;
    1.11 @@ -140,24 +141,7 @@
    1.12       * @return the return type for the method this object represents
    1.13       */
    1.14      public Class<?> getReturnType() {
    1.15 -        switch (sig.charAt(0)) {
    1.16 -            case 'I': return Integer.TYPE;
    1.17 -            case 'J': return Long.TYPE;
    1.18 -            case 'D': return Double.TYPE;
    1.19 -            case 'F': return Float.TYPE;
    1.20 -            case 'B': return Byte.TYPE;
    1.21 -            case 'Z': return Boolean.TYPE;
    1.22 -            case 'S': return Short.TYPE;
    1.23 -            case 'V': return Void.TYPE;
    1.24 -            case 'L': try {
    1.25 -                int up = sig.indexOf("_2");
    1.26 -                String type = sig.substring(1, up);
    1.27 -                return Class.forName(type);
    1.28 -            } catch (ClassNotFoundException ex) {
    1.29 -                // should not happen
    1.30 -            }
    1.31 -        }
    1.32 -        throw new UnsupportedOperationException(sig);
    1.33 +        return MethodImpl.signatureParser(sig).nextElement();
    1.34      }
    1.35  
    1.36      /**
    1.37 @@ -199,8 +183,13 @@
    1.38       * represents
    1.39       */
    1.40      public Class<?>[] getParameterTypes() {
    1.41 -        throw new UnsupportedOperationException();
    1.42 -        //return (Class<?>[]) parameterTypes.clone();
    1.43 +        Class[] arr = new Class[MethodImpl.signatureElements(sig) - 1];
    1.44 +        Enumeration<Class> en = MethodImpl.signatureParser(sig);
    1.45 +        en.nextElement(); // return type
    1.46 +        for (int i = 0; i < arr.length; i++) {
    1.47 +            arr[i] = en.nextElement();
    1.48 +        }
    1.49 +        return arr;
    1.50      }
    1.51  
    1.52      /**
    1.53 @@ -512,21 +501,41 @@
    1.54          throws IllegalAccessException, IllegalArgumentException,
    1.55             InvocationTargetException
    1.56      {
    1.57 -        if ((getModifiers() & Modifier.STATIC) == 0 && obj == null) {
    1.58 +        final boolean isStatic = (getModifiers() & Modifier.STATIC) == 0;
    1.59 +        if (isStatic && obj == null) {
    1.60              throw new NullPointerException();
    1.61          }
    1.62 -        Object res = invoke0(this, obj, args);
    1.63 +        Class[] types = getParameterTypes();
    1.64 +        if (types.length != args.length) {
    1.65 +            throw new IllegalArgumentException("Types len " + types.length + " args: " + args.length);
    1.66 +        } else {
    1.67 +            args = args.clone();
    1.68 +            for (int i = 0; i < types.length; i++) {
    1.69 +                Class c = types[i];
    1.70 +                if (c.isPrimitive()) {
    1.71 +                    args[i] = toPrimitive(c, args[i]);
    1.72 +                }
    1.73 +            }
    1.74 +        }
    1.75 +        Object res = invoke0(isStatic, this, obj, args);
    1.76          if (getReturnType().isPrimitive()) {
    1.77              res = fromPrimitive(getReturnType(), res);
    1.78          }
    1.79          return res;
    1.80      }
    1.81      
    1.82 -    @JavaScriptBody(args = { "method", "self", "args" }, body =
    1.83 -          "if (args.length > 0) throw 'unsupported now';"
    1.84 -        + "return method.fld_data(self);"
    1.85 +    @JavaScriptBody(args = { "st", "method", "self", "args" }, body =
    1.86 +          "var p;\n"
    1.87 +        + "if (st) {\n"
    1.88 +        + "  p = new Array(1);\n"
    1.89 +        + "  p[0] = self;\n"
    1.90 +        + "  p = p.concat(args);\n"
    1.91 +        + "} else {\n"
    1.92 +        + "  p = args;\n"
    1.93 +        + "}\n"
    1.94 +        + "return method.fld_data.apply(self, p);\n"
    1.95      )
    1.96 -    private static native Object invoke0(Method m, Object self, Object[] args);
    1.97 +    private static native Object invoke0(boolean isStatic, Method m, Object self, Object[] args);
    1.98  
    1.99      private static Object fromPrimitive(Class<?> type, Object o) {
   1.100          if (type == Integer.TYPE) {
   1.101 @@ -560,6 +569,39 @@
   1.102          body = "return cls.cnstr(false)[m](o);"
   1.103      )
   1.104      private static native Integer fromRaw(Class<?> cls, String m, Object o);
   1.105 +
   1.106 +    private static Object toPrimitive(Class<?> type, Object o) {
   1.107 +        if (type == Integer.TYPE) {
   1.108 +            return toRaw("intValue__I", o);
   1.109 +        }
   1.110 +        if (type == Long.TYPE) {
   1.111 +            return toRaw("longValue__J", o);
   1.112 +        }
   1.113 +        if (type == Double.TYPE) {
   1.114 +            return toRaw("doubleValue__D", o);
   1.115 +        }
   1.116 +        if (type == Float.TYPE) {
   1.117 +            return toRaw("floatValue__F", o);
   1.118 +        }
   1.119 +        if (type == Byte.TYPE) {
   1.120 +            return toRaw("byteValue__B", o);
   1.121 +        }
   1.122 +        if (type == Boolean.TYPE) {
   1.123 +            return toRaw("booleanValue__Z", o);
   1.124 +        }
   1.125 +        if (type == Short.TYPE) {
   1.126 +            return toRaw("shortValue__S", o);
   1.127 +        }
   1.128 +        if (type.getName().equals("void")) {
   1.129 +            return o;
   1.130 +        }
   1.131 +        throw new IllegalStateException("Can't convert " + o);
   1.132 +    }
   1.133 +    
   1.134 +    @JavaScriptBody(args = { "m", "o" }, 
   1.135 +        body = "return o[m](o);"
   1.136 +    )
   1.137 +    private static native Object toRaw(String m, Object o);
   1.138      
   1.139      /**
   1.140       * Returns {@code true} if this method is a bridge
     2.1 --- a/emul/src/main/java/org/apidesign/bck2brwsr/emul/MethodImpl.java	Mon Jan 07 18:40:20 2013 +0100
     2.2 +++ b/emul/src/main/java/org/apidesign/bck2brwsr/emul/MethodImpl.java	Tue Jan 08 16:01:25 2013 +0100
     2.3 @@ -25,6 +25,7 @@
     2.4  package org.apidesign.bck2brwsr.emul;
     2.5  
     2.6  import java.lang.reflect.Method;
     2.7 +import java.util.Enumeration;
     2.8  import org.apidesign.bck2brwsr.core.JavaScriptBody;
     2.9  
    2.10  /** Utilities to work on methods.
    2.11 @@ -96,5 +97,56 @@
    2.12          }
    2.13          return arr;
    2.14      }
    2.15 +
    2.16 +    public static int signatureElements(String sig) {
    2.17 +        Enumeration<Class> en = signatureParser(sig);
    2.18 +        int cnt = 0;
    2.19 +        while (en.hasMoreElements()) {
    2.20 +            en.nextElement();
    2.21 +            cnt++;
    2.22 +        }
    2.23 +        return cnt;
    2.24 +    }
    2.25      
    2.26 +    public static Enumeration<Class> signatureParser(final String sig) {
    2.27 +        class E implements Enumeration<Class> {
    2.28 +            int pos;
    2.29 +            
    2.30 +            public boolean hasMoreElements() {
    2.31 +                return pos < sig.length();
    2.32 +            }
    2.33 +
    2.34 +            public Class nextElement() {
    2.35 +                switch (sig.charAt(pos++)) {
    2.36 +                    case 'I':
    2.37 +                        return Integer.TYPE;
    2.38 +                    case 'J':
    2.39 +                        return Long.TYPE;
    2.40 +                    case 'D':
    2.41 +                        return Double.TYPE;
    2.42 +                    case 'F':
    2.43 +                        return Float.TYPE;
    2.44 +                    case 'B':
    2.45 +                        return Byte.TYPE;
    2.46 +                    case 'Z':
    2.47 +                        return Boolean.TYPE;
    2.48 +                    case 'S':
    2.49 +                        return Short.TYPE;
    2.50 +                    case 'V':
    2.51 +                        return Void.TYPE;
    2.52 +                    case 'L':
    2.53 +                        try {
    2.54 +                            int up = sig.indexOf("_2");
    2.55 +                            String type = sig.substring(1, up);
    2.56 +                            pos = up + 2;
    2.57 +                            return Class.forName(type);
    2.58 +                        } catch (ClassNotFoundException ex) {
    2.59 +                            // should not happen
    2.60 +                        }
    2.61 +                }
    2.62 +                throw new UnsupportedOperationException(sig + " at " + pos);
    2.63 +            }
    2.64 +        }
    2.65 +        return new E();
    2.66 +    }
    2.67  }
     3.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Mon Jan 07 18:40:20 2013 +0100
     3.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Tue Jan 08 16:01:25 2013 +0100
     3.3 @@ -102,6 +102,11 @@
     3.4              "java.io.IOException", true, "name"
     3.5          );
     3.6      }
     3.7 +    @Test public void jsInvokeParamMethod() throws Exception {
     3.8 +        assertExec("sums two numbers via reflection", Classes.class, 
     3.9 +            "reflectiveSum__III", Double.valueOf(5), 2, 3
    3.10 +        );
    3.11 +    }
    3.12      @Test public void javaFindMethod() throws Exception {
    3.13          assertEquals(Classes.reflectiveMethodCall(false, "name"), "java.io.IOException", "Calls the name() method via reflection");
    3.14      }
     4.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Mon Jan 07 18:40:20 2013 +0100
     4.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Tue Jan 08 16:01:25 2013 +0100
     4.3 @@ -146,4 +146,9 @@
     4.4          }
     4.5          return find.invoke(null);
     4.6      }
     4.7 +    
     4.8 +    public static int reflectiveSum(int a, int b) throws Exception {
     4.9 +        Method m = StaticMethod.class.getMethod("sum", int.class, int.class);
    4.10 +        return (int) m.invoke(null, a, b);
    4.11 +    }
    4.12  }
     5.1 --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java	Mon Jan 07 18:40:20 2013 +0100
     5.2 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java	Tue Jan 08 16:01:25 2013 +0100
     5.3 @@ -77,6 +77,16 @@
     5.4          }
     5.5      }
     5.6      
     5.7 +    @Compare public String paramTypes() throws Exception {
     5.8 +        Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE);
     5.9 +        final Class[] pt = plus.getParameterTypes();
    5.10 +        return pt[0].getName();
    5.11 +    }
    5.12 +    @Compare public int methodWithArgs() throws Exception {
    5.13 +        Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE);
    5.14 +        return (Integer)plus.invoke(null, 2, 3);
    5.15 +    }
    5.16 +    
    5.17      @JavaScriptBody(args = { "arr", "len" }, body="var a = arr.slice(0, len); a.sort(); return a;")
    5.18      private static String[] sort(String[] arr, int len) {
    5.19          List<String> list = Arrays.asList(arr).subList(0, len);
     6.1 --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java	Mon Jan 07 18:40:20 2013 +0100
     6.2 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java	Tue Jan 08 16:01:25 2013 +0100
     6.3 @@ -24,4 +24,8 @@
     6.4      
     6.5      public void instanceMethod() {
     6.6      }
     6.7 +
     6.8 +    public static int plus(int a, int b) {
     6.9 +        return a + b;
    6.10 +    }
    6.11  }