launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 17 Jun 2013 16:17:46 +0200
branchclassloader
changeset 1178 4ae766848ce0
parent 1177 006617ca6707
child 1179 2fee889b9830
permissions -rw-r--r--
Allow overloaded @JavaScriptBody methods
     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.launcher.fximpl;
    19 
    20 import java.io.IOException;
    21 import java.io.InputStream;
    22 import java.net.URL;
    23 import java.util.ArrayList;
    24 import java.util.Enumeration;
    25 import java.util.List;
    26 import org.objectweb.asm.AnnotationVisitor;
    27 import org.objectweb.asm.ClassReader;
    28 import org.objectweb.asm.ClassVisitor;
    29 import org.objectweb.asm.ClassWriter;
    30 import org.objectweb.asm.Label;
    31 import org.objectweb.asm.MethodVisitor;
    32 import org.objectweb.asm.Opcodes;
    33 import org.objectweb.asm.Type;
    34 import org.objectweb.asm.signature.SignatureReader;
    35 import org.objectweb.asm.signature.SignatureVisitor;
    36 
    37 /** 
    38  *
    39  * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    40  */
    41 abstract class JsClassLoader extends ClassLoader {
    42     JsClassLoader(ClassLoader parent) {
    43         super(parent);
    44     }
    45     
    46     @Override
    47     protected abstract URL findResource(String name);
    48     
    49     @Override
    50     protected abstract Enumeration<URL> findResources(String name);
    51 
    52     @Override
    53     protected Class<?> findClass(String name) throws ClassNotFoundException {
    54         URL u = findResource(name.replace('.', '/') + ".class");
    55         if (u != null) {
    56             InputStream is = null;
    57             try {
    58                 is = u.openStream();
    59                 byte[] arr = new byte[is.available()];
    60                 int len = 0;
    61                 while (len < arr.length) {
    62                     int read = is.read(arr, len, arr.length - len);
    63                     if (read == -1) {
    64                         throw new IOException("Can't read " + u);
    65                     }
    66                     len += read;
    67                 }
    68                 is.close();
    69                 is = null;
    70                 ClassReader cr = new ClassReader(arr);
    71                 FindInClass tst = new FindInClass(null);
    72                 cr.accept(tst, 0);
    73                 if (tst.found > 0) {
    74                     ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    75                     FindInClass fic = new FindInClass(w);
    76                     cr.accept(fic, 0);
    77                     arr = w.toByteArray();
    78                 }
    79                 if (arr != null) {
    80                     return defineClass(name, arr, 0, arr.length);
    81                 }
    82             } catch (IOException ex) {
    83                 throw new ClassNotFoundException("Can't load " + name, ex);
    84             } finally {
    85                 try {
    86                     if (is != null) is.close();
    87                 } catch (IOException ex) {
    88                     throw new ClassNotFoundException(null, ex);
    89                 }
    90             }
    91         }
    92         if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.Fn")) {
    93             return Class.forName(name);
    94         }
    95         
    96         return super.findClass(name);
    97     }
    98     
    99     protected abstract Fn defineFn(String code, String... names);
   100     
   101     
   102     private static final class FindInClass extends ClassVisitor {
   103         private String name;
   104         private int found;
   105         
   106         public FindInClass(ClassVisitor cv) {
   107             super(Opcodes.ASM4, cv);
   108         }
   109 
   110         @Override
   111         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
   112             this.name = name;
   113             super.visit(version, access, name, signature, superName, interfaces);
   114         }
   115         
   116         
   117 
   118         @Override
   119         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
   120             return new FindInMethod(name, desc,
   121                 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
   122             );
   123         }
   124         
   125         private final class FindInMethod extends MethodVisitor {
   126             private final String name;
   127             private final String desc;
   128             private List<String> args;
   129             private String body;
   130             private boolean bodyGenerated;
   131             
   132             public FindInMethod(String name, String desc, MethodVisitor mv) {
   133                 super(Opcodes.ASM4, mv);
   134                 this.name = name;
   135                 this.desc = desc;
   136             }
   137 
   138             @Override
   139             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   140                 if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N
   141                     found++;
   142                     return new FindInAnno();
   143                 }
   144                 return super.visitAnnotation(desc, visible);
   145             }
   146 
   147             private void generateJSBody(List<String> args, String body) {
   148                 this.args = args;
   149                 this.body = body;
   150             }
   151             
   152             @Override
   153             public void visitCode() {
   154                 if (body == null) {
   155                     return;
   156                 } 
   157                 generateBody();
   158             }
   159             
   160             private boolean generateBody() {
   161                 if (bodyGenerated) {
   162                     return false;
   163                 }
   164                 bodyGenerated = true;
   165                 
   166                 super.visitFieldInsn(
   167                     Opcodes.GETSTATIC, FindInClass.this.name, 
   168                     "$$bck2brwsr$$" + name + "_" + found, 
   169                     "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
   170                 );
   171                 super.visitInsn(Opcodes.DUP);
   172                 Label ifNotNull = new Label();
   173                 super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
   174                 
   175                 // init Fn
   176                 super.visitInsn(Opcodes.POP);
   177                 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   178                 super.visitLdcInsn(body);
   179                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   180                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   181                 for (int i = 0; i < args.size(); i++) {
   182                     String name = args.get(i);
   183                     super.visitInsn(Opcodes.DUP);
   184                     super.visitIntInsn(Opcodes.BIPUSH, i);
   185                     super.visitLdcInsn(name);
   186                     super.visitInsn(Opcodes.AASTORE);
   187                 }
   188                 super.visitMethodInsn(Opcodes.INVOKESTATIC, 
   189                     "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "define", 
   190                     "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
   191                 );
   192                 // end of Fn init
   193                 
   194                 super.visitLabel(ifNotNull);
   195                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   196                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   197                 
   198                 class SV extends SignatureVisitor {
   199                     private boolean nowReturn;
   200                     private Type returnType;
   201                     private int index;
   202                     
   203                     public SV() {
   204                         super(Opcodes.ASM4);
   205                     }
   206                     
   207                     @Override
   208                     public void visitBaseType(char descriptor) {
   209                         final Type t = Type.getType("" + descriptor);
   210                         if (nowReturn) {
   211                             returnType = t;
   212                             return;
   213                         }
   214                         FindInMethod.super.visitInsn(Opcodes.DUP);
   215                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
   216                         FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index);
   217                         String factory;
   218                         switch (descriptor) {
   219                         case 'I': factory = "java/lang/Integer"; break;
   220                         case 'J': factory = "java/lang/Long"; break;
   221                         case 'S': factory = "java/lang/Short"; break;
   222                         case 'F': factory = "java/lang/Float"; break;
   223                         case 'D': factory = "java/lang/Double"; break;
   224                         case 'Z': factory = "java/lang/Boolean"; break;
   225                         case 'C': factory = "java/lang/Character"; break;
   226                         case 'B': factory = "java/lang/Byte"; break;
   227                         default: throw new IllegalStateException(t.toString());
   228                         }
   229                         FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
   230                             factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
   231                         );
   232                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   233                         index++;
   234                     }
   235 
   236                     @Override
   237                     public void visitClassType(String name) {
   238                         if (nowReturn) {
   239                             returnType = Type.getObjectType(name);
   240                             return;
   241                         }
   242                         FindInMethod.super.visitInsn(Opcodes.DUP);
   243                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
   244                         FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index);
   245                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   246                         index++;
   247                     }
   248 
   249                     @Override
   250                     public SignatureVisitor visitReturnType() {
   251                         nowReturn = true;
   252                         return this;
   253                     }
   254                     
   255                     
   256                 }
   257                 SV sv = new SV();
   258                 SignatureReader sr = new SignatureReader(desc);
   259                 sr.accept(sv);
   260                 
   261                 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   262                     "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"
   263                 );
   264                 switch (sv.returnType.getSort()) {
   265                 case Type.VOID: 
   266                     super.visitInsn(Opcodes.RETURN);
   267                     break;
   268                 case Type.ARRAY:
   269                 case Type.OBJECT:
   270                     super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
   271                     super.visitInsn(Opcodes.ARETURN);
   272                     break;
   273                 default:
   274                     super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
   275                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   276                         "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
   277                     );
   278                     super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
   279                 }
   280                 return true;
   281             }
   282 
   283             @Override
   284             public void visitEnd() {
   285                 super.visitEnd();
   286                 if (body != null) {
   287                     if (generateBody()) {
   288                         // native method
   289                         super.visitMaxs(1, 0);
   290                     }
   291                     FindInClass.this.visitField(
   292                         Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 
   293                         "$$bck2brwsr$$" + name + "_" + found, 
   294                         "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;", 
   295                         null, null
   296                     );
   297                 }
   298             }
   299             
   300             
   301             
   302             
   303         
   304             private final class FindInAnno extends AnnotationVisitor {
   305                 private List<String> args = new ArrayList<String>();
   306                 private String body;
   307 
   308                 public FindInAnno() {
   309                     super(Opcodes.ASM4);
   310                 }
   311 
   312                 @Override
   313                 public void visit(String name, Object value) {
   314                     if (name == null) {
   315                         args.add((String) value);
   316                         return;
   317                     }
   318                     assert name.equals("body");
   319                     body = (String) value;
   320                 }
   321 
   322                 @Override
   323                 public AnnotationVisitor visitArray(String name) {
   324                     return this;
   325                 }
   326 
   327                 @Override
   328                 public void visitEnd() {
   329                     if (body != null) {
   330                         generateJSBody(args, body);
   331                     }
   332                 }
   333             }
   334         }
   335     }
   336     
   337     private class ClassWriterEx extends ClassWriter {
   338 
   339         public ClassWriterEx(ClassReader classReader, int flags) {
   340             super(classReader, flags);
   341         }
   342         
   343         @Override
   344         protected String getCommonSuperClass(final String type1, final String type2) {
   345             Class<?> c, d;
   346             ClassLoader classLoader = JsClassLoader.this;
   347             try {
   348                 c = Class.forName(type1.replace('/', '.'), false, classLoader);
   349                 d = Class.forName(type2.replace('/', '.'), false, classLoader);
   350             } catch (Exception e) {
   351                 throw new RuntimeException(e.toString());
   352             }
   353             if (c.isAssignableFrom(d)) {
   354                 return type1;
   355             }
   356             if (d.isAssignableFrom(c)) {
   357                 return type2;
   358             }
   359             if (c.isInterface() || d.isInterface()) {
   360                 return "java/lang/Object";
   361             } else {
   362                 do {
   363                     c = c.getSuperclass();
   364                 } while (!c.isAssignableFrom(d));
   365                 return c.getName().replace('.', '/');
   366             }
   367         }        
   368     }
   369 }