rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 22 May 2014 15:29:40 +0200
branchclosure
changeset 1586 d4ee65642d8d
parent 1321 7a78a84ab583
child 1636 eb97a082741b
permissions -rw-r--r--
Tests pass OK in full obfuscation mode
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.emul.reflect;
    19 
    20 import java.lang.reflect.Array;
    21 import java.lang.reflect.Constructor;
    22 import java.lang.reflect.Method;
    23 import java.util.Enumeration;
    24 import org.apidesign.bck2brwsr.core.Exported;
    25 import org.apidesign.bck2brwsr.core.JavaScriptBody;
    26 
    27 /** Utilities to work on methods.
    28  *
    29  * @author Jaroslav Tulach <jtulach@netbeans.org>
    30  */
    31 public abstract class MethodImpl {
    32     public static MethodImpl INSTANCE;
    33     static {
    34         try {
    35             Class.forName(Method.class.getName());
    36         } catch (ClassNotFoundException ex) {
    37             throw new IllegalStateException(ex);
    38         }
    39     }
    40 
    41     protected abstract Method create(Class<?> declaringClass, String name, Object data, String sig);
    42     protected abstract Constructor create(Class<?> declaringClass, Object data, String sig);
    43     
    44     
    45     //
    46     // bck2brwsr implementation
    47     //
    48 
    49     @JavaScriptBody(args = {"clazz", "prefix", "cnstr"},
    50         body = ""
    51         + "var c = clazz.cnstr;\n"
    52         + "if (!cnstr) c = c.prototype;"
    53         + "var arr = new Array();\n"
    54         + "for (m in c) {\n"
    55         + "  if (m.indexOf(prefix) === 0) {\n"
    56         + "     if (!c[m].cls) continue;\n"
    57         + "     arr.push(m);\n"
    58         + "     arr.push(c[m]);\n"
    59         + "     arr.push(c[m].cls.$class);\n"
    60         + "  }"
    61         + "}\n"
    62         + "return arr;")
    63     private static native Object[] findMethodData(
    64         Class<?> clazz, String prefix, boolean cnstr);
    65 
    66     public static Constructor findConstructor(
    67         Class<?> clazz, Class<?>... parameterTypes) {
    68         Object[] data = findMethodData(clazz, "cons__", true);
    69         BIG: for (int i = 0; i < data.length; i += 3) {
    70             String sig = ((String) data[i]).substring(6);
    71             Class<?> cls = (Class<?>) data[i + 2];
    72             Constructor tmp = INSTANCE.create(cls, data[i + 1], sig);
    73             Class<?>[] tmpParms = tmp.getParameterTypes();
    74             if (parameterTypes.length != tmpParms.length) {
    75                 continue;
    76             }
    77             for (int j = 0; j < tmpParms.length; j++) {
    78                 if (!parameterTypes[j].equals(tmpParms[j])) {
    79                     continue BIG;
    80                 }
    81             }
    82             return tmp;
    83         }
    84         return null;
    85     }
    86 
    87     public static Constructor[] findConstructors(Class<?> clazz, int mask) {
    88         Object[] namesAndData = findMethodData(clazz, "", true);
    89         int cnt = 0;
    90         for (int i = 0; i < namesAndData.length; i += 3) {
    91             String sig = (String) namesAndData[i];
    92             Object data = namesAndData[i + 1];
    93             if (!sig.startsWith("cons__")) {
    94                 continue;
    95             }
    96             sig = sig.substring(6);
    97             Class<?> cls = (Class<?>) namesAndData[i + 2];
    98             final Constructor m = INSTANCE.create(cls, data, sig);
    99             if ((m.getModifiers() & mask) == 0) {
   100                 continue;
   101             }
   102             namesAndData[cnt++] = m;
   103         }
   104         Constructor[] arr = new Constructor[cnt];
   105         for (int i = 0; i < cnt; i++) {
   106             arr[i] = (Constructor) namesAndData[i];
   107         }
   108         return arr;
   109     }
   110     public static Method findMethod(
   111         Class<?> clazz, String name, Class<?>... parameterTypes) {
   112         Object[] data = findMethodData(clazz, name + "__", false);
   113         BIG: for (int i = 0; i < data.length; i += 3) {
   114             String sig = ((String) data[i]).substring(name.length() + 2);
   115             Class<?> cls = (Class<?>) data[i + 2];
   116             Method tmp = INSTANCE.create(cls, name, data[i + 1], sig);
   117             Class<?>[] tmpParms = tmp.getParameterTypes();
   118             if (parameterTypes.length != tmpParms.length) {
   119                 continue;
   120             }
   121             for (int j = 0; j < tmpParms.length; j++) {
   122                 if (!parameterTypes[j].equals(tmpParms[j])) {
   123                     continue BIG;
   124                 }
   125             }
   126             return tmp;
   127         }
   128         return null;
   129     }
   130     
   131     public static Method[] findMethods(Class<?> clazz, int mask) {
   132         Object[] namesAndData = findMethodData(clazz, "", false);
   133         int cnt = 0;
   134         for (int i = 0; i < namesAndData.length; i += 3) {
   135             String sig = (String) namesAndData[i];
   136             Object data = namesAndData[i + 1];
   137             int middle = sig.indexOf("__");
   138             if (middle == -1) {
   139                 continue;
   140             }
   141             if (sig.startsWith("$") && sig.endsWith("$")) {
   142                 // produced by Closure compiler in debug mode
   143                 // needs to be ignored
   144                 continue;
   145             }
   146             String name = sig.substring(0, middle);
   147             sig = sig.substring(middle + 2);
   148             Class<?> cls = (Class<?>) namesAndData[i + 2];
   149             final Method m = INSTANCE.create(cls, name, data, sig);
   150             if ((m.getModifiers() & mask) == 0) {
   151                 continue;
   152             }
   153             namesAndData[cnt++] = m;
   154         }
   155         Method[] arr = new Method[cnt];
   156         for (int i = 0; i < cnt; i++) {
   157             arr[i] = (Method) namesAndData[i];
   158         }
   159         return arr;
   160     }
   161     
   162     @Exported static String toSignature(Method m) {
   163         StringBuilder sb = new StringBuilder();
   164         sb.append(m.getName()).append("__");
   165         appendType(sb, m.getReturnType());
   166         Class<?>[] arr = m.getParameterTypes();
   167         for (int i = 0; i < arr.length; i++) {
   168             appendType(sb, arr[i]);
   169         }
   170         return sb.toString();
   171     }
   172     
   173     private static void appendType(StringBuilder sb, Class<?> type) {
   174         if (type == Integer.TYPE) {
   175             sb.append('I');
   176             return;
   177         }
   178         if (type == Long.TYPE) {
   179             sb.append('J');
   180             return;
   181         }
   182         if (type == Double.TYPE) {
   183             sb.append('D');
   184             return;
   185         }
   186         if (type == Float.TYPE) {
   187             sb.append('F');
   188             return;
   189         }
   190         if (type == Byte.TYPE) {
   191             sb.append('B');
   192             return;
   193         }
   194         if (type == Boolean.TYPE) {
   195             sb.append('Z');
   196             return;
   197         }
   198         if (type == Short.TYPE) {
   199             sb.append('S');
   200             return;
   201         }
   202         if (type == Void.TYPE) {
   203             sb.append('V');
   204             return;
   205         }
   206         if (type == Character.TYPE) {
   207             sb.append('C');
   208             return;
   209         }
   210         if (type.isArray()) {
   211             sb.append("_3");
   212             appendType(sb, type.getComponentType());
   213             return;
   214         }
   215         sb.append('L').append(type.getName().replace('.', '_'));
   216         sb.append("_2");
   217     }
   218 
   219     public static int signatureElements(String sig) {
   220         Enumeration<Class> en = signatureParser(sig);
   221         int cnt = 0;
   222         while (en.hasMoreElements()) {
   223             en.nextElement();
   224             cnt++;
   225         }
   226         return cnt;
   227     }
   228     
   229     public static Enumeration<Class> signatureParser(final String sig) {
   230         class E implements Enumeration<Class> {
   231             int pos;
   232             int len;
   233             
   234             E() {
   235                 len = sig.length();
   236                 while (sig.charAt(len - 1) == '$') {
   237                     len--;
   238                 }
   239             }
   240             
   241             public boolean hasMoreElements() {
   242                 return pos < len;
   243             }
   244 
   245             public Class nextElement() {
   246                 switch (sig.charAt(pos++)) {
   247                     case 'I':
   248                         return Integer.TYPE;
   249                     case 'J':
   250                         return Long.TYPE;
   251                     case 'D':
   252                         return Double.TYPE;
   253                     case 'F':
   254                         return Float.TYPE;
   255                     case 'B':
   256                         return Byte.TYPE;
   257                     case 'Z':
   258                         return Boolean.TYPE;
   259                     case 'S':
   260                         return Short.TYPE;
   261                     case 'V':
   262                         return Void.TYPE;
   263                     case 'C':
   264                         return Character.TYPE;
   265                     case 'L':
   266                         try {
   267                             int up = sig.indexOf("_2", pos);
   268                             String type = sig.substring(pos, up);
   269                             pos = up + 2;
   270                             return Class.forName(type.replace('_', '.'));
   271                         } catch (ClassNotFoundException ex) {
   272                             throw new IllegalStateException(ex);
   273                         }
   274                     case '_': {
   275                         char nch = sig.charAt(pos++);
   276                         assert nch == '3' : "Can't find '3' at " + sig.substring(pos - 1);
   277                         final Class compType = nextElement();
   278                         return Array.newInstance(compType, 0).getClass();
   279                     }
   280                 }
   281                 throw new UnsupportedOperationException(sig + " at " + pos);
   282             }
   283         }
   284         return new E();
   285     }
   286 }