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