launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 12 Jun 2013 18:19:29 +0200
branchclassloader
changeset 1174 f78cdceed17b
parent 1172 c04c43d5fdc6
child 1175 15c6903c8612
permissions -rw-r--r--
Trying to use JsClassLoader in FX launcher
     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.net.URLClassLoader;
    24 import java.util.ArrayList;
    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 Class<?> findClass(String name) throws ClassNotFoundException {
    51         URL u = findResource(name.replace('.', '/') + ".class");
    52         if (u != null) {
    53             InputStream is = null;
    54             try {
    55                 is = u.openStream();
    56                 ClassReader cr = new ClassReader(is);
    57                 ClassWriter w = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    58                 FindInClass fic = new FindInClass(w);
    59                 cr.accept(fic, 0);
    60                 byte[] arr = w.toByteArray();
    61                 return defineClass(name, arr, 0, arr.length);
    62             } catch (IOException ex) {
    63                 throw new ClassNotFoundException("Can't load " + name, ex);
    64             } finally {
    65                 try {
    66                     if (is != null) is.close();
    67                 } catch (IOException ex) {
    68                     throw new ClassNotFoundException(null, ex);
    69                 }
    70             }
    71         }
    72         if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.Fn")) {
    73             return Class.forName(name);
    74         }
    75         
    76         return super.findClass(name);
    77     }
    78     
    79     protected abstract Fn defineFn(String code, String... names);
    80     
    81     
    82     private static final class FindInClass extends ClassVisitor {
    83         private String name;
    84         
    85         public FindInClass(ClassVisitor cv) {
    86             super(Opcodes.ASM4, cv);
    87         }
    88 
    89         @Override
    90         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    91             this.name = name;
    92             super.visit(version, access, name, signature, superName, interfaces);
    93         }
    94         
    95         
    96 
    97         @Override
    98         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    99             return new FindInMethod(name, desc,
   100                 super.visitMethod(access, name, desc, signature, exceptions)
   101             );
   102         }
   103         
   104         private final class FindInMethod extends MethodVisitor {
   105             private final String name;
   106             private final String desc;
   107             private List<String> args;
   108             private String body;
   109             
   110             public FindInMethod(String name, String desc, MethodVisitor mv) {
   111                 super(Opcodes.ASM4, mv);
   112                 this.name = name;
   113                 this.desc = desc;
   114             }
   115 
   116             @Override
   117             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   118                 if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N
   119                     return new FindInAnno();
   120                 }
   121                 return super.visitAnnotation(desc, visible);
   122             }
   123 
   124             private void generateJSBody(List<String> args, String body) {
   125                 this.args = args;
   126                 this.body = body;
   127             }
   128             
   129             @Override
   130             public void visitCode() {
   131                 if (body == null) {
   132                     return;
   133                 } 
   134                 
   135                 super.visitFieldInsn(
   136                     Opcodes.GETSTATIC, FindInClass.this.name, 
   137                     "$$bck2brwsr$$" + name, 
   138                     "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
   139                 );
   140                 super.visitInsn(Opcodes.DUP);
   141                 Label ifNotNull = new Label();
   142                 super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
   143                 
   144                 // init Fn
   145                 super.visitInsn(Opcodes.POP);
   146                 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   147                 super.visitLdcInsn(body);
   148                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   149                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   150                 for (int i = 0; i < args.size(); i++) {
   151                     String name = args.get(i);
   152                     super.visitInsn(Opcodes.DUP);
   153                     super.visitIntInsn(Opcodes.BIPUSH, i);
   154                     super.visitLdcInsn(name);
   155                     super.visitInsn(Opcodes.AASTORE);
   156                 }
   157                 super.visitMethodInsn(Opcodes.INVOKESTATIC, 
   158                     "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "define", 
   159                     "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
   160                 );
   161                 // end of Fn init
   162                 
   163                 super.visitLabel(ifNotNull);
   164                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   165                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   166                 
   167                 class SV extends SignatureVisitor {
   168                     private boolean nowReturn;
   169                     private Type returnType;
   170                     private int index;
   171                     
   172                     public SV() {
   173                         super(Opcodes.ASM4);
   174                     }
   175                     
   176                     @Override
   177                     public void visitBaseType(char descriptor) {
   178                         final Type t = Type.getType("" + descriptor);
   179                         if (nowReturn) {
   180                             returnType = t;
   181                             return;
   182                         }
   183                         FindInMethod.super.visitInsn(Opcodes.DUP);
   184                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
   185                         FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index);
   186                         String factory;
   187                         switch (descriptor) {
   188                         case 'I': factory = "java/lang/Integer"; break;
   189                         case 'J': factory = "java/lang/Long"; break;
   190                         case 'S': factory = "java/lang/Short"; break;
   191                         case 'F': factory = "java/lang/Float"; break;
   192                         case 'D': factory = "java/lang/Double"; break;
   193                         case 'Z': factory = "java/lang/Boolean"; break;
   194                         case 'C': factory = "java/lang/Character"; break;
   195                         case 'B': factory = "java/lang/Byte"; break;
   196                         default: throw new IllegalStateException(t.toString());
   197                         }
   198                         FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
   199                             factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
   200                         );
   201                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   202                         index++;
   203                     }
   204 
   205                     @Override
   206                     public void visitClassType(String name) {
   207                         if (nowReturn) {
   208                             returnType = Type.getObjectType(name);
   209                             return;
   210                         }
   211                         FindInMethod.super.visitInsn(Opcodes.DUP);
   212                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
   213                         FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index);
   214                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   215                         index++;
   216                     }
   217 
   218                     @Override
   219                     public SignatureVisitor visitReturnType() {
   220                         nowReturn = true;
   221                         return this;
   222                     }
   223                     
   224                     
   225                 }
   226                 SV sv = new SV();
   227                 SignatureReader sr = new SignatureReader(desc);
   228                 sr.accept(sv);
   229                 
   230                 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   231                     "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"
   232                 );
   233                 switch (sv.returnType.getSort()) {
   234                 case Type.VOID: 
   235                     super.visitInsn(Opcodes.RETURN);
   236                     return;
   237                 case Type.ARRAY:
   238                 case Type.OBJECT:
   239                     super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
   240                     super.visitInsn(Opcodes.ARETURN);
   241                     return;
   242                 default:
   243                     super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
   244                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   245                         "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
   246                     );
   247                     super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
   248                 }
   249             }
   250 
   251             @Override
   252             public void visitEnd() {
   253                 super.visitEnd();
   254                 if (body != null) {
   255                     FindInClass.this.visitField(
   256                         Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 
   257                         "$$bck2brwsr$$" + name, 
   258                         "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;", 
   259                         null, null
   260                     );
   261                 }
   262             }
   263             
   264             
   265             
   266             
   267         
   268             private final class FindInAnno extends AnnotationVisitor {
   269                 private List<String> args = new ArrayList<String>();
   270                 private String body;
   271 
   272                 public FindInAnno() {
   273                     super(Opcodes.ASM4);
   274                 }
   275 
   276                 @Override
   277                 public void visit(String name, Object value) {
   278                     if (name == null) {
   279                         args.add((String) value);
   280                         return;
   281                     }
   282                     assert name.equals("body");
   283                     body = (String) value;
   284                 }
   285 
   286                 @Override
   287                 public AnnotationVisitor visitArray(String name) {
   288                     return this;
   289                 }
   290 
   291                 @Override
   292                 public void visitEnd() {
   293                     if (body != null) {
   294                         generateJSBody(args, body);
   295                     }
   296                 }
   297             }
   298         }
   299     }
   300 }