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.util.ArrayList; jaroslav@1175: import java.util.Enumeration; 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@1172: import org.objectweb.asm.signature.SignatureReader; jaroslav@1172: import org.objectweb.asm.signature.SignatureVisitor; jaroslav@1170: jaroslav@1170: /** jaroslav@1170: * jaroslav@1170: * @author Jaroslav Tulach jaroslav@1170: */ jaroslav@1174: abstract class JsClassLoader extends ClassLoader { jaroslav@1174: JsClassLoader(ClassLoader parent) { jaroslav@1174: super(parent); jaroslav@1170: } jaroslav@1174: jaroslav@1174: @Override jaroslav@1174: protected abstract URL findResource(String name); jaroslav@1175: jaroslav@1175: @Override jaroslav@1175: protected abstract Enumeration findResources(String name); jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: protected Class findClass(String name) throws ClassNotFoundException { jaroslav@1179: if (name.startsWith("javafx")) { jaroslav@1179: return Class.forName(name); jaroslav@1179: } jaroslav@1179: if (name.startsWith("netscape")) { jaroslav@1179: return Class.forName(name); jaroslav@1179: } jaroslav@1179: if (name.startsWith("com.sun")) { jaroslav@1179: return Class.forName(name); jaroslav@1179: } jaroslav@1179: if (name.equals(JsClassLoader.class.getName())) { jaroslav@1179: return JsClassLoader.class; jaroslav@1179: } jaroslav@1179: if (name.equals(Fn.class.getName())) { jaroslav@1179: return Fn.class; jaroslav@1179: } 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@1175: byte[] arr = new byte[is.available()]; jaroslav@1177: int len = 0; jaroslav@1177: while (len < arr.length) { jaroslav@1177: int read = is.read(arr, len, arr.length - len); jaroslav@1177: if (read == -1) { jaroslav@1177: throw new IOException("Can't read " + u); jaroslav@1177: } jaroslav@1177: len += read; jaroslav@1175: } jaroslav@1175: is.close(); jaroslav@1175: is = null; jaroslav@1175: ClassReader cr = new ClassReader(arr); jaroslav@1175: FindInClass tst = new FindInClass(null); jaroslav@1175: cr.accept(tst, 0); jaroslav@1178: if (tst.found > 0) { jaroslav@1175: ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); jaroslav@1175: FindInClass fic = new FindInClass(w); jaroslav@1175: cr.accept(fic, 0); jaroslav@1175: arr = w.toByteArray(); jaroslav@1175: } jaroslav@1175: if (arr != null) { jaroslav@1175: return defineClass(name, arr, 0, arr.length); jaroslav@1175: } 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@1171: if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.Fn")) { jaroslav@1170: return Class.forName(name); jaroslav@1170: } jaroslav@1170: jaroslav@1170: return super.findClass(name); jaroslav@1170: } jaroslav@1170: jaroslav@1170: protected abstract Fn defineFn(String code, String... names); jaroslav@1170: jaroslav@1170: jaroslav@1170: private static final class FindInClass extends ClassVisitor { jaroslav@1170: private String name; jaroslav@1178: private int found; 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@1181: return new FindInMethod(access, name, desc, jaroslav@1176: super.visitMethod(access & (~Opcodes.ACC_NATIVE), 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@1172: private final String desc; jaroslav@1181: private final int access; jaroslav@1170: private List args; jaroslav@1170: private String body; jaroslav@1176: private boolean bodyGenerated; jaroslav@1170: jaroslav@1181: public FindInMethod(int access, String name, String desc, MethodVisitor mv) { jaroslav@1170: super(Opcodes.ASM4, mv); jaroslav@1181: this.access = access; jaroslav@1170: this.name = name; jaroslav@1172: this.desc = desc; 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@1178: found++; 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@1176: generateBody(); jaroslav@1176: } jaroslav@1176: jaroslav@1176: private boolean generateBody() { jaroslav@1176: if (bodyGenerated) { jaroslav@1176: return false; jaroslav@1176: } jaroslav@1176: bodyGenerated = true; jaroslav@1170: jaroslav@1170: super.visitFieldInsn( jaroslav@1170: Opcodes.GETSTATIC, FindInClass.this.name, jaroslav@1178: "$$bck2brwsr$$" + name + "_" + found, jaroslav@1171: "Lorg/apidesign/bck2brwsr/launcher/fximpl/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.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@1171: super.visitMethodInsn(Opcodes.INVOKESTATIC, jaroslav@1171: "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "define", jaroslav@1171: "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;" jaroslav@1170: ); jaroslav@1170: // end of Fn init jaroslav@1170: jaroslav@1170: super.visitLabel(ifNotNull); jaroslav@1181: jaroslav@1181: final int offset; jaroslav@1181: if ((access & Opcodes.ACC_STATIC) == 0) { jaroslav@1181: offset = 1; jaroslav@1181: super.visitIntInsn(Opcodes.ALOAD, 0); jaroslav@1181: } else { jaroslav@1181: offset = 0; jaroslav@1181: super.visitInsn(Opcodes.ACONST_NULL); jaroslav@1181: } jaroslav@1181: jaroslav@1170: super.visitIntInsn(Opcodes.SIPUSH, args.size()); jaroslav@1170: super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); jaroslav@1172: jaroslav@1172: class SV extends SignatureVisitor { jaroslav@1172: private boolean nowReturn; jaroslav@1172: private Type returnType; jaroslav@1172: private int index; jaroslav@1172: jaroslav@1172: public SV() { jaroslav@1172: super(Opcodes.ASM4); jaroslav@1172: } jaroslav@1172: jaroslav@1172: @Override jaroslav@1172: public void visitBaseType(char descriptor) { jaroslav@1172: final Type t = Type.getType("" + descriptor); jaroslav@1172: if (nowReturn) { jaroslav@1172: returnType = t; jaroslav@1172: return; jaroslav@1172: } jaroslav@1172: FindInMethod.super.visitInsn(Opcodes.DUP); jaroslav@1172: FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index); jaroslav@1181: FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index + offset); jaroslav@1172: String factory; jaroslav@1172: switch (descriptor) { jaroslav@1172: case 'I': factory = "java/lang/Integer"; break; jaroslav@1172: case 'J': factory = "java/lang/Long"; break; jaroslav@1172: case 'S': factory = "java/lang/Short"; break; jaroslav@1172: case 'F': factory = "java/lang/Float"; break; jaroslav@1172: case 'D': factory = "java/lang/Double"; break; jaroslav@1172: case 'Z': factory = "java/lang/Boolean"; break; jaroslav@1172: case 'C': factory = "java/lang/Character"; break; jaroslav@1172: case 'B': factory = "java/lang/Byte"; break; jaroslav@1172: default: throw new IllegalStateException(t.toString()); jaroslav@1172: } jaroslav@1172: FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC, jaroslav@1172: factory, "valueOf", "(" + descriptor + ")L" + factory + ";" jaroslav@1172: ); jaroslav@1172: FindInMethod.super.visitInsn(Opcodes.AASTORE); jaroslav@1172: index++; jaroslav@1172: } jaroslav@1172: jaroslav@1172: @Override jaroslav@1172: public void visitClassType(String name) { jaroslav@1172: if (nowReturn) { jaroslav@1172: returnType = Type.getObjectType(name); jaroslav@1172: return; jaroslav@1172: } jaroslav@1172: FindInMethod.super.visitInsn(Opcodes.DUP); jaroslav@1172: FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index); jaroslav@1181: FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index + offset); jaroslav@1172: FindInMethod.super.visitInsn(Opcodes.AASTORE); jaroslav@1172: index++; jaroslav@1172: } jaroslav@1172: jaroslav@1172: @Override jaroslav@1172: public SignatureVisitor visitReturnType() { jaroslav@1172: nowReturn = true; jaroslav@1172: return this; jaroslav@1172: } jaroslav@1172: jaroslav@1172: jaroslav@1172: } jaroslav@1172: SV sv = new SV(); jaroslav@1172: SignatureReader sr = new SignatureReader(desc); jaroslav@1172: sr.accept(sv); jaroslav@1172: jaroslav@1170: super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1181: "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" jaroslav@1170: ); jaroslav@1172: switch (sv.returnType.getSort()) { jaroslav@1172: case Type.VOID: jaroslav@1172: super.visitInsn(Opcodes.RETURN); jaroslav@1176: break; jaroslav@1172: case Type.ARRAY: jaroslav@1172: case Type.OBJECT: jaroslav@1172: super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName()); jaroslav@1172: super.visitInsn(Opcodes.ARETURN); jaroslav@1176: break; jaroslav@1172: default: jaroslav@1172: super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number"); jaroslav@1172: super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1172: "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor() jaroslav@1172: ); jaroslav@1172: super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN)); jaroslav@1172: } jaroslav@1176: return true; jaroslav@1170: } jaroslav@1170: jaroslav@1170: @Override jaroslav@1170: public void visitEnd() { jaroslav@1170: super.visitEnd(); jaroslav@1170: if (body != null) { jaroslav@1176: if (generateBody()) { jaroslav@1176: // native method jaroslav@1176: super.visitMaxs(1, 0); jaroslav@1176: } jaroslav@1170: FindInClass.this.visitField( jaroslav@1170: Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, jaroslav@1178: "$$bck2brwsr$$" + name + "_" + found, jaroslav@1171: "Lorg/apidesign/bck2brwsr/launcher/fximpl/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@1175: jaroslav@1175: private class ClassWriterEx extends ClassWriter { jaroslav@1175: jaroslav@1175: public ClassWriterEx(ClassReader classReader, int flags) { jaroslav@1175: super(classReader, flags); jaroslav@1175: } jaroslav@1175: jaroslav@1175: @Override jaroslav@1175: protected String getCommonSuperClass(final String type1, final String type2) { jaroslav@1175: Class c, d; jaroslav@1175: ClassLoader classLoader = JsClassLoader.this; jaroslav@1175: try { jaroslav@1175: c = Class.forName(type1.replace('/', '.'), false, classLoader); jaroslav@1175: d = Class.forName(type2.replace('/', '.'), false, classLoader); jaroslav@1175: } catch (Exception e) { jaroslav@1175: throw new RuntimeException(e.toString()); jaroslav@1175: } jaroslav@1175: if (c.isAssignableFrom(d)) { jaroslav@1175: return type1; jaroslav@1175: } jaroslav@1175: if (d.isAssignableFrom(c)) { jaroslav@1175: return type2; jaroslav@1175: } jaroslav@1175: if (c.isInterface() || d.isInterface()) { jaroslav@1175: return "java/lang/Object"; jaroslav@1175: } else { jaroslav@1175: do { jaroslav@1175: c = c.getSuperclass(); jaroslav@1175: } while (!c.isAssignableFrom(d)); jaroslav@1175: return c.getName().replace('.', '/'); jaroslav@1175: } jaroslav@1175: } jaroslav@1175: } jaroslav@1170: }