You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2017/09/03 12:48:52 UTC
[19/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial
donation of HTML/Java NetBeans API. Equivalent to the content of
html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip
with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
new file mode 100644
index 0000000..7abae31
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
@@ -0,0 +1,717 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import org.netbeans.html.boot.spi.Fn;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+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;
+
+/** Utilities related to bytecode transformations. Depend on asm.jar which
+ * needs to be added to be provided to classpath to make methods in this
+ * class useful.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class FnUtils {
+
+ private FnUtils() {
+ }
+
+ /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
+ * in the bytecode and converts them into real code. Used by Maven plugin
+ * postprocessing classes.
+ *
+ * @param bytecode the original bytecode with javascript specific annotations
+ * @param loader the loader to load resources (scripts and classes) when needed
+ * @return the transformed bytecode
+ * @since 0.7
+ */
+ public static byte[] transform(byte[] bytecode, ClassLoader loader) {
+ ClassReader cr = new ClassReader(bytecode) {
+ // 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(loader, null);
+ cr.accept(tst, 0);
+ if (tst.found > 0) {
+ ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ FindInClass fic = new FindInClass(loader, w);
+ cr.accept(fic, 0);
+ bytecode = w.toByteArray();
+ }
+ return bytecode;
+ }
+
+ public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
+ return new JsClassLoaderImpl(parent, f, d);
+ }
+
+ static String callback(final String body) {
+ return new JsCallback() {
+ @Override
+ protected CharSequence callMethod(
+ String ident, String fqn, String method, String params
+ ) {
+ StringBuilder sb = new StringBuilder();
+ if (ident != null) {
+ sb.append("vm.raw$");
+ } else {
+ sb.append("vm.");
+ }
+ sb.append(mangle(fqn, method, params));
+ sb.append("(");
+ if (ident != null) {
+ sb.append(ident);
+ }
+ return sb;
+ }
+
+ }.parse(body);
+ }
+
+ private static final class FindInClass extends ClassVisitor {
+ private String name;
+ private int found;
+ private String resource;
+
+ public FindInClass(ClassLoader l, 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) {
+ final AnnotationVisitor del = super.visitAnnotation(desc, visible);
+ if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
+ return new LoadResource(del);
+ }
+ return del;
+ }
+
+ @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)
+ );
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ if (name.startsWith("$$fn$$")) {
+ return null;
+ }
+ return superField(access, name, desc, signature, value);
+ }
+
+ final FieldVisitor superField(int access, String name, String desc, String signature, Object value) {
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ private final class FindInMethod extends MethodVisitor {
+
+ private final String name;
+ private final String desc;
+ private final int access;
+ private FindInAnno fia;
+ 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
+ found++;
+ return new FindInAnno();
+ }
+ return super.visitAnnotation(desc, visible);
+ }
+
+ private void generateJSBody(FindInAnno fia) {
+ this.fia = fia;
+ }
+
+ @Override
+ public void visitCode() {
+ if (fia == null) {
+ return;
+ }
+ generateBody(true);
+ }
+
+ private boolean generateBody(boolean hasCode) {
+ if (bodyGenerated) {
+ return false;
+ }
+ bodyGenerated = true;
+ if (mv != null) {
+ AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
+ AnnotationVisitor varr = va.visitArray("args");
+ for (String argName : fia.args) {
+ varr.visit(null, argName);
+ }
+ varr.visitEnd();
+ va.visit("javacall", fia.javacall);
+ va.visit("body", fia.body);
+ va.visitEnd();
+ }
+
+ String body;
+ List<String> args;
+ if (fia.javacall) {
+ body = callback(fia.body);
+ args = new ArrayList<String>(fia.args);
+ args.add("vm");
+ } else {
+ body = fia.body;
+ args = fia.args;
+ }
+
+ super.visitFieldInsn(
+ Opcodes.GETSTATIC, FindInClass.this.name,
+ "$$fn$$" + name + "_" + found,
+ "Lorg/netbeans/html/boot/spi/Fn;"
+ );
+ super.visitInsn(Opcodes.DUP);
+ super.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ "org/netbeans/html/boot/spi/Fn", "isValid",
+ "(Lorg/netbeans/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.visitInsn(fia.keepAlive ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+ 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/netbeans/html/boot/spi/Fn", "define",
+ "(Ljava/lang/Class;ZLjava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
+ );
+ Label noPresenter = new Label();
+ super.visitInsn(Opcodes.DUP);
+ super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
+ if (resource != null) {
+ super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
+ super.visitLdcInsn(resource);
+ super.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "org/netbeans/html/boot/spi/Fn", "preload",
+ "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
+ );
+ }
+ super.visitInsn(Opcodes.DUP);
+ super.visitFieldInsn(
+ Opcodes.PUTSTATIC, FindInClass.this.name,
+ "$$fn$$" + name + "_" + found,
+ "Lorg/netbeans/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) {
+ return new SignatureVisitor(Opcodes.ASM4) {
+ @Override
+ public void visitClassType(String name) {
+ returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
+ }
+
+ @Override
+ public void visitBaseType(char descriptor) {
+ returnType = Type.getType("[" + descriptor);
+ }
+ };
+ }
+ 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);
+ }
+
+ if (fia.wait4js) {
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "org/netbeans/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: {
+ Label handleNullValue = new Label();
+ super.visitInsn(Opcodes.DUP);
+ super.visitJumpInsn(Opcodes.IFNULL, handleNullValue);
+ super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/lang/Boolean", "booleanValue", "()Z"
+ );
+ super.visitInsn(Opcodes.IRETURN);
+ super.visitLabel(handleNullValue);
+ super.visitInsn(Opcodes.ICONST_0);
+ 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));
+ }
+ } else {
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
+ );
+ super.visitInsn(Opcodes.RETURN);
+ }
+ super.visitLabel(noPresenter);
+ if (hasCode) {
+ super.visitCode();
+ } else {
+ super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
+ super.visitInsn(Opcodes.DUP);
+ super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
+ super.visitMethodInsn(Opcodes.INVOKESPECIAL,
+ "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
+ );
+ this.visitInsn(Opcodes.ATHROW);
+ }
+ return true;
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ if (fia != null) {
+ if (generateBody(false)) {
+ // native method
+ super.visitMaxs(1, 0);
+ }
+ FindInClass.this.superField(
+ Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
+ "$$fn$$" + name + "_" + found,
+ "Lorg/netbeans/html/boot/spi/Fn;",
+ null, null
+ );
+ }
+ }
+
+ private final class FindInAnno extends AnnotationVisitor {
+
+ List<String> args = new ArrayList<String>();
+ String body;
+ boolean javacall = false;
+ boolean wait4js = true;
+ boolean keepAlive = true;
+
+ 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;
+ }
+ if (name.equals("wait4js")) { // NOI18N
+ wait4js = (Boolean) value;
+ return;
+ }
+ if (name.equals("keepAlive")) { // NOI18N
+ keepAlive = (Boolean) value;
+ return;
+ }
+ assert name.equals("body"); // NOI18N
+ body = (String) value;
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return this;
+ }
+
+ @Override
+ public void visitEnd() {
+ if (body != null) {
+ generateJSBody(this);
+ }
+ }
+ }
+ }
+
+ private final class LoadResource extends AnnotationVisitor {
+ public LoadResource(AnnotationVisitor av) {
+ super(Opcodes.ASM4, av);
+ }
+
+ @Override
+ public void visit(String attrName, Object value) {
+ super.visit(attrName, value);
+ String relPath = (String) value;
+ if (relPath.startsWith("/")) {
+ resource = relPath;
+ } else {
+ int last = name.lastIndexOf('/');
+ String fullPath = name.substring(0, last + 1) + relPath;
+ resource = fullPath;
+ }
+ }
+ }
+ }
+
+ private static class ClassWriterEx extends ClassWriter {
+
+ private final ClassLoader loader;
+
+ public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
+ super(classReader, flags);
+ this.loader = l;
+ }
+
+ @Override
+ protected String getCommonSuperClass(final String type1, final String type2) {
+ Class<?> c, d;
+ try {
+ c = Class.forName(type1.replace('/', '.'), false, loader);
+ d = Class.forName(type2.replace('/', '.'), false, loader);
+ } 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('.', '/');
+ }
+ }
+ }
+
+ static class JsClassLoaderImpl extends JsClassLoader {
+
+ private final FindResources f;
+ private final Fn.Presenter d;
+
+ public JsClassLoaderImpl(ClassLoader parent, FindResources f, Fn.Presenter d) {
+ super(parent);
+ setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
+ this.f = f;
+ this.d = d;
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ List<URL> l = res(name, true);
+ return l.isEmpty() ? null : l.get(0);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) {
+ return Collections.enumeration(res(name, false));
+ }
+
+ private List<URL> res(String name, boolean oneIsEnough) {
+ List<URL> l = new ArrayList<URL>();
+ f.findResources(name, l, oneIsEnough);
+ return l;
+ }
+
+ @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.startsWith("org.netbeans.html.context.spi")) {
+ return Class.forName(name);
+ }
+ if (name.startsWith("net.java.html.BrwsrCtx")) {
+ 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(Fn.ToJavaScript.class.getName())) {
+ return Fn.ToJavaScript.class;
+ }
+ if (name.equals(Fn.FromJavaScript.class.getName())) {
+ return Fn.FromJavaScript.class;
+ }
+ if (name.equals(FnUtils.class.getName())) {
+ return FnUtils.class;
+ }
+ if (
+ name.equals("org.netbeans.html.boot.spi.Fn") ||
+ name.equals("org.netbeans.html.boot.impl.FnUtils") ||
+ name.equals("org.netbeans.html.boot.impl.FnContext")
+ ) {
+ return Class.forName(name);
+ }
+ 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;
+ if (JsPkgCache.process(this, name)) {
+ arr = FnUtils.transform(arr, this);
+ }
+ 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);
+ }
+ }
+ }
+ return super.findClass(name);
+ }
+
+ protected Fn defineFn(String code, String... names) {
+ return d.defineFn(code, names);
+ }
+
+ protected void loadScript(Reader code) throws Exception {
+ d.loadScript(code);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
new file mode 100644
index 0000000..ce9c4bb
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
@@ -0,0 +1,528 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.Completions;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = Processor.class)
+public final class JavaScriptProcesor extends AbstractProcessor {
+ private final Map<String,Map<String,ExecutableElement>> javacalls =
+ new HashMap<String,Map<String,ExecutableElement>>();
+ private final Map<String,Set<TypeElement>> bodies =
+ new HashMap<String, Set<TypeElement>>();
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ Set<String> set = new HashSet<String>();
+ set.add(JavaScriptBody.class.getName());
+ set.add(JavaScriptResource.class.getName());
+ return set;
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ final Messager msg = processingEnv.getMessager();
+ for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptBody.class)) {
+ if (e.getKind() != ElementKind.METHOD && e.getKind() != ElementKind.CONSTRUCTOR) {
+ continue;
+ }
+ ExecutableElement ee = (ExecutableElement)e;
+ List<? extends VariableElement> params = ee.getParameters();
+
+ JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class);
+ if (jsb == null) {
+ continue;
+ } else {
+ Set<TypeElement> classes = this.bodies.get(findPkg(e));
+ if (classes == null) {
+ classes = new HashSet<TypeElement>();
+ bodies.put(findPkg(e), classes);
+ }
+ Element t = e.getEnclosingElement();
+ while (!t.getKind().isClass() && !t.getKind().isInterface()) {
+ t = t.getEnclosingElement();
+ }
+ classes.add((TypeElement)t);
+ }
+ String[] arr = jsb.args();
+ if (params.size() != arr.length) {
+ msg.printMessage(Diagnostic.Kind.ERROR, "Number of args arguments does not match real arguments!", e);
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (!params.get(i).getSimpleName().toString().equals(arr[i])) {
+ msg.printMessage(Diagnostic.Kind.WARNING, "Actual method parameter names and args ones " + Arrays.toString(arr) + " differ", e);
+ }
+ }
+ if (!jsb.wait4js() && ee.getReturnType().getKind() != TypeKind.VOID) {
+ msg.printMessage(Diagnostic.Kind.ERROR, "Methods that don't wait for JavaScript to finish must return void!", e);
+ }
+ if (!jsb.javacall() && jsb.body().contains(".@")) {
+ msg.printMessage(Diagnostic.Kind.WARNING, "Usage of .@ usually requires javacall=true", e);
+ }
+ if (jsb.javacall()) {
+ JsCallback verify = new VerifyCallback(e);
+ try {
+ verify.parse(jsb.body());
+ } catch (IllegalStateException ex) {
+ msg.printMessage(Diagnostic.Kind.ERROR, ex.getLocalizedMessage(), e);
+ }
+ }
+ }
+ for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptResource.class)) {
+ JavaScriptResource r = e.getAnnotation(JavaScriptResource.class);
+ if (r == null) {
+ continue;
+ }
+ final String res;
+ if (r.value().startsWith("/")) {
+ res = r.value().substring(1);
+ } else {
+ res = findPkg(e).replace('.', '/') + "/" + r.value();
+ }
+
+ try {
+ FileObject os = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", res);
+ os.openInputStream().close();
+ } catch (IOException ex1) {
+ try {
+ FileObject os2 = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", res);
+ os2.openInputStream().close();
+ } catch (IOException ex2) {
+ try {
+ FileObject os3 = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", res);
+ os3.openInputStream().close();
+ } catch (IOException ex3) {
+ msg.printMessage(Diagnostic.Kind.ERROR, "Cannot find resource " + res, e);
+ }
+ }
+ }
+
+ boolean found = false;
+ for (Element mthod : e.getEnclosedElements()) {
+ if (mthod.getKind() != ElementKind.METHOD) {
+ continue;
+ }
+ if (mthod.getAnnotation(JavaScriptBody.class) != null) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ msg.printMessage(Diagnostic.Kind.ERROR, "At least one method needs @JavaScriptBody annotation. "
+ + "Otherwise it is not guaranteed the resource will ever be loaded,", e
+ );
+ }
+ }
+
+ if (roundEnv.processingOver()) {
+ generateCallbackClass(javacalls);
+ generateJavaScriptBodyList(bodies);
+ javacalls.clear();
+ }
+ return true;
+ }
+
+ @Override
+ public Iterable<? extends Completion> getCompletions(Element e,
+ AnnotationMirror annotation, ExecutableElement member, String userText
+ ) {
+ StringBuilder sb = new StringBuilder();
+ if (e.getKind() == ElementKind.METHOD && member.getSimpleName().contentEquals("args")) {
+ ExecutableElement ee = (ExecutableElement) e;
+ String sep = "";
+ sb.append("{ ");
+ for (VariableElement ve : ee.getParameters()) {
+ sb.append(sep).append('"').append(ve.getSimpleName())
+ .append('"');
+ sep = ", ";
+ }
+ sb.append(" }");
+ return Collections.nCopies(1, Completions.of(sb.toString()));
+ }
+ return null;
+ }
+
+ private class VerifyCallback extends JsCallback {
+ private final Element e;
+ public VerifyCallback(Element e) {
+ this.e = e;
+ }
+
+ @Override
+ protected CharSequence callMethod(String ident, String fqn, String method, String params) {
+ final TypeElement type = processingEnv.getElementUtils().getTypeElement(fqn);
+ if (type == null) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+ "Callback to non-existing class " + fqn, e
+ );
+ return "";
+ }
+ ExecutableElement found = null;
+ StringBuilder foundParams = new StringBuilder();
+ for (Element m : type.getEnclosedElements()) {
+ if (m.getKind() != ElementKind.METHOD) {
+ continue;
+ }
+ if (m.getSimpleName().contentEquals(method)) {
+ String paramTypes = findParamTypes((ExecutableElement)m);
+ if (paramTypes.equals(params)) {
+ found = (ExecutableElement) m;
+ break;
+ }
+ foundParams.append(paramTypes).append("\n");
+ }
+ }
+ if (found == null) {
+ if (foundParams.length() == 0) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+ "Callback to class " + fqn + " with unknown method " + method, e
+ );
+ } else {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+ "Callback to " + fqn + "." + method + " with wrong parameters: " +
+ params + ". Only known parameters are " + foundParams, e
+ );
+ }
+ } else {
+ Map<String,ExecutableElement> mangledOnes = javacalls.get(findPkg(e));
+ if (mangledOnes == null) {
+ mangledOnes = new TreeMap<String, ExecutableElement>();
+ javacalls.put(findPkg(e), mangledOnes);
+ }
+ String mangled = JsCallback.mangle(fqn, method, findParamTypes(found));
+ mangledOnes.put(mangled, found);
+ }
+ return "";
+ }
+
+ private String findParamTypes(ExecutableElement method) {
+ ExecutableType t = (ExecutableType) method.asType();
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ for (TypeMirror tm : t.getParameterTypes()) {
+ if (tm.getKind().isPrimitive()) {
+ switch (tm.getKind()) {
+ case INT: sb.append('I'); break;
+ case BOOLEAN: sb.append('Z'); break;
+ case BYTE: sb.append('B'); break;
+ case CHAR: sb.append('C'); break;
+ case SHORT: sb.append('S'); break;
+ case DOUBLE: sb.append('D'); break;
+ case FLOAT: sb.append('F'); break;
+ case LONG: sb.append('J'); break;
+ default:
+ throw new IllegalStateException("Uknown " + tm.getKind());
+ }
+ } else {
+ while (tm.getKind() == TypeKind.ARRAY) {
+ sb.append('[');
+ tm = ((ArrayType)tm).getComponentType();
+ }
+ sb.append('L');
+ Types tu = processingEnv.getTypeUtils();
+ Element elm = tu.asElement(tu.erasure(tm));
+ dumpElems(sb, elm, ';');
+ }
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+ }
+
+ private static void dumpElems(StringBuilder sb, Element e, char after) {
+ if (e == null) {
+ return;
+ }
+ if (e.getKind() == ElementKind.PACKAGE) {
+ PackageElement pe = (PackageElement) e;
+ sb.append(pe.getQualifiedName().toString().replace('.', '/')).append('/');
+ return;
+ }
+ Element p = e.getEnclosingElement();
+ dumpElems(sb, p, '$');
+ sb.append(e.getSimpleName());
+ sb.append(after);
+ }
+
+ private void generateJavaScriptBodyList(Map<String,Set<TypeElement>> bodies) {
+ if (bodies.isEmpty()) {
+ return;
+ }
+ try {
+ FileObject all = processingEnv.getFiler().createResource(
+ StandardLocation.CLASS_OUTPUT, "", "META-INF/net.java.html.js.classes"
+ );
+ PrintWriter wAll = new PrintWriter(new OutputStreamWriter(
+ all.openOutputStream(), "UTF-8"
+ ));
+ for (Map.Entry<String, Set<TypeElement>> entry : bodies.entrySet()) {
+ String pkg = entry.getKey();
+ Set<TypeElement> classes = entry.getValue();
+
+ FileObject out = processingEnv.getFiler().createResource(
+ StandardLocation.CLASS_OUTPUT, pkg, "net.java.html.js.classes",
+ classes.iterator().next()
+ );
+ OutputStream os = out.openOutputStream();
+ try {
+ PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
+ for (TypeElement type : classes) {
+ final Name bn = processingEnv.getElementUtils().getBinaryName(type);
+ w.println(bn);
+ wAll.println(bn);
+ }
+ w.flush();
+ w.close();
+ } catch (IOException x) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
+ } finally {
+ os.close();
+ }
+ }
+ wAll.close();
+ } catch (IOException x) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + "META-INF/net.java.html.js.classes: " + x.toString());
+ }
+ }
+
+ private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
+ for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
+ String pkgName = pkgEn.getKey();
+ Map<String, ExecutableElement> map = pkgEn.getValue();
+ StringBuilder source = new StringBuilder();
+ source.append("package ").append(pkgName).append(";\n");
+ source.append("public final class $JsCallbacks$ {\n");
+ source.append(" static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
+ source.append(" private final org.netbeans.html.boot.spi.Fn.Presenter p;\n");
+ source.append(" private $JsCallbacks$ last;\n");
+ source.append(" private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
+ source.append(" this.p = p;\n");
+ source.append(" }\n");
+ source.append(" final $JsCallbacks$ current() {\n");
+ source.append(" org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
+ source.append(" if (now == p) return this;\n");
+ source.append(" if (last != null && now == last.p) return last;\n");
+ source.append(" return last = new $JsCallbacks$(now);\n");
+ source.append(" }\n");
+ for (Map.Entry<String, ExecutableElement> entry : map.entrySet()) {
+ final String mangled = entry.getKey();
+ final ExecutableElement m = entry.getValue();
+ generateMethod(false, m, source, mangled);
+ generateMethod(true, m, source, "raw$" + mangled);
+ }
+ source.append("}\n");
+ final String srcName = pkgName + ".$JsCallbacks$";
+ try {
+ Writer w = processingEnv.getFiler().createSourceFile(srcName,
+ map.values().toArray(new Element[map.size()])
+ ).openWriter();
+ w.write(source.toString());
+ w.close();
+ } catch (IOException ex) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.ERROR, "Can't write " + srcName + ": " + ex.getMessage()
+ );
+ }
+ }
+ }
+
+ private void generateMethod(boolean selfObj, final ExecutableElement m, StringBuilder source, final String mangled) {
+ final boolean isStatic = m.getModifiers().contains(Modifier.STATIC);
+ if (isStatic && selfObj) {
+ return;
+ }
+ final TypeElement selfType = (TypeElement)m.getEnclosingElement();
+ Types tu = processingEnv.getTypeUtils();
+
+ source.append("\n public java.lang.Object ")
+ .append(mangled)
+ .append("(");
+
+ String sep = "";
+ StringBuilder convert = new StringBuilder();
+ if (!isStatic) {
+ if (selfObj) {
+ source.append("java.lang.Object self");
+ convert.append(" if (p instanceof org.netbeans.html.boot.spi.Fn.FromJavaScript) {\n");
+ convert.append(" self").
+ append(" = ((org.netbeans.html.boot.spi.Fn.FromJavaScript)p).toJava(self").
+ append(");\n");
+ convert.append(" }\n");
+ } else {
+ source.append(selfType.getQualifiedName());
+ source.append(" self");
+ }
+ sep = ", ";
+ }
+
+ int cnt = 0;
+ for (VariableElement ve : m.getParameters()) {
+ source.append(sep);
+ ++cnt;
+ final TypeMirror t = ve.asType();
+ if (!t.getKind().isPrimitive() && !"java.lang.String".equals(t.toString())) { // NOI18N
+ source.append("java.lang.Object");
+ convert.append(" if (p instanceof org.netbeans.html.boot.spi.Fn.FromJavaScript) {\n");
+ convert.append(" arg").append(cnt).
+ append(" = ((org.netbeans.html.boot.spi.Fn.FromJavaScript)p).toJava(arg").append(cnt).
+ append(");\n");
+ convert.append(" }\n");
+ } else {
+ source.append(t);
+ }
+ source.append(" arg").append(cnt);
+ sep = ", ";
+ }
+ source.append(") throws Throwable {\n");
+ source.append(convert);
+ if (useTryResources()) {
+ source.append(" try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
+ } else {
+ source.append(" java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p); try {\n");
+ }
+ source.append(" ");
+ if (m.getReturnType().getKind() != TypeKind.VOID) {
+ source.append("java.lang.Object $ret = ");
+ }
+ if (isStatic) {
+ source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
+ source.append('.');
+ } else {
+ if (selfObj) {
+ source.append("((");
+ source.append(selfType.getQualifiedName());
+ source.append(")self).");
+ } else {
+ source.append("self.");
+ }
+ }
+ source.append(m.getSimpleName());
+ source.append("(");
+ cnt = 0;
+ sep = "";
+ for (VariableElement ve : m.getParameters()) {
+ source.append(sep);
+ source.append("(").append(tu.erasure(ve.asType()));
+ source.append(")arg").append(++cnt);
+ sep = ", ";
+ }
+ source.append(");\n");
+ if (m.getReturnType().getKind() == TypeKind.VOID) {
+ source.append(" return null;\n");
+ } else {
+ source.append(" if (p instanceof org.netbeans.html.boot.spi.Fn.ToJavaScript) {\n");
+ source.append(" $ret = ((org.netbeans.html.boot.spi.Fn.ToJavaScript)p).toJavaScript($ret);\n");
+ source.append(" }\n");
+ source.append(" return $ret;\n");
+ }
+ if (useTryResources()) {
+ source.append(" }\n");
+ } else {
+
+ source.append(" } finally {\n");
+ source.append(" a.close();\n");
+ source.append(" }\n");
+ }
+ source.append(" }\n");
+ }
+
+ private boolean useTryResources() {
+ try {
+ return processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0;
+ } catch (LinkageError err) {
+ // can happen when running on JDK6
+ return false;
+ }
+ }
+
+ private static String findPkg(Element e) {
+ while (e.getKind() != ElementKind.PACKAGE) {
+ e = e.getEnclosingElement();
+ }
+ return ((PackageElement)e).getQualifiedName().toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
new file mode 100644
index 0000000..5d72fa2
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
@@ -0,0 +1,75 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JsAgent implements ClassFileTransformer {
+ public static void premain(String args, Instrumentation instr) {
+ instr.addTransformer(new JsAgent());
+ }
+
+ public static void agentmain(String args, Instrumentation instr) {
+ instr.addTransformer(new JsAgent());
+ }
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ try {
+ if (JsPkgCache.process(loader, className)) {
+ return FnUtils.transform(classfileBuffer, loader);
+ } else {
+ return classfileBuffer;
+ }
+ } catch (Exception ex) {
+ return classfileBuffer;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
new file mode 100644
index 0000000..45bf53b
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
@@ -0,0 +1,160 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+abstract class JsCallback {
+ final String parse(String body) {
+ StringBuilder sb = new StringBuilder();
+ int pos = 0;
+ for (;;) {
+ int next = body.indexOf(".@", pos);
+ if (next == -1) {
+ sb.append(body.substring(pos));
+ body = sb.toString();
+ break;
+ }
+ int ident = next;
+ while (ident > 0) {
+ if (!Character.isJavaIdentifierPart(body.charAt(--ident))) {
+ ident++;
+ break;
+ }
+ }
+ String refId = body.substring(ident, next);
+
+ sb.append(body.substring(pos, ident));
+
+ int sigBeg = body.indexOf('(', next);
+ int sigEnd = body.indexOf(')', sigBeg);
+ int colon4 = body.indexOf("::", next);
+ if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
+ throw new IllegalStateException(
+ "Wrong format of instance callback. "
+ + "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n"
+ + body
+ );
+ }
+ String fqn = body.substring(next + 2, colon4);
+ String method = body.substring(colon4 + 2, sigBeg);
+ String params = body.substring(sigBeg, sigEnd + 1);
+
+ int paramBeg = body.indexOf('(', sigEnd + 1);
+ if (paramBeg == -1) {
+ throw new IllegalStateException(
+ "Wrong format of instance callback. "
+ + "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n"
+ + body
+ );
+ }
+
+ sb.append(callMethod(refId, fqn, method, params));
+ if (body.charAt(paramBeg + 1) != (')')) {
+ sb.append(",");
+ }
+ pos = paramBeg + 1;
+ }
+ pos = 0;
+ sb = null;
+ for (;;) {
+ int next = body.indexOf("@", pos);
+ if (next == -1) {
+ if (sb == null) {
+ return body;
+ }
+ sb.append(body.substring(pos));
+ return sb.toString();
+ }
+ if (sb == null) {
+ sb = new StringBuilder();
+ }
+
+ sb.append(body.substring(pos, next));
+
+ int sigBeg = body.indexOf('(', next);
+ int sigEnd = body.indexOf(')', sigBeg);
+ int colon4 = body.indexOf("::", next);
+ int paramBeg = body.indexOf('(', sigEnd + 1);
+ if (sigBeg == -1 || sigEnd == -1 || colon4 == -1 || paramBeg == -1) {
+ throw new IllegalStateException(
+ "Wrong format of static callback. "
+ + "Should be: '@pkg.Class::staticMethod(Ljava/lang/Object;)(param)':\n"
+ + body
+ );
+ }
+ String fqn = body.substring(next + 1, colon4);
+ String method = body.substring(colon4 + 2, sigBeg);
+ String params = body.substring(sigBeg, sigEnd + 1);
+
+
+ sb.append(callMethod(null, fqn, method, params));
+ pos = paramBeg + 1;
+ }
+ }
+
+ protected abstract CharSequence callMethod(
+ String ident, String fqn, String method, String params
+ );
+
+ static String mangle(String fqn, String method, String params) {
+ if (params.startsWith("(")) {
+ params = params.substring(1);
+ }
+ if (params.endsWith(")")) {
+ params = params.substring(0, params.length() - 1);
+ }
+ return
+ replace(fqn) + "$" + replace(method) + "$" + replace(params);
+ }
+
+ private static String replace(String orig) {
+ return orig.replace("_", "_1").
+ replace(";", "_2").
+ replace("[", "_3").
+ replace('.', '_').replace('/', '_');
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
new file mode 100644
index 0000000..58b0a62
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
@@ -0,0 +1,57 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import net.java.html.js.JavaScriptBody;
+
+/** Marker class to help us recognize we assigned classloader is
+ * capable to handle {@link JavaScriptBody} annotated methods.
+ *
+ * @author Jaroslav Tulach
+ */
+abstract class JsClassLoader extends ClassLoader {
+ JsClassLoader(ClassLoader parent) {
+ super(parent);
+ setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
new file mode 100644
index 0000000..7064d57
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
@@ -0,0 +1,132 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class JsPkgCache {
+ private final Map<String,Set<String>> props = new WeakHashMap<String, Set<String>>();
+ private static final Map<ClassLoader, JsPkgCache> CACHE = new WeakHashMap<ClassLoader, JsPkgCache>();
+ private static final Set<String> NONE = Collections.emptySet();
+
+ public static boolean process(ClassLoader l, String className) {
+ if (className.equals("org.netbeans.html.boot.impl.Test")) { // NOI18N
+ return true;
+ }
+ Set<String> p;
+ JsPkgCache c;
+ String pkgName;
+ synchronized (CACHE) {
+ c = CACHE.get(l);
+ if (c == null) {
+ c = new JsPkgCache();
+ CACHE.put(l, c);
+ }
+ int lastDot = className.lastIndexOf('.');
+ pkgName = className.substring(0, lastDot + 1).replace('.', '/');
+ p = c.props.get(pkgName);
+ if (p == NONE) {
+ return false;
+ } else if (p != null) {
+ return p.contains(className);
+ }
+ }
+ final String res = pkgName + "net.java.html.js.classes";
+
+ Enumeration<URL> en;
+ try {
+ en = l.getResources(res);
+ } catch (IOException ex) {
+ en = null;
+ }
+ if (en == null || !en.hasMoreElements()) synchronized (CACHE) {
+ c.props.put(pkgName, NONE);
+ return false;
+ }
+
+ try {
+ Set<String> arr = new TreeSet<String>();
+ while (en.hasMoreElements()) {
+ URL u = en.nextElement();
+ BufferedReader r = new BufferedReader(
+ new InputStreamReader(u.openStream(), "UTF-8")
+ );
+ for (;;) {
+ String line = r.readLine();
+ if (line == null) {
+ break;
+ }
+ arr.add(line);
+ }
+ r.close();
+ }
+ p = arr;
+ } catch (IOException ex) {
+ LOG.log(Level.WARNING, "Can't read " + res, ex);
+ p = NONE;
+ }
+
+ synchronized (CACHE) {
+ c.props.put(pkgName, p);
+ return p.contains(className);
+ }
+
+ }
+ private static final Logger LOG = Logger.getLogger(JsPkgCache.class.getName());
+}