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