jaroslav@1170: /** jaroslav@1170: * Back 2 Browser Bytecode Translator jaroslav@1170: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1170: * jaroslav@1170: * This program is free software: you can redistribute it and/or modify jaroslav@1170: * it under the terms of the GNU General Public License as published by jaroslav@1170: * the Free Software Foundation, version 2 of the License. jaroslav@1170: * jaroslav@1170: * This program is distributed in the hope that it will be useful, jaroslav@1170: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1170: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1170: * GNU General Public License for more details. jaroslav@1170: * jaroslav@1170: * You should have received a copy of the GNU General Public License jaroslav@1170: * along with this program. Look for COPYING file in the top folder. jaroslav@1170: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1170: */ jaroslav@1170: package org.apidesign.bck2brwsr.launcher.fximpl; jaroslav@1170: jaroslav@1170: import java.io.IOException; jaroslav@1170: import java.io.InputStream; jaroslav@1170: import java.net.URL; jaroslav@1170: import java.net.URLClassLoader; jaroslav@1170: import java.util.ArrayList; jaroslav@1170: import java.util.Arrays; jaroslav@1170: import java.util.List; jaroslav@1170: import org.objectweb.asm.AnnotationVisitor; jaroslav@1170: import org.objectweb.asm.ClassReader; jaroslav@1170: import org.objectweb.asm.ClassVisitor; jaroslav@1170: import org.objectweb.asm.ClassWriter; jaroslav@1170: import org.objectweb.asm.Label; jaroslav@1170: import org.objectweb.asm.MethodVisitor; jaroslav@1170: import org.objectweb.asm.Opcodes; jaroslav@1170: import org.objectweb.asm.Type; jaroslav@1170: jaroslav@1170: /** jaroslav@1170: * jaroslav@1170: * @author Jaroslav Tulach jaroslav@1170: */ jaroslav@1170: public abstract class JsClassLoader extends URLClassLoader { jaroslav@1170: JsClassLoader(URL[] urls, ClassLoader parent) { jaroslav@1170: super(urls, parent); jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: protected Class findClass(String name) throws ClassNotFoundException { jaroslav@1170: URL u = findResource(name.replace('.', '/') + ".class"); jaroslav@1170: if (u != null) { jaroslav@1170: InputStream is = null; jaroslav@1170: try { jaroslav@1170: is = u.openStream(); jaroslav@1170: ClassReader cr = new ClassReader(is); jaroslav@1170: ClassWriter w = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); jaroslav@1170: FindInClass fic = new FindInClass(w); jaroslav@1170: cr.accept(fic, 0); jaroslav@1170: byte[] arr = w.toByteArray(); jaroslav@1170: return defineClass(name, arr, 0, arr.length); jaroslav@1170: } catch (IOException ex) { jaroslav@1170: throw new ClassNotFoundException("Can't load " + name, ex); jaroslav@1170: } finally { jaroslav@1170: try { jaroslav@1170: if (is != null) is.close(); jaroslav@1170: } catch (IOException ex) { jaroslav@1170: throw new ClassNotFoundException(null, ex); jaroslav@1170: } jaroslav@1170: } jaroslav@1170: } jaroslav@1170: if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.JsClassLoader")) { jaroslav@1170: return Class.forName(name); jaroslav@1170: } jaroslav@1170: jaroslav@1170: return super.findClass(name); jaroslav@1170: } jaroslav@1170: jaroslav@1170: public final Fn define(String code, String... names) { jaroslav@1170: return defineFn(code, names); jaroslav@1170: } jaroslav@1170: jaroslav@1170: jaroslav@1170: protected abstract Fn defineFn(String code, String... names); jaroslav@1170: jaroslav@1170: public static abstract class Fn { jaroslav@1170: public abstract Object invoke(Object... args) throws Exception; jaroslav@1170: } jaroslav@1170: jaroslav@1170: jaroslav@1170: private static final class FindInClass extends ClassVisitor { jaroslav@1170: private String name; jaroslav@1170: jaroslav@1170: public FindInClass(ClassVisitor cv) { jaroslav@1170: super(Opcodes.ASM4, cv); jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { jaroslav@1170: this.name = name; jaroslav@1170: super.visit(version, access, name, signature, superName, interfaces); jaroslav@1170: } jaroslav@1170: jaroslav@1170: jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { jaroslav@1170: return new FindInMethod(name, jaroslav@1170: super.visitMethod(access, name, desc, signature, exceptions) jaroslav@1170: ); jaroslav@1170: } jaroslav@1170: jaroslav@1170: private final class FindInMethod extends MethodVisitor { jaroslav@1170: private final String name; jaroslav@1170: private List args; jaroslav@1170: private String body; jaroslav@1170: jaroslav@1170: public FindInMethod(String name, MethodVisitor mv) { jaroslav@1170: super(Opcodes.ASM4, mv); jaroslav@1170: this.name = name; jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public AnnotationVisitor visitAnnotation(String desc, boolean visible) { jaroslav@1170: if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N jaroslav@1170: return new FindInAnno(); jaroslav@1170: } jaroslav@1170: return super.visitAnnotation(desc, visible); jaroslav@1170: } jaroslav@1170: jaroslav@1170: private void generateJSBody(List args, String body) { jaroslav@1170: this.args = args; jaroslav@1170: this.body = body; jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visitCode() { jaroslav@1170: if (body == null) { jaroslav@1170: return; jaroslav@1170: } jaroslav@1170: jaroslav@1170: super.visitFieldInsn( jaroslav@1170: Opcodes.GETSTATIC, FindInClass.this.name, jaroslav@1170: "$$bck2brwsr$$" + name, jaroslav@1170: "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;" jaroslav@1170: ); jaroslav@1170: super.visitInsn(Opcodes.DUP); jaroslav@1170: Label ifNotNull = new Label(); jaroslav@1170: super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull); jaroslav@1170: jaroslav@1170: // init Fn jaroslav@1170: super.visitInsn(Opcodes.POP); jaroslav@1170: super.visitLdcInsn(Type.getObjectType(FindInClass.this.name)); jaroslav@1170: super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1170: "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;" jaroslav@1170: ); jaroslav@1170: super.visitTypeInsn(Opcodes.CHECKCAST, "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader"); jaroslav@1170: super.visitLdcInsn(body); jaroslav@1170: super.visitIntInsn(Opcodes.SIPUSH, args.size()); jaroslav@1170: super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String"); jaroslav@1170: for (int i = 0; i < args.size(); i++) { jaroslav@1170: String name = args.get(i); jaroslav@1170: super.visitInsn(Opcodes.DUP); jaroslav@1170: super.visitIntInsn(Opcodes.BIPUSH, i); jaroslav@1170: super.visitLdcInsn(name); jaroslav@1170: super.visitInsn(Opcodes.AASTORE); jaroslav@1170: } jaroslav@1170: super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1170: "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader", jaroslav@1170: "define", "(Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;" jaroslav@1170: ); jaroslav@1170: // end of Fn init jaroslav@1170: jaroslav@1170: super.visitLabel(ifNotNull); jaroslav@1170: super.visitIntInsn(Opcodes.SIPUSH, args.size()); jaroslav@1170: super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); jaroslav@1170: super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1170: "org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;" jaroslav@1170: ); jaroslav@1170: super.visitInsn(Opcodes.ARETURN); jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visitEnd() { jaroslav@1170: super.visitEnd(); jaroslav@1170: if (body != null) { jaroslav@1170: FindInClass.this.visitField( jaroslav@1170: Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, jaroslav@1170: "$$bck2brwsr$$" + name, jaroslav@1170: "Lorg/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader$Fn;", jaroslav@1170: null, null jaroslav@1170: ); jaroslav@1170: } jaroslav@1170: } jaroslav@1170: jaroslav@1170: jaroslav@1170: jaroslav@1170: jaroslav@1170: jaroslav@1170: private final class FindInAnno extends AnnotationVisitor { jaroslav@1170: private List args = new ArrayList(); jaroslav@1170: private String body; jaroslav@1170: jaroslav@1170: public FindInAnno() { jaroslav@1170: super(Opcodes.ASM4); jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visit(String name, Object value) { jaroslav@1170: if (name == null) { jaroslav@1170: args.add((String) value); jaroslav@1170: return; jaroslav@1170: } jaroslav@1170: assert name.equals("body"); jaroslav@1170: body = (String) value; jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public AnnotationVisitor visitArray(String name) { jaroslav@1170: return this; jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visitEnd() { jaroslav@1170: if (body != null) { jaroslav@1170: generateJSBody(args, body); jaroslav@1170: } jaroslav@1170: } jaroslav@1170: } jaroslav@1170: } jaroslav@1170: } jaroslav@1170: }