launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
branchclassloader
changeset 1170 ebedd84fba80
child 1171 9753524d698f
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java	Mon Jun 10 18:12:38 2013 +0200
     1.3 @@ -0,0 +1,228 @@
     1.4 +/**
     1.5 + * Back 2 Browser Bytecode Translator
     1.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     1.7 + *
     1.8 + * This program is free software: you can redistribute it and/or modify
     1.9 + * it under the terms of the GNU General Public License as published by
    1.10 + * the Free Software Foundation, version 2 of the License.
    1.11 + *
    1.12 + * This program is distributed in the hope that it will be useful,
    1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 + * GNU General Public License for more details.
    1.16 + *
    1.17 + * You should have received a copy of the GNU General Public License
    1.18 + * along with this program. Look for COPYING file in the top folder.
    1.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    1.20 + */
    1.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
    1.22 +
    1.23 +import java.io.IOException;
    1.24 +import java.io.InputStream;
    1.25 +import java.net.URL;
    1.26 +import java.net.URLClassLoader;
    1.27 +import java.util.ArrayList;
    1.28 +import java.util.Arrays;
    1.29 +import java.util.List;
    1.30 +import org.objectweb.asm.AnnotationVisitor;
    1.31 +import org.objectweb.asm.ClassReader;
    1.32 +import org.objectweb.asm.ClassVisitor;
    1.33 +import org.objectweb.asm.ClassWriter;
    1.34 +import org.objectweb.asm.Label;
    1.35 +import org.objectweb.asm.MethodVisitor;
    1.36 +import org.objectweb.asm.Opcodes;
    1.37 +import org.objectweb.asm.Type;
    1.38 +
    1.39 +/** 
    1.40 + *
    1.41 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    1.42 + */
    1.43 +public abstract class JsClassLoader extends URLClassLoader {
    1.44 +    JsClassLoader(URL[] urls, ClassLoader parent) {
    1.45 +        super(urls, parent);
    1.46 +    }
    1.47 +
    1.48 +    @Override
    1.49 +    protected Class<?> findClass(String name) throws ClassNotFoundException {
    1.50 +        URL u = findResource(name.replace('.', '/') + ".class");
    1.51 +        if (u != null) {
    1.52 +            InputStream is = null;
    1.53 +            try {
    1.54 +                is = u.openStream();
    1.55 +                ClassReader cr = new ClassReader(is);
    1.56 +                ClassWriter w = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    1.57 +                FindInClass fic = new FindInClass(w);
    1.58 +                cr.accept(fic, 0);
    1.59 +                byte[] arr = w.toByteArray();
    1.60 +                return defineClass(name, arr, 0, arr.length);
    1.61 +            } catch (IOException ex) {
    1.62 +                throw new ClassNotFoundException("Can't load " + name, ex);
    1.63 +            } finally {
    1.64 +                try {
    1.65 +                    if (is != null) is.close();
    1.66 +                } catch (IOException ex) {
    1.67 +                    throw new ClassNotFoundException(null, ex);
    1.68 +                }
    1.69 +            }
    1.70 +        }
    1.71 +        if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.JsClassLoader")) {
    1.72 +            return Class.forName(name);
    1.73 +        }
    1.74 +        
    1.75 +        return super.findClass(name);
    1.76 +    }
    1.77 +    
    1.78 +    public final Fn define(String code, String... names) {
    1.79 +        return defineFn(code, names);
    1.80 +    }
    1.81 +    
    1.82 +
    1.83 +    protected abstract Fn defineFn(String code, String... names);
    1.84 +    
    1.85 +    public static abstract class Fn {
    1.86 +        public abstract Object invoke(Object... args) throws Exception;
    1.87 +    }
    1.88 +    
    1.89 +    
    1.90 +    private static final class FindInClass extends ClassVisitor {
    1.91 +        private String name;
    1.92 +        
    1.93 +        public FindInClass(ClassVisitor cv) {
    1.94 +            super(Opcodes.ASM4, cv);
    1.95 +        }
    1.96 +
    1.97 +        @Override
    1.98 +        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    1.99 +            this.name = name;
   1.100 +            super.visit(version, access, name, signature, superName, interfaces);
   1.101 +        }
   1.102 +        
   1.103 +        
   1.104 +
   1.105 +        @Override
   1.106 +        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
   1.107 +            return new FindInMethod(name,
   1.108 +                super.visitMethod(access, name, desc, signature, exceptions)
   1.109 +            );
   1.110 +        }
   1.111 +        
   1.112 +        private final class FindInMethod extends MethodVisitor {
   1.113 +            private final String name;
   1.114 +            private List<String> args;
   1.115 +            private String body;
   1.116 +            
   1.117 +            public FindInMethod(String name, MethodVisitor mv) {
   1.118 +                super(Opcodes.ASM4, mv);
   1.119 +                this.name = name;
   1.120 +            }
   1.121 +
   1.122 +            @Override
   1.123 +            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   1.124 +                if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N
   1.125 +                    return new FindInAnno();
   1.126 +                }
   1.127 +                return super.visitAnnotation(desc, visible);
   1.128 +            }
   1.129 +
   1.130 +            private void generateJSBody(List<String> args, String body) {
   1.131 +                this.args = args;
   1.132 +                this.body = body;
   1.133 +            }
   1.134 +            
   1.135 +            @Override
   1.136 +            public void visitCode() {
   1.137 +                if (body == null) {
   1.138 +                    return;
   1.139 +                } 
   1.140 +                
   1.141 +                super.visitFieldInsn(
   1.142 +                    Opcodes.GETSTATIC, FindInClass.this.name, 
   1.143 +                    "$$bck2brwsr$$" + name, 
   1.144 +                    "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;"
   1.145 +                );
   1.146 +                super.visitInsn(Opcodes.DUP);
   1.147 +                Label ifNotNull = new Label();
   1.148 +                super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
   1.149 +                
   1.150 +                // init Fn
   1.151 +                super.visitInsn(Opcodes.POP);
   1.152 +                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   1.153 +                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   1.154 +                    "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;"
   1.155 +                );
   1.156 +                super.visitTypeInsn(Opcodes.CHECKCAST, "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader");
   1.157 +                super.visitLdcInsn(body);
   1.158 +                super.visitIntInsn(Opcodes.SIPUSH, args.size());
   1.159 +                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   1.160 +                for (int i = 0; i < args.size(); i++) {
   1.161 +                    String name = args.get(i);
   1.162 +                    super.visitInsn(Opcodes.DUP);
   1.163 +                    super.visitIntInsn(Opcodes.BIPUSH, i);
   1.164 +                    super.visitLdcInsn(name);
   1.165 +                    super.visitInsn(Opcodes.AASTORE);
   1.166 +                }
   1.167 +                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   1.168 +                    "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader",
   1.169 +                    "define", "(Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;"
   1.170 +                );
   1.171 +                // end of Fn init
   1.172 +                
   1.173 +                super.visitLabel(ifNotNull);
   1.174 +                super.visitIntInsn(Opcodes.SIPUSH, args.size());
   1.175 +                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   1.176 +                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
   1.177 +                    "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"
   1.178 +                );
   1.179 +                super.visitInsn(Opcodes.ARETURN);
   1.180 +            }
   1.181 +
   1.182 +            @Override
   1.183 +            public void visitEnd() {
   1.184 +                super.visitEnd();
   1.185 +                if (body != null) {
   1.186 +                    FindInClass.this.visitField(
   1.187 +                        Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 
   1.188 +                        "$$bck2brwsr$$" + name, 
   1.189 +                        "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;", 
   1.190 +                        null, null
   1.191 +                    );
   1.192 +                }
   1.193 +            }
   1.194 +            
   1.195 +            
   1.196 +            
   1.197 +            
   1.198 +        
   1.199 +            private final class FindInAnno extends AnnotationVisitor {
   1.200 +                private List<String> args = new ArrayList<String>();
   1.201 +                private String body;
   1.202 +
   1.203 +                public FindInAnno() {
   1.204 +                    super(Opcodes.ASM4);
   1.205 +                }
   1.206 +
   1.207 +                @Override
   1.208 +                public void visit(String name, Object value) {
   1.209 +                    if (name == null) {
   1.210 +                        args.add((String) value);
   1.211 +                        return;
   1.212 +                    }
   1.213 +                    assert name.equals("body");
   1.214 +                    body = (String) value;
   1.215 +                }
   1.216 +
   1.217 +                @Override
   1.218 +                public AnnotationVisitor visitArray(String name) {
   1.219 +                    return this;
   1.220 +                }
   1.221 +
   1.222 +                @Override
   1.223 +                public void visitEnd() {
   1.224 +                    if (body != null) {
   1.225 +                        generateJSBody(args, body);
   1.226 +                    }
   1.227 +                }
   1.228 +            }
   1.229 +        }
   1.230 +    }
   1.231 +}