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