2 * HTML via Java(tm) Language Bindings
3 * Copyright (C) 2013 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. apidesign.org
13 * designates this particular file as subject to the
14 * "Classpath" exception as provided by apidesign.org
15 * in the License file that accompanied this code.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. Look for COPYING file in the top folder.
19 * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
21 package org.apidesign.html.boot.impl;
23 import org.apidesign.html.boot.spi.Fn;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.Reader;
28 import java.util.ArrayList;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.List;
33 import org.objectweb.asm.AnnotationVisitor;
34 import org.objectweb.asm.ClassReader;
35 import org.objectweb.asm.ClassVisitor;
36 import org.objectweb.asm.ClassWriter;
37 import org.objectweb.asm.Label;
38 import org.objectweb.asm.MethodVisitor;
39 import org.objectweb.asm.Opcodes;
40 import org.objectweb.asm.Type;
41 import org.objectweb.asm.signature.SignatureReader;
42 import org.objectweb.asm.signature.SignatureVisitor;
43 import org.objectweb.asm.signature.SignatureWriter;
47 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
49 abstract class JsClassLoader extends ClassLoader {
50 JsClassLoader(ClassLoader parent) {
52 setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
56 protected abstract URL findResource(String name);
59 protected abstract Enumeration<URL> findResources(String name);
62 protected Class<?> findClass(String name) throws ClassNotFoundException {
63 if (name.startsWith("javafx")) {
64 return Class.forName(name);
66 if (name.startsWith("netscape")) {
67 return Class.forName(name);
69 if (name.startsWith("com.sun")) {
70 return Class.forName(name);
72 if (name.equals(JsClassLoader.class.getName())) {
73 return JsClassLoader.class;
75 if (name.equals(Fn.class.getName())) {
78 if (name.equals(FnUtils.class.getName())) {
81 URL u = findResource(name.replace('.', '/') + ".class");
83 InputStream is = null;
86 byte[] arr = new byte[is.available()];
88 while (len < arr.length) {
89 int read = is.read(arr, len, arr.length - len);
91 throw new IOException("Can't read " + u);
97 ClassReader cr = new ClassReader(arr);
98 FindInClass tst = new FindInClass(null, new HashMap<String,String>());
101 ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
102 FindInClass fic = new FindInClass(w, tst.methods);
104 arr = w.toByteArray();
107 return defineClass(name, arr, 0, arr.length);
109 } catch (IOException ex) {
110 throw new ClassNotFoundException("Can't load " + name, ex);
113 if (is != null) is.close();
114 } catch (IOException ex) {
115 throw new ClassNotFoundException(null, ex);
120 name.equals("org.apidesign.html.boot.spi.Fn") ||
121 name.equals("org.apidesign.html.boot.impl.FnUtils")
123 return Class.forName(name);
126 return super.findClass(name);
129 protected abstract Fn defineFn(String code, String... names);
130 protected abstract void loadScript(Reader code) throws Exception;
132 private final class FindInClass extends ClassVisitor {
135 private final Map<String,String> methods;
137 public FindInClass(ClassVisitor cv, Map<String,String> methods) {
138 super(Opcodes.ASM4, cv);
139 this.methods = methods;
143 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
145 super.visit(version, access, name, signature, superName, interfaces);
149 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
150 if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
151 return new LoadResource();
153 return super.visitAnnotation(desc, visible);
158 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
159 int end = desc.indexOf(')');
160 methods.put(name + desc.substring(0, end + 1), desc);
162 return new FindInMethod(access, name, desc,
163 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
167 private final class FindInMethod extends MethodVisitor {
168 private final String name;
169 private final String desc;
170 private final int access;
171 private List<String> args;
173 private boolean bodyGenerated;
175 public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
176 super(Opcodes.ASM4, mv);
177 this.access = access;
183 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
185 "Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
186 || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
189 return new FindInAnno();
191 return super.visitAnnotation(desc, visible);
194 private void generateJSBody(List<String> args, String body) {
200 public void visitCode() {
207 private boolean generateBody() {
211 bodyGenerated = true;
213 super.visitFieldInsn(
214 Opcodes.GETSTATIC, FindInClass.this.name,
215 "$$fn$$" + name + "_" + found,
216 "Lorg/apidesign/html/boot/spi/Fn;"
218 super.visitInsn(Opcodes.DUP);
219 Label ifNotNull = new Label();
220 super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
223 super.visitInsn(Opcodes.POP);
224 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
225 super.visitLdcInsn(body);
226 super.visitIntInsn(Opcodes.SIPUSH, args.size());
227 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
228 for (int i = 0; i < args.size(); i++) {
229 String name = args.get(i);
230 super.visitInsn(Opcodes.DUP);
231 super.visitIntInsn(Opcodes.BIPUSH, i);
232 super.visitLdcInsn(name);
233 super.visitInsn(Opcodes.AASTORE);
235 super.visitMethodInsn(Opcodes.INVOKESTATIC,
236 "org/apidesign/html/boot/impl/FnUtils", "define",
237 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
241 super.visitLabel(ifNotNull);
244 if ((access & Opcodes.ACC_STATIC) == 0) {
246 super.visitIntInsn(Opcodes.ALOAD, 0);
249 super.visitInsn(Opcodes.ACONST_NULL);
252 super.visitIntInsn(Opcodes.SIPUSH, args.size());
253 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
255 class SV extends SignatureVisitor {
256 private boolean nowReturn;
257 private Type returnType;
265 public void visitBaseType(char descriptor) {
266 final Type t = Type.getType("" + descriptor);
271 FindInMethod.super.visitInsn(Opcodes.DUP);
272 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
273 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index + offset);
275 switch (descriptor) {
276 case 'I': factory = "java/lang/Integer"; break;
277 case 'J': factory = "java/lang/Long"; break;
278 case 'S': factory = "java/lang/Short"; break;
279 case 'F': factory = "java/lang/Float"; break;
280 case 'D': factory = "java/lang/Double"; break;
281 case 'Z': factory = "java/lang/Boolean"; break;
282 case 'C': factory = "java/lang/Character"; break;
283 case 'B': factory = "java/lang/Byte"; break;
284 default: throw new IllegalStateException(t.toString());
286 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
287 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
289 FindInMethod.super.visitInsn(Opcodes.AASTORE);
294 public SignatureVisitor visitArrayType() {
296 throw new IllegalStateException("Not supported yet");
299 return new SignatureWriter();
303 public void visitClassType(String name) {
305 returnType = Type.getObjectType(name);
312 public SignatureVisitor visitReturnType() {
317 private void loadObject() {
318 FindInMethod.super.visitInsn(Opcodes.DUP);
319 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
320 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index + offset);
321 FindInMethod.super.visitInsn(Opcodes.AASTORE);
327 SignatureReader sr = new SignatureReader(desc);
330 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
331 "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
333 switch (sv.returnType.getSort()) {
335 super.visitInsn(Opcodes.RETURN);
339 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
340 super.visitInsn(Opcodes.ARETURN);
343 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
344 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
345 "java/lang/Boolean", "booleanValue", "()Z"
347 super.visitInsn(Opcodes.IRETURN);
350 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
351 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
352 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
354 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
360 public void visitEnd() {
363 if (generateBody()) {
365 super.visitMaxs(1, 0);
367 FindInClass.this.visitField(
368 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
369 "$$fn$$" + name + "_" + found,
370 "Lorg/apidesign/html/boot/spi/Fn;",
380 private final class FindInAnno extends AnnotationVisitor {
381 private List<String> args = new ArrayList<String>();
383 private boolean javacall = false;
385 public FindInAnno() {
390 public void visit(String name, Object value) {
392 args.add((String) value);
395 if (name.equals("javacall")) { // NOI18N
396 javacall = (Boolean)value;
399 assert name.equals("body");
400 body = (String) value;
404 public AnnotationVisitor visitArray(String name) {
409 public void visitEnd() {
411 generateJSBody(args, javacall ?
412 FnUtils.callback(body, JsClassLoader.this, FindInClass.this.name, FindInClass.this.methods) :
420 private final class LoadResource extends AnnotationVisitor {
421 public LoadResource() {
426 public void visit(String attrName, Object value) {
427 String relPath = (String) value;
428 if (relPath.startsWith("/")) {
429 FnUtils.loadScript(JsClassLoader.this, relPath);
431 int last = name.lastIndexOf('/');
432 String fullPath = name.substring(0, last + 1) + relPath;
433 FnUtils.loadScript(JsClassLoader.this, fullPath);
439 private class ClassWriterEx extends ClassWriter {
441 public ClassWriterEx(ClassReader classReader, int flags) {
442 super(classReader, flags);
446 protected String getCommonSuperClass(final String type1, final String type2) {
448 ClassLoader classLoader = JsClassLoader.this;
450 c = Class.forName(type1.replace('/', '.'), false, classLoader);
451 d = Class.forName(type2.replace('/', '.'), false, classLoader);
452 } catch (Exception e) {
453 throw new RuntimeException(e.toString());
455 if (c.isAssignableFrom(d)) {
458 if (d.isAssignableFrom(c)) {
461 if (c.isInterface() || d.isInterface()) {
462 return "java/lang/Object";
465 c = c.getSuperclass();
466 } while (!c.isAssignableFrom(d));
467 return c.getName().replace('.', '/');