launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
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 +}