launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JsClassLoader.java
2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. Look for COPYING file in the top folder.
16 * If not, see http://opensource.org/licenses/GPL-2.0.
18 package org.apidesign.bck2brwsr.launcher.fximpl;
20 import java.io.IOException;
21 import java.io.InputStream;
23 import java.util.ArrayList;
24 import java.util.Enumeration;
25 import java.util.List;
26 import org.objectweb.asm.AnnotationVisitor;
27 import org.objectweb.asm.ClassReader;
28 import org.objectweb.asm.ClassVisitor;
29 import org.objectweb.asm.ClassWriter;
30 import org.objectweb.asm.Label;
31 import org.objectweb.asm.MethodVisitor;
32 import org.objectweb.asm.Opcodes;
33 import org.objectweb.asm.Type;
34 import org.objectweb.asm.signature.SignatureReader;
35 import org.objectweb.asm.signature.SignatureVisitor;
39 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
41 abstract class JsClassLoader extends ClassLoader {
42 JsClassLoader(ClassLoader parent) {
47 protected abstract URL findResource(String name);
50 protected abstract Enumeration<URL> findResources(String name);
53 protected Class<?> findClass(String name) throws ClassNotFoundException {
54 if (name.startsWith("javafx")) {
55 return Class.forName(name);
57 if (name.startsWith("netscape")) {
58 return Class.forName(name);
60 if (name.startsWith("com.sun")) {
61 return Class.forName(name);
63 if (name.equals(JsClassLoader.class.getName())) {
64 return JsClassLoader.class;
66 if (name.equals(Fn.class.getName())) {
69 URL u = findResource(name.replace('.', '/') + ".class");
71 InputStream is = null;
74 byte[] arr = new byte[is.available()];
76 while (len < arr.length) {
77 int read = is.read(arr, len, arr.length - len);
79 throw new IOException("Can't read " + u);
85 ClassReader cr = new ClassReader(arr);
86 FindInClass tst = new FindInClass(null);
89 ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
90 FindInClass fic = new FindInClass(w);
92 arr = w.toByteArray();
95 return defineClass(name, arr, 0, arr.length);
97 } catch (IOException ex) {
98 throw new ClassNotFoundException("Can't load " + name, ex);
101 if (is != null) is.close();
102 } catch (IOException ex) {
103 throw new ClassNotFoundException(null, ex);
107 if (name.startsWith("org.apidesign.bck2brwsr.launcher.fximpl.Fn")) {
108 return Class.forName(name);
111 return super.findClass(name);
114 protected abstract Fn defineFn(String code, String... names);
117 private static final class FindInClass extends ClassVisitor {
121 public FindInClass(ClassVisitor cv) {
122 super(Opcodes.ASM4, cv);
126 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
128 super.visit(version, access, name, signature, superName, interfaces);
134 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
135 return new FindInMethod(name, desc,
136 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
140 private final class FindInMethod extends MethodVisitor {
141 private final String name;
142 private final String desc;
143 private List<String> args;
145 private boolean bodyGenerated;
147 public FindInMethod(String name, String desc, MethodVisitor mv) {
148 super(Opcodes.ASM4, mv);
154 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
155 if ("Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc)) { // NOI18N
157 return new FindInAnno();
159 return super.visitAnnotation(desc, visible);
162 private void generateJSBody(List<String> args, String body) {
168 public void visitCode() {
175 private boolean generateBody() {
179 bodyGenerated = true;
181 super.visitFieldInsn(
182 Opcodes.GETSTATIC, FindInClass.this.name,
183 "$$bck2brwsr$$" + name + "_" + found,
184 "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
186 super.visitInsn(Opcodes.DUP);
187 Label ifNotNull = new Label();
188 super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
191 super.visitInsn(Opcodes.POP);
192 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
193 super.visitLdcInsn(body);
194 super.visitIntInsn(Opcodes.SIPUSH, args.size());
195 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
196 for (int i = 0; i < args.size(); i++) {
197 String name = args.get(i);
198 super.visitInsn(Opcodes.DUP);
199 super.visitIntInsn(Opcodes.BIPUSH, i);
200 super.visitLdcInsn(name);
201 super.visitInsn(Opcodes.AASTORE);
203 super.visitMethodInsn(Opcodes.INVOKESTATIC,
204 "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "define",
205 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;"
209 super.visitLabel(ifNotNull);
210 super.visitIntInsn(Opcodes.SIPUSH, args.size());
211 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
213 class SV extends SignatureVisitor {
214 private boolean nowReturn;
215 private Type returnType;
223 public void visitBaseType(char descriptor) {
224 final Type t = Type.getType("" + descriptor);
229 FindInMethod.super.visitInsn(Opcodes.DUP);
230 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
231 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index);
233 switch (descriptor) {
234 case 'I': factory = "java/lang/Integer"; break;
235 case 'J': factory = "java/lang/Long"; break;
236 case 'S': factory = "java/lang/Short"; break;
237 case 'F': factory = "java/lang/Float"; break;
238 case 'D': factory = "java/lang/Double"; break;
239 case 'Z': factory = "java/lang/Boolean"; break;
240 case 'C': factory = "java/lang/Character"; break;
241 case 'B': factory = "java/lang/Byte"; break;
242 default: throw new IllegalStateException(t.toString());
244 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
245 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
247 FindInMethod.super.visitInsn(Opcodes.AASTORE);
252 public void visitClassType(String name) {
254 returnType = Type.getObjectType(name);
257 FindInMethod.super.visitInsn(Opcodes.DUP);
258 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
259 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index);
260 FindInMethod.super.visitInsn(Opcodes.AASTORE);
265 public SignatureVisitor visitReturnType() {
273 SignatureReader sr = new SignatureReader(desc);
276 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
277 "org/apidesign/bck2brwsr/launcher/fximpl/Fn", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"
279 switch (sv.returnType.getSort()) {
281 super.visitInsn(Opcodes.RETURN);
285 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
286 super.visitInsn(Opcodes.ARETURN);
289 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
290 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
291 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
293 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
299 public void visitEnd() {
302 if (generateBody()) {
304 super.visitMaxs(1, 0);
306 FindInClass.this.visitField(
307 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
308 "$$bck2brwsr$$" + name + "_" + found,
309 "Lorg/apidesign/bck2brwsr/launcher/fximpl/Fn;",
319 private final class FindInAnno extends AnnotationVisitor {
320 private List<String> args = new ArrayList<String>();
323 public FindInAnno() {
328 public void visit(String name, Object value) {
330 args.add((String) value);
333 assert name.equals("body");
334 body = (String) value;
338 public AnnotationVisitor visitArray(String name) {
343 public void visitEnd() {
345 generateJSBody(args, body);
352 private class ClassWriterEx extends ClassWriter {
354 public ClassWriterEx(ClassReader classReader, int flags) {
355 super(classReader, flags);
359 protected String getCommonSuperClass(final String type1, final String type2) {
361 ClassLoader classLoader = JsClassLoader.this;
363 c = Class.forName(type1.replace('/', '.'), false, classLoader);
364 d = Class.forName(type2.replace('/', '.'), false, classLoader);
365 } catch (Exception e) {
366 throw new RuntimeException(e.toString());
368 if (c.isAssignableFrom(d)) {
371 if (d.isAssignableFrom(c)) {
374 if (c.isInterface() || d.isInterface()) {
375 return "java/lang/Object";
378 c = c.getSuperclass();
379 } while (!c.isAssignableFrom(d));
380 return c.getName().replace('.', '/');