During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package org.netbeans.html.boot.impl;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.io.Reader;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.Enumeration;
52 import java.util.List;
53 import net.java.html.js.JavaScriptBody;
54 import net.java.html.js.JavaScriptResource;
55 import org.netbeans.html.boot.spi.Fn;
56 import org.objectweb.asm.AnnotationVisitor;
57 import org.objectweb.asm.ClassReader;
58 import org.objectweb.asm.ClassVisitor;
59 import org.objectweb.asm.ClassWriter;
60 import org.objectweb.asm.Label;
61 import org.objectweb.asm.MethodVisitor;
62 import org.objectweb.asm.Opcodes;
63 import org.objectweb.asm.Type;
64 import org.objectweb.asm.signature.SignatureReader;
65 import org.objectweb.asm.signature.SignatureVisitor;
66 import org.objectweb.asm.signature.SignatureWriter;
70 * @author Jaroslav Tulach
72 public final class FnUtils {
77 /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
78 * in the bytecode and converts them into real code. Used by Maven plugin
79 * postprocessing classes.
81 * @param bytecode the original bytecode with javascript specific annotations
82 * @param loader the loader to load resources (scripts and classes) when needed
83 * @return the transformed bytecode
86 public static byte[] transform(byte[] bytecode, ClassLoader loader) {
87 ClassReader cr = new ClassReader(bytecode) {
88 // to allow us to compile with -profile compact1 on
89 // JDK8 while processing the class as JDK7, the highest
90 // class format asm 4.1 understands to
92 public short readShort(int index) {
93 short s = super.readShort(index);
94 if (index == 6 && s > Opcodes.V1_7) {
100 FindInClass tst = new FindInClass(loader, null);
103 ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
104 FindInClass fic = new FindInClass(loader, w);
106 bytecode = w.toByteArray();
111 public static boolean isJavaScriptCapable(ClassLoader l) {
112 if (l instanceof JsClassLoader) {
115 if (l.getResource("META-INF/net.java.html.js.classes") != null) {
121 public static boolean isValid(Fn fn) {
122 return fn != null && fn.isValid();
125 public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
126 return new JsClassLoader(parent) {
128 protected URL findResource(String name) {
129 List<URL> l = res(name, true);
130 return l.isEmpty() ? null : l.get(0);
134 protected Enumeration<URL> findResources(String name) {
135 return Collections.enumeration(res(name, false));
138 private List<URL> res(String name, boolean oneIsEnough) {
139 List<URL> l = new ArrayList<URL>();
140 f.findResources(name, l, oneIsEnough);
145 protected Fn defineFn(String code, String... names) {
146 return d.defineFn(code, names);
150 protected void loadScript(Reader code) throws Exception {
156 static String callback(final String body) {
157 return new JsCallback() {
159 protected CharSequence callMethod(
160 String ident, String fqn, String method, String params
162 StringBuilder sb = new StringBuilder();
163 sb.append("vm.").append(mangle(fqn, method, params));
174 static void loadScript(ClassLoader jcl, String resource) {
175 final InputStream script = jcl.getResourceAsStream(resource);
176 if (script == null) {
177 throw new NullPointerException("Can't find " + resource);
182 isr = new InputStreamReader(script, "UTF-8");
183 FnContext.currentPresenter(false).loadScript(isr);
189 } catch (Exception ex) {
190 throw new IllegalStateException("Can't execute " + resource, ex);
195 private static final class FindInClass extends ClassVisitor {
198 private ClassLoader loader;
199 private String resource;
201 public FindInClass(ClassLoader l, ClassVisitor cv) {
202 super(Opcodes.ASM4, cv);
207 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
209 super.visit(version, access, name, signature, superName, interfaces);
213 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
214 final AnnotationVisitor del = super.visitAnnotation(desc, visible);
215 if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
216 return new LoadResource(del);
222 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
223 return new FindInMethod(access, name, desc,
224 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
228 private final class FindInMethod extends MethodVisitor {
230 private final String name;
231 private final String desc;
232 private final int access;
233 private FindInAnno fia;
234 private boolean bodyGenerated;
236 public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
237 super(Opcodes.ASM4, mv);
238 this.access = access;
244 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
245 if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
247 return new FindInAnno();
249 return super.visitAnnotation(desc, visible);
252 private void generateJSBody(FindInAnno fia) {
257 public void visitCode() {
264 private boolean generateBody(boolean hasCode) {
268 bodyGenerated = true;
270 AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
271 AnnotationVisitor varr = va.visitArray("args");
272 for (String argName : fia.args) {
273 varr.visit(null, argName);
276 va.visit("javacall", fia.javacall);
277 va.visit("body", fia.body);
284 body = callback(fia.body);
285 args = new ArrayList<String>(fia.args);
292 super.visitFieldInsn(
293 Opcodes.GETSTATIC, FindInClass.this.name,
294 "$$fn$$" + name + "_" + found,
295 "Lorg/netbeans/html/boot/spi/Fn;"
297 super.visitInsn(Opcodes.DUP);
298 super.visitMethodInsn(
299 Opcodes.INVOKESTATIC,
300 "org/netbeans/html/boot/spi/Fn", "isValid",
301 "(Lorg/netbeans/html/boot/spi/Fn;)Z"
303 Label ifNotNull = new Label();
304 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
307 super.visitInsn(Opcodes.POP);
308 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
309 super.visitLdcInsn(body);
310 super.visitIntInsn(Opcodes.SIPUSH, args.size());
311 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
312 boolean needsVM = false;
313 for (int i = 0; i < args.size(); i++) {
315 String argName = args.get(i);
316 needsVM = "vm".equals(argName);
317 super.visitInsn(Opcodes.DUP);
318 super.visitIntInsn(Opcodes.BIPUSH, i);
319 super.visitLdcInsn(argName);
320 super.visitInsn(Opcodes.AASTORE);
322 super.visitMethodInsn(Opcodes.INVOKESTATIC,
323 "org/netbeans/html/boot/spi/Fn", "define",
324 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
326 Label noPresenter = new Label();
328 super.visitInsn(Opcodes.DUP);
329 super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
331 if (resource != null) {
332 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
333 super.visitLdcInsn(resource);
334 super.visitMethodInsn(Opcodes.INVOKESTATIC,
335 "org/netbeans/html/boot/spi/Fn", "preload",
336 "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
339 super.visitInsn(Opcodes.DUP);
340 super.visitFieldInsn(
341 Opcodes.PUTSTATIC, FindInClass.this.name,
342 "$$fn$$" + name + "_" + found,
343 "Lorg/netbeans/html/boot/spi/Fn;"
347 super.visitLabel(ifNotNull);
350 if ((access & Opcodes.ACC_STATIC) == 0) {
352 super.visitIntInsn(Opcodes.ALOAD, 0);
355 super.visitInsn(Opcodes.ACONST_NULL);
358 super.visitIntInsn(Opcodes.SIPUSH, args.size());
359 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
361 class SV extends SignatureVisitor {
363 private boolean nowReturn;
364 private Type returnType;
366 private int loadIndex = offset;
373 public void visitBaseType(char descriptor) {
374 final Type t = Type.getType("" + descriptor);
379 FindInMethod.super.visitInsn(Opcodes.DUP);
380 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
381 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
383 switch (descriptor) {
385 factory = "java/lang/Integer";
388 factory = "java/lang/Long";
392 factory = "java/lang/Short";
395 factory = "java/lang/Float";
398 factory = "java/lang/Double";
402 factory = "java/lang/Boolean";
405 factory = "java/lang/Character";
408 factory = "java/lang/Byte";
411 throw new IllegalStateException(t.toString());
413 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
414 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
416 FindInMethod.super.visitInsn(Opcodes.AASTORE);
420 public SignatureVisitor visitArrayType() {
422 return new SignatureVisitor(Opcodes.ASM4) {
424 public void visitClassType(String name) {
425 returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
430 return new SignatureWriter();
434 public void visitClassType(String name) {
436 returnType = Type.getObjectType(name);
443 public SignatureVisitor visitReturnType() {
448 private void loadObject() {
449 FindInMethod.super.visitInsn(Opcodes.DUP);
450 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
451 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
452 FindInMethod.super.visitInsn(Opcodes.AASTORE);
457 SignatureReader sr = new SignatureReader(desc);
461 FindInMethod.super.visitInsn(Opcodes.DUP);
462 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
463 int lastSlash = FindInClass.this.name.lastIndexOf('/');
464 String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
465 FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
466 FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
467 FindInMethod.super.visitInsn(Opcodes.AASTORE);
471 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
472 "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
474 switch (sv.returnType.getSort()) {
476 super.visitInsn(Opcodes.RETURN);
480 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
481 super.visitInsn(Opcodes.ARETURN);
484 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
485 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
486 "java/lang/Boolean", "booleanValue", "()Z"
488 super.visitInsn(Opcodes.IRETURN);
491 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
492 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
493 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
495 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
498 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
499 "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
501 super.visitInsn(Opcodes.RETURN);
504 super.visitLabel(noPresenter);
511 public void visitEnd() {
514 if (generateBody(false)) {
516 super.visitMaxs(1, 0);
518 FindInClass.this.visitField(
519 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
520 "$$fn$$" + name + "_" + found,
521 "Lorg/netbeans/html/boot/spi/Fn;",
527 private final class FindInAnno extends AnnotationVisitor {
529 List<String> args = new ArrayList<String>();
531 boolean javacall = false;
532 boolean wait4js = true;
534 public FindInAnno() {
539 public void visit(String name, Object value) {
541 args.add((String) value);
544 if (name.equals("javacall")) { // NOI18N
545 javacall = (Boolean) value;
548 if (name.equals("wait4js")) { // NOI18N
549 wait4js = (Boolean) value;
552 assert name.equals("body");
553 body = (String) value;
557 public AnnotationVisitor visitArray(String name) {
562 public void visitEnd() {
564 generateJSBody(this);
570 private final class LoadResource extends AnnotationVisitor {
571 public LoadResource(AnnotationVisitor av) {
572 super(Opcodes.ASM4, av);
576 public void visit(String attrName, Object value) {
577 super.visit(attrName, value);
578 String relPath = (String) value;
579 if (relPath.startsWith("/")) {
582 int last = name.lastIndexOf('/');
583 String fullPath = name.substring(0, last + 1) + relPath;
590 private static class ClassWriterEx extends ClassWriter {
592 private ClassLoader loader;
594 public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
595 super(classReader, flags);
600 protected String getCommonSuperClass(final String type1, final String type2) {
603 c = Class.forName(type1.replace('/', '.'), false, loader);
604 d = Class.forName(type2.replace('/', '.'), false, loader);
605 } catch (Exception e) {
606 throw new RuntimeException(e.toString());
608 if (c.isAssignableFrom(d)) {
611 if (d.isAssignableFrom(c)) {
614 if (c.isInterface() || d.isInterface()) {
615 return "java/lang/Object";
618 c = c.getSuperclass();
619 } while (!c.isAssignableFrom(d));
620 return c.getName().replace('.', '/');