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