
org.apidesign.html.boot.impl.JsClassLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of net.java.html.boot Show documentation
Show all versions of net.java.html.boot Show documentation
Builder to launch your Java/HTML based application.
The newest version!
/**
* HTML via Java(tm) Language Bindings
* Copyright (C) 2013 Jaroslav Tulach
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. apidesign.org
* designates this particular file as subject to the
* "Classpath" exception as provided by apidesign.org
* in the License file that accompanied this code.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
*/
package org.apidesign.html.boot.impl;
import org.apidesign.html.boot.spi.Fn;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.signature.SignatureWriter;
/**
*
* @author Jaroslav Tulach
*/
abstract class JsClassLoader extends ClassLoader {
JsClassLoader(ClassLoader parent) {
super(parent);
setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
}
@Override
protected abstract URL findResource(String name);
@Override
protected abstract Enumeration findResources(String name);
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
if (name.startsWith("javafx")) {
return Class.forName(name);
}
if (name.startsWith("netscape")) {
return Class.forName(name);
}
if (name.startsWith("com.sun")) {
return Class.forName(name);
}
if (name.equals(JsClassLoader.class.getName())) {
return JsClassLoader.class;
}
if (name.equals(Fn.class.getName())) {
return Fn.class;
}
if (name.equals(Fn.Presenter.class.getName())) {
return Fn.Presenter.class;
}
if (name.equals(FnUtils.class.getName())) {
return FnUtils.class;
}
URL u = findResource(name.replace('.', '/') + ".class");
if (u != null) {
InputStream is = null;
try {
is = u.openStream();
byte[] arr = new byte[is.available()];
int len = 0;
while (len < arr.length) {
int read = is.read(arr, len, arr.length - len);
if (read == -1) {
throw new IOException("Can't read " + u);
}
len += read;
}
is.close();
is = null;
ClassReader cr = new ClassReader(arr) {
// to allow us to compile with -profile compact1 on
// JDK8 while processing the class as JDK7, the highest
// class format asm 4.1 understands to
@Override
public short readShort(int index) {
short s = super.readShort(index);
if (index == 6 && s > Opcodes.V1_7) {
return Opcodes.V1_7;
}
return s;
}
};
FindInClass tst = new FindInClass(null);
cr.accept(tst, 0);
if (tst.found > 0) {
ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
FindInClass fic = new FindInClass(w);
cr.accept(fic, 0);
arr = w.toByteArray();
}
if (arr != null) {
return defineClass(name, arr, 0, arr.length);
}
} catch (IOException ex) {
throw new ClassNotFoundException("Can't load " + name, ex);
} finally {
try {
if (is != null) is.close();
} catch (IOException ex) {
throw new ClassNotFoundException(null, ex);
}
}
}
if (
name.equals("org.apidesign.html.boot.spi.Fn") ||
name.equals("org.apidesign.html.boot.impl.FnUtils")
) {
return Class.forName(name);
}
return super.findClass(name);
}
protected abstract Fn defineFn(String code, String... names);
protected abstract void loadScript(Reader code) throws Exception;
private final class FindInClass extends ClassVisitor {
private String name;
private int found;
public FindInClass(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.name = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
return new LoadResource();
}
return super.visitAnnotation(desc, visible);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new FindInMethod(access, name, desc,
super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
);
}
private final class FindInMethod extends MethodVisitor {
private final String name;
private final String desc;
private final int access;
private List args;
private String body;
private boolean bodyGenerated;
public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
super(Opcodes.ASM4, mv);
this.access = access;
this.name = name;
this.desc = desc;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (
"Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
|| "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
) {
found++;
return new FindInAnno();
}
return super.visitAnnotation(desc, visible);
}
private void generateJSBody(List args, String body) {
this.args = args;
this.body = body;
}
@Override
public void visitCode() {
if (body == null) {
return;
}
generateBody();
}
private boolean generateBody() {
if (bodyGenerated) {
return false;
}
bodyGenerated = true;
super.visitFieldInsn(
Opcodes.GETSTATIC, FindInClass.this.name,
"$$fn$$" + name + "_" + found,
"Lorg/apidesign/html/boot/spi/Fn;"
);
super.visitInsn(Opcodes.DUP);
super.visitMethodInsn(
Opcodes.INVOKESTATIC,
"org/apidesign/html/boot/impl/FnUtils", "isValid",
"(Lorg/apidesign/html/boot/spi/Fn;)Z"
);
Label ifNotNull = new Label();
super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
// init Fn
super.visitInsn(Opcodes.POP);
super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
super.visitLdcInsn(body);
super.visitIntInsn(Opcodes.SIPUSH, args.size());
super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
boolean needsVM = false;
for (int i = 0; i < args.size(); i++) {
assert !needsVM;
String argName = args.get(i);
needsVM = "vm".equals(argName);
super.visitInsn(Opcodes.DUP);
super.visitIntInsn(Opcodes.BIPUSH, i);
super.visitLdcInsn(argName);
super.visitInsn(Opcodes.AASTORE);
}
super.visitMethodInsn(Opcodes.INVOKESTATIC,
"org/apidesign/html/boot/impl/FnUtils", "define",
"(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
);
super.visitInsn(Opcodes.DUP);
super.visitFieldInsn(
Opcodes.PUTSTATIC, FindInClass.this.name,
"$$fn$$" + name + "_" + found,
"Lorg/apidesign/html/boot/spi/Fn;"
);
// end of Fn init
super.visitLabel(ifNotNull);
final int offset;
if ((access & Opcodes.ACC_STATIC) == 0) {
offset = 1;
super.visitIntInsn(Opcodes.ALOAD, 0);
} else {
offset = 0;
super.visitInsn(Opcodes.ACONST_NULL);
}
super.visitIntInsn(Opcodes.SIPUSH, args.size());
super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
class SV extends SignatureVisitor {
private boolean nowReturn;
private Type returnType;
private int index;
private int loadIndex = offset;
public SV() {
super(Opcodes.ASM4);
}
@Override
public void visitBaseType(char descriptor) {
final Type t = Type.getType("" + descriptor);
if (nowReturn) {
returnType = t;
return;
}
FindInMethod.super.visitInsn(Opcodes.DUP);
FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
String factory;
switch (descriptor) {
case 'I': factory = "java/lang/Integer"; break;
case 'J': factory = "java/lang/Long"; loadIndex++; break;
case 'S': factory = "java/lang/Short"; break;
case 'F': factory = "java/lang/Float"; break;
case 'D': factory = "java/lang/Double"; loadIndex++; break;
case 'Z': factory = "java/lang/Boolean"; break;
case 'C': factory = "java/lang/Character"; break;
case 'B': factory = "java/lang/Byte"; break;
default: throw new IllegalStateException(t.toString());
}
FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
);
FindInMethod.super.visitInsn(Opcodes.AASTORE);
}
@Override
public SignatureVisitor visitArrayType() {
if (nowReturn) {
throw new IllegalStateException("Not supported yet");
}
loadObject();
return new SignatureWriter();
}
@Override
public void visitClassType(String name) {
if (nowReturn) {
returnType = Type.getObjectType(name);
return;
}
loadObject();
}
@Override
public SignatureVisitor visitReturnType() {
nowReturn = true;
return this;
}
private void loadObject() {
FindInMethod.super.visitInsn(Opcodes.DUP);
FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
FindInMethod.super.visitInsn(Opcodes.AASTORE);
}
}
SV sv = new SV();
SignatureReader sr = new SignatureReader(desc);
sr.accept(sv);
if (needsVM) {
FindInMethod.super.visitInsn(Opcodes.DUP);
FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
int lastSlash = FindInClass.this.name.lastIndexOf('/');
String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
FindInMethod.super.visitInsn(Opcodes.AASTORE);
}
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
);
switch (sv.returnType.getSort()) {
case Type.VOID:
super.visitInsn(Opcodes.RETURN);
break;
case Type.ARRAY:
case Type.OBJECT:
super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
super.visitInsn(Opcodes.ARETURN);
break;
case Type.BOOLEAN:
super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Boolean", "booleanValue", "()Z"
);
super.visitInsn(Opcodes.IRETURN);
break;
default:
super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
);
super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
}
return true;
}
@Override
public void visitEnd() {
super.visitEnd();
if (body != null) {
if (generateBody()) {
// native method
super.visitMaxs(1, 0);
}
FindInClass.this.visitField(
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
"$$fn$$" + name + "_" + found,
"Lorg/apidesign/html/boot/spi/Fn;",
null, null
);
}
}
private final class FindInAnno extends AnnotationVisitor {
private List args = new ArrayList();
private String body;
private boolean javacall = false;
public FindInAnno() {
super(Opcodes.ASM4);
}
@Override
public void visit(String name, Object value) {
if (name == null) {
args.add((String) value);
return;
}
if (name.equals("javacall")) { // NOI18N
javacall = (Boolean)value;
return;
}
assert name.equals("body");
body = (String) value;
}
@Override
public AnnotationVisitor visitArray(String name) {
return this;
}
@Override
public void visitEnd() {
if (body != null) {
if (javacall) {
body = FnUtils.callback(body);
args.add("vm");
}
generateJSBody(args, body);
}
}
}
}
private final class LoadResource extends AnnotationVisitor {
public LoadResource() {
super(Opcodes.ASM4);
}
@Override
public void visit(String attrName, Object value) {
String relPath = (String) value;
if (relPath.startsWith("/")) {
FnUtils.loadScript(JsClassLoader.this, relPath);
} else {
int last = name.lastIndexOf('/');
String fullPath = name.substring(0, last + 1) + relPath;
FnUtils.loadScript(JsClassLoader.this, fullPath);
}
}
}
}
private class ClassWriterEx extends ClassWriter {
public ClassWriterEx(ClassReader classReader, int flags) {
super(classReader, flags);
}
@Override
protected String getCommonSuperClass(final String type1, final String type2) {
Class> c, d;
ClassLoader classLoader = JsClassLoader.this;
try {
c = Class.forName(type1.replace('/', '.'), false, classLoader);
d = Class.forName(type2.replace('/', '.'), false, classLoader);
} catch (Exception e) {
throw new RuntimeException(e.toString());
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy