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.List;
31 import org.objectweb.asm.AnnotationVisitor;
32 import org.objectweb.asm.ClassReader;
33 import org.objectweb.asm.ClassVisitor;
34 import org.objectweb.asm.ClassWriter;
35 import org.objectweb.asm.Label;
36 import org.objectweb.asm.MethodVisitor;
37 import org.objectweb.asm.Opcodes;
38 import org.objectweb.asm.Type;
39 import org.objectweb.asm.signature.SignatureReader;
40 import org.objectweb.asm.signature.SignatureVisitor;
41 import org.objectweb.asm.signature.SignatureWriter;
45 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
47 abstract class JsClassLoader extends ClassLoader {
48 JsClassLoader(ClassLoader parent) {
50 setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
54 protected abstract URL findResource(String name);
57 protected abstract Enumeration<URL> findResources(String name);
60 protected Class<?> findClass(String name) throws ClassNotFoundException {
61 if (name.startsWith("javafx")) {
62 return Class.forName(name);
64 if (name.startsWith("netscape")) {
65 return Class.forName(name);
67 if (name.startsWith("com.sun")) {
68 return Class.forName(name);
70 if (name.equals(JsClassLoader.class.getName())) {
71 return JsClassLoader.class;
73 if (name.equals(Fn.class.getName())) {
76 if (name.equals(Fn.Presenter.class.getName())) {
77 return Fn.Presenter.class;
79 if (name.equals(FnUtils.class.getName())) {
82 URL u = findResource(name.replace('.', '/') + ".class");
84 InputStream is = null;
87 byte[] arr = new byte[is.available()];
89 while (len < arr.length) {
90 int read = is.read(arr, len, arr.length - len);
92 throw new IOException("Can't read " + u);
98 ClassReader cr = new ClassReader(arr) {
99 // to allow us to compile with -profile compact1 on
100 // JDK8 while processing the class as JDK7, the highest
101 // class format asm 4.1 understands to
103 public short readShort(int index) {
104 short s = super.readShort(index);
105 if (index == 6 && s > Opcodes.V1_7) {
111 FindInClass tst = new FindInClass(null);
114 ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
115 FindInClass fic = new FindInClass(w);
117 arr = w.toByteArray();
120 return defineClass(name, arr, 0, arr.length);
122 } catch (IOException ex) {
123 throw new ClassNotFoundException("Can't load " + name, ex);
126 if (is != null) is.close();
127 } catch (IOException ex) {
128 throw new ClassNotFoundException(null, ex);
133 name.equals("org.apidesign.html.boot.spi.Fn") ||
134 name.equals("org.apidesign.html.boot.impl.FnUtils")
136 return Class.forName(name);
139 return super.findClass(name);
142 protected abstract Fn defineFn(String code, String... names);
143 protected abstract void loadScript(Reader code) throws Exception;
145 private final class FindInClass extends ClassVisitor {
149 public FindInClass(ClassVisitor cv) {
150 super(Opcodes.ASM4, cv);
154 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
156 super.visit(version, access, name, signature, superName, interfaces);
160 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
161 if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
162 return new LoadResource();
164 return super.visitAnnotation(desc, visible);
169 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
170 return new FindInMethod(access, name, desc,
171 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
175 private final class FindInMethod extends MethodVisitor {
176 private final String name;
177 private final String desc;
178 private final int access;
179 private List<String> args;
181 private boolean bodyGenerated;
183 public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
184 super(Opcodes.ASM4, mv);
185 this.access = access;
191 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
193 "Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
194 || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
197 return new FindInAnno();
199 return super.visitAnnotation(desc, visible);
202 private void generateJSBody(List<String> args, String body) {
208 public void visitCode() {
215 private boolean generateBody() {
219 bodyGenerated = true;
221 super.visitFieldInsn(
222 Opcodes.GETSTATIC, FindInClass.this.name,
223 "$$fn$$" + name + "_" + found,
224 "Lorg/apidesign/html/boot/spi/Fn;"
226 super.visitInsn(Opcodes.DUP);
227 super.visitMethodInsn(
228 Opcodes.INVOKESTATIC,
229 "org/apidesign/html/boot/impl/FnUtils", "isValid",
230 "(Lorg/apidesign/html/boot/spi/Fn;)Z"
232 Label ifNotNull = new Label();
233 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
236 super.visitInsn(Opcodes.POP);
237 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
238 super.visitLdcInsn(body);
239 super.visitIntInsn(Opcodes.SIPUSH, args.size());
240 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
241 boolean needsVM = false;
242 for (int i = 0; i < args.size(); i++) {
244 String argName = args.get(i);
245 needsVM = "vm".equals(argName);
246 super.visitInsn(Opcodes.DUP);
247 super.visitIntInsn(Opcodes.BIPUSH, i);
248 super.visitLdcInsn(argName);
249 super.visitInsn(Opcodes.AASTORE);
251 super.visitMethodInsn(Opcodes.INVOKESTATIC,
252 "org/apidesign/html/boot/impl/FnUtils", "define",
253 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
255 super.visitInsn(Opcodes.DUP);
256 super.visitFieldInsn(
257 Opcodes.PUTSTATIC, FindInClass.this.name,
258 "$$fn$$" + name + "_" + found,
259 "Lorg/apidesign/html/boot/spi/Fn;"
263 super.visitLabel(ifNotNull);
266 if ((access & Opcodes.ACC_STATIC) == 0) {
268 super.visitIntInsn(Opcodes.ALOAD, 0);
271 super.visitInsn(Opcodes.ACONST_NULL);
274 super.visitIntInsn(Opcodes.SIPUSH, args.size());
275 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
277 class SV extends SignatureVisitor {
278 private boolean nowReturn;
279 private Type returnType;
281 private int loadIndex = offset;
288 public void visitBaseType(char descriptor) {
289 final Type t = Type.getType("" + descriptor);
294 FindInMethod.super.visitInsn(Opcodes.DUP);
295 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
296 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
298 switch (descriptor) {
299 case 'I': factory = "java/lang/Integer"; break;
300 case 'J': factory = "java/lang/Long"; loadIndex++; break;
301 case 'S': factory = "java/lang/Short"; break;
302 case 'F': factory = "java/lang/Float"; break;
303 case 'D': factory = "java/lang/Double"; loadIndex++; break;
304 case 'Z': factory = "java/lang/Boolean"; break;
305 case 'C': factory = "java/lang/Character"; break;
306 case 'B': factory = "java/lang/Byte"; break;
307 default: throw new IllegalStateException(t.toString());
309 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
310 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
312 FindInMethod.super.visitInsn(Opcodes.AASTORE);
316 public SignatureVisitor visitArrayType() {
318 throw new IllegalStateException("Not supported yet");
321 return new SignatureWriter();
325 public void visitClassType(String name) {
327 returnType = Type.getObjectType(name);
334 public SignatureVisitor visitReturnType() {
339 private void loadObject() {
340 FindInMethod.super.visitInsn(Opcodes.DUP);
341 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
342 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
343 FindInMethod.super.visitInsn(Opcodes.AASTORE);
348 SignatureReader sr = new SignatureReader(desc);
352 FindInMethod.super.visitInsn(Opcodes.DUP);
353 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
354 int lastSlash = FindInClass.this.name.lastIndexOf('/');
355 String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
356 FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
357 FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
358 FindInMethod.super.visitInsn(Opcodes.AASTORE);
361 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
362 "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
364 switch (sv.returnType.getSort()) {
366 super.visitInsn(Opcodes.RETURN);
370 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
371 super.visitInsn(Opcodes.ARETURN);
374 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
375 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
376 "java/lang/Boolean", "booleanValue", "()Z"
378 super.visitInsn(Opcodes.IRETURN);
381 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
382 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
383 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
385 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
391 public void visitEnd() {
394 if (generateBody()) {
396 super.visitMaxs(1, 0);
398 FindInClass.this.visitField(
399 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
400 "$$fn$$" + name + "_" + found,
401 "Lorg/apidesign/html/boot/spi/Fn;",
411 private final class FindInAnno extends AnnotationVisitor {
412 private List<String> args = new ArrayList<String>();
414 private boolean javacall = false;
416 public FindInAnno() {
421 public void visit(String name, Object value) {
423 args.add((String) value);
426 if (name.equals("javacall")) { // NOI18N
427 javacall = (Boolean)value;
430 assert name.equals("body");
431 body = (String) value;
435 public AnnotationVisitor visitArray(String name) {
440 public void visitEnd() {
443 body = FnUtils.callback(body);
446 generateJSBody(args, body);
452 private final class LoadResource extends AnnotationVisitor {
453 public LoadResource() {
458 public void visit(String attrName, Object value) {
459 String relPath = (String) value;
460 if (relPath.startsWith("/")) {
461 FnUtils.loadScript(JsClassLoader.this, relPath);
463 int last = name.lastIndexOf('/');
464 String fullPath = name.substring(0, last + 1) + relPath;
465 FnUtils.loadScript(JsClassLoader.this, fullPath);
471 private class ClassWriterEx extends ClassWriter {
473 public ClassWriterEx(ClassReader classReader, int flags) {
474 super(classReader, flags);
478 protected String getCommonSuperClass(final String type1, final String type2) {
480 ClassLoader classLoader = JsClassLoader.this;
482 c = Class.forName(type1.replace('/', '.'), false, classLoader);
483 d = Class.forName(type2.replace('/', '.'), false, classLoader);
484 } catch (Exception e) {
485 throw new RuntimeException(e.toString());
487 if (c.isAssignableFrom(d)) {
490 if (d.isAssignableFrom(c)) {
493 if (c.isInterface() || d.isInterface()) {
494 return "java/lang/Object";
497 c = c.getSuperclass();
498 } while (!c.isAssignableFrom(d));
499 return c.getName().replace('.', '/');