launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 10 Jun 2013 18:12:38 +0200
branchclassloader
changeset 1170 ebedd84fba80
child 1171 9753524d698f
permissions -rw-r--r--
Initial version of ClassLoader that understands @JavaScriptBody
     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.Arrays;
    26 import java.util.List;
    27 import org.objectweb.asm.AnnotationVisitor;
    28 import org.objectweb.asm.ClassReader;
    29 import org.objectweb.asm.ClassVisitor;
    30 import org.objectweb.asm.ClassWriter;
    31 import org.objectweb.asm.Label;
    32 import org.objectweb.asm.MethodVisitor;
    33 import org.objectweb.asm.Opcodes;
    34 import org.objectweb.asm.Type;
    35 
    36 /** 
    37  *
    38  * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    39  */
    40 public abstract class JsClassLoader extends URLClassLoader {
    41     JsClassLoader(URL[] urls, ClassLoader parent) {
    42         super(urls, parent);
    43     }
    44 
    45     @Override
    46     protected Class<?> findClass(String name) throws ClassNotFoundException {
    47         URL u = findResource(name.replace('.', '/') + ".class");
    48         if (u != null) {
    49             InputStream is = null;
    50             try {
    51                 is = u.openStream();
    52                 ClassReader cr = new ClassReader(is);
    53                 ClassWriter w = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    54                 FindInClass fic = new FindInClass(w);
    55                 cr.accept(fic, 0);
    56                 byte[] arr = w.toByteArray();
    57                 return defineClass(name, arr, 0, arr.length);
    58             } catch (IOException ex) {
    59                 throw new ClassNotFoundException("Can't load " + name, ex);
    60             } finally {
    61                 try {
    62                     if (is != null) is.close();
    63                 } catch (IOException ex) {
    64                     throw new ClassNotFoundException(null, ex);
    65                 }
    66             }
    67         }
    68         if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.JsClassLoader")) {
    69             return Class.forName(name);
    70         }
    71         
    72         return super.findClass(name);
    73     }
    74     
    75     public final Fn define(String code, String... names) {
    76         return defineFn(code, names);
    77     }
    78     
    79 
    80     protected abstract Fn defineFn(String code, String... names);
    81     
    82     public static abstract class Fn {
    83         public abstract Object invoke(Object... args) throws Exception;
    84     }
    85     
    86     
    87     private static final class FindInClass extends ClassVisitor {
    88         private String name;
    89         
    90         public FindInClass(ClassVisitor cv) {
    91             super(Opcodes.ASM4, cv);
    92         }
    93 
    94         @Override
    95         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    96             this.name = name;
    97             super.visit(version, access, name, signature, superName, interfaces);
    98         }
    99         
   100         
   101 
   102         @Override
   103         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
   104             return new FindInMethod(name,
   105                 super.visitMethod(access, name, desc, signature, exceptions)
   106             );
   107         }
   108         
   109         private final class FindInMethod extends MethodVisitor {
   110             private final String name;
   111             private List<String> args;
   112             private String body;
   113             
   114             public FindInMethod(String name, MethodVisitor mv) {
   115                 super(Opcodes.ASM4, mv);
   116                 this.name = name;
   117             }
   118 
   119             @Override
   120             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   121                 if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N
   122                     return new FindInAnno();
   123                 }
   124                 return super.visitAnnotation(desc, visible);
   125             }
   126 
   127             private void generateJSBody(List<String> args, String body) {
   128                 this.args = args;
   129                 this.body = body;
   130             }
   131             
   132             @Override
   133             public void visitCode() {
   134                 if (body == null) {
   135                     return;
   136                 } 
   137                 
   138                 super.visitFieldInsn(
   139                     Opcodes.GETSTATIC, FindInClass.this.name, 
   140                     "$$bck2brwsr$$" + name, 
   141                     "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;"
   142                 );
   143                 super.visitInsn(Opcodes.DUP);
   144                 Label ifNotNull = new Label();
   145                 super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
   146                 
   147                 // init Fn
   148                 super.visitInsn(Opcodes.POP);
   149                 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   150                 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   151                     "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;"
   152                 );
   153                 super.visitTypeInsn(Opcodes.CHECKCAST, "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader");
   154                 super.visitLdcInsn(body);
   155                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   156                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   157                 for (int i = 0; i < args.size(); i++) {
   158                     String name = args.get(i);
   159                     super.visitInsn(Opcodes.DUP);
   160                     super.visitIntInsn(Opcodes.BIPUSH, i);
   161                     super.visitLdcInsn(name);
   162                     super.visitInsn(Opcodes.AASTORE);
   163                 }
   164                 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   165                     "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader",
   166                     "define", "(Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;"
   167                 );
   168                 // end of Fn init
   169                 
   170                 super.visitLabel(ifNotNull);
   171                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   172                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   173                 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   174                     "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"
   175                 );
   176                 super.visitInsn(Opcodes.ARETURN);
   177             }
   178 
   179             @Override
   180             public void visitEnd() {
   181                 super.visitEnd();
   182                 if (body != null) {
   183                     FindInClass.this.visitField(
   184                         Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 
   185                         "$$bck2brwsr$$" + name, 
   186                         "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;", 
   187                         null, null
   188                     );
   189                 }
   190             }
   191             
   192             
   193             
   194             
   195         
   196             private final class FindInAnno extends AnnotationVisitor {
   197                 private List<String> args = new ArrayList<String>();
   198                 private String body;
   199 
   200                 public FindInAnno() {
   201                     super(Opcodes.ASM4);
   202                 }
   203 
   204                 @Override
   205                 public void visit(String name, Object value) {
   206                     if (name == null) {
   207                         args.add((String) value);
   208                         return;
   209                     }
   210                     assert name.equals("body");
   211                     body = (String) value;
   212                 }
   213 
   214                 @Override
   215                 public AnnotationVisitor visitArray(String name) {
   216                     return this;
   217                 }
   218 
   219                 @Override
   220                 public void visitEnd() {
   221                     if (body != null) {
   222                         generateJSBody(args, body);
   223                     }
   224                 }
   225             }
   226         }
   227     }
   228 }