Use optional dependency on asm-5.0.jar, so we don't have to bundle it when the classes are post processed.
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 isValid(Fn fn) {
112 return fn != null && fn.isValid();
115 public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
116 return new JsClassLoader(parent) {
118 protected URL findResource(String name) {
119 List<URL> l = res(name, true);
120 return l.isEmpty() ? null : l.get(0);
124 protected Enumeration<URL> findResources(String name) {
125 return Collections.enumeration(res(name, false));
128 private List<URL> res(String name, boolean oneIsEnough) {
129 List<URL> l = new ArrayList<URL>();
130 f.findResources(name, l, oneIsEnough);
135 protected Fn defineFn(String code, String... names) {
136 return d.defineFn(code, names);
140 protected void loadScript(Reader code) throws Exception {
146 static String callback(final String body) {
147 return new JsCallback() {
149 protected CharSequence callMethod(
150 String ident, String fqn, String method, String params
152 StringBuilder sb = new StringBuilder();
153 sb.append("vm.").append(mangle(fqn, method, params));
164 static void loadScript(ClassLoader jcl, String resource) {
165 final InputStream script = jcl.getResourceAsStream(resource);
166 if (script == null) {
167 throw new NullPointerException("Can't find " + resource);
172 isr = new InputStreamReader(script, "UTF-8");
173 FnContext.currentPresenter(false).loadScript(isr);
179 } catch (Exception ex) {
180 throw new IllegalStateException("Can't execute " + resource, ex);
185 private static final class FindInClass extends ClassVisitor {
188 private ClassLoader loader;
189 private String resource;
191 public FindInClass(ClassLoader l, ClassVisitor cv) {
192 super(Opcodes.ASM4, cv);
197 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
199 super.visit(version, access, name, signature, superName, interfaces);
203 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
204 final AnnotationVisitor del = super.visitAnnotation(desc, visible);
205 if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
206 return new LoadResource(del);
212 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
213 return new FindInMethod(access, name, desc,
214 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
218 private final class FindInMethod extends MethodVisitor {
220 private final String name;
221 private final String desc;
222 private final int access;
223 private FindInAnno fia;
224 private boolean bodyGenerated;
226 public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
227 super(Opcodes.ASM4, mv);
228 this.access = access;
234 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
235 if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
237 return new FindInAnno();
239 return super.visitAnnotation(desc, visible);
242 private void generateJSBody(FindInAnno fia) {
247 public void visitCode() {
254 private boolean generateBody(boolean hasCode) {
258 bodyGenerated = true;
260 AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
261 AnnotationVisitor varr = va.visitArray("args");
262 for (String argName : fia.args) {
263 varr.visit(null, argName);
266 va.visit("javacall", fia.javacall);
267 va.visit("body", fia.body);
274 body = callback(fia.body);
275 args = new ArrayList<String>(fia.args);
282 super.visitFieldInsn(
283 Opcodes.GETSTATIC, FindInClass.this.name,
284 "$$fn$$" + name + "_" + found,
285 "Lorg/netbeans/html/boot/spi/Fn;"
287 super.visitInsn(Opcodes.DUP);
288 super.visitMethodInsn(
289 Opcodes.INVOKESTATIC,
290 "org/netbeans/html/boot/spi/Fn", "isValid",
291 "(Lorg/netbeans/html/boot/spi/Fn;)Z"
293 Label ifNotNull = new Label();
294 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
297 super.visitInsn(Opcodes.POP);
298 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
299 super.visitLdcInsn(body);
300 super.visitIntInsn(Opcodes.SIPUSH, args.size());
301 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
302 boolean needsVM = false;
303 for (int i = 0; i < args.size(); i++) {
305 String argName = args.get(i);
306 needsVM = "vm".equals(argName);
307 super.visitInsn(Opcodes.DUP);
308 super.visitIntInsn(Opcodes.BIPUSH, i);
309 super.visitLdcInsn(argName);
310 super.visitInsn(Opcodes.AASTORE);
312 super.visitMethodInsn(Opcodes.INVOKESTATIC,
313 "org/netbeans/html/boot/spi/Fn", "define",
314 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
316 Label noPresenter = new Label();
317 super.visitInsn(Opcodes.DUP);
318 super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
319 if (resource != null) {
320 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
321 super.visitLdcInsn(resource);
322 super.visitMethodInsn(Opcodes.INVOKESTATIC,
323 "org/netbeans/html/boot/spi/Fn", "preload",
324 "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
327 super.visitInsn(Opcodes.DUP);
328 super.visitFieldInsn(
329 Opcodes.PUTSTATIC, FindInClass.this.name,
330 "$$fn$$" + name + "_" + found,
331 "Lorg/netbeans/html/boot/spi/Fn;"
335 super.visitLabel(ifNotNull);
338 if ((access & Opcodes.ACC_STATIC) == 0) {
340 super.visitIntInsn(Opcodes.ALOAD, 0);
343 super.visitInsn(Opcodes.ACONST_NULL);
346 super.visitIntInsn(Opcodes.SIPUSH, args.size());
347 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
349 class SV extends SignatureVisitor {
351 private boolean nowReturn;
352 private Type returnType;
354 private int loadIndex = offset;
361 public void visitBaseType(char descriptor) {
362 final Type t = Type.getType("" + descriptor);
367 FindInMethod.super.visitInsn(Opcodes.DUP);
368 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
369 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
371 switch (descriptor) {
373 factory = "java/lang/Integer";
376 factory = "java/lang/Long";
380 factory = "java/lang/Short";
383 factory = "java/lang/Float";
386 factory = "java/lang/Double";
390 factory = "java/lang/Boolean";
393 factory = "java/lang/Character";
396 factory = "java/lang/Byte";
399 throw new IllegalStateException(t.toString());
401 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
402 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
404 FindInMethod.super.visitInsn(Opcodes.AASTORE);
408 public SignatureVisitor visitArrayType() {
410 return new SignatureVisitor(Opcodes.ASM4) {
412 public void visitClassType(String name) {
413 returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
418 return new SignatureWriter();
422 public void visitClassType(String name) {
424 returnType = Type.getObjectType(name);
431 public SignatureVisitor visitReturnType() {
436 private void loadObject() {
437 FindInMethod.super.visitInsn(Opcodes.DUP);
438 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
439 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
440 FindInMethod.super.visitInsn(Opcodes.AASTORE);
445 SignatureReader sr = new SignatureReader(desc);
449 FindInMethod.super.visitInsn(Opcodes.DUP);
450 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
451 int lastSlash = FindInClass.this.name.lastIndexOf('/');
452 String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
453 FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
454 FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
455 FindInMethod.super.visitInsn(Opcodes.AASTORE);
459 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
460 "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
462 switch (sv.returnType.getSort()) {
464 super.visitInsn(Opcodes.RETURN);
468 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
469 super.visitInsn(Opcodes.ARETURN);
472 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
473 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
474 "java/lang/Boolean", "booleanValue", "()Z"
476 super.visitInsn(Opcodes.IRETURN);
479 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
480 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
481 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
483 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
486 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
487 "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
489 super.visitInsn(Opcodes.RETURN);
491 super.visitLabel(noPresenter);
495 super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
496 super.visitInsn(Opcodes.DUP);
497 super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
498 super.visitMethodInsn(Opcodes.INVOKESPECIAL,
499 "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
501 this.visitInsn(Opcodes.ATHROW);
507 public void visitEnd() {
510 if (generateBody(false)) {
512 super.visitMaxs(1, 0);
514 FindInClass.this.visitField(
515 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
516 "$$fn$$" + name + "_" + found,
517 "Lorg/netbeans/html/boot/spi/Fn;",
523 private final class FindInAnno extends AnnotationVisitor {
525 List<String> args = new ArrayList<String>();
527 boolean javacall = false;
528 boolean wait4js = true;
530 public FindInAnno() {
535 public void visit(String name, Object value) {
537 args.add((String) value);
540 if (name.equals("javacall")) { // NOI18N
541 javacall = (Boolean) value;
544 if (name.equals("wait4js")) { // NOI18N
545 wait4js = (Boolean) value;
548 assert name.equals("body");
549 body = (String) value;
553 public AnnotationVisitor visitArray(String name) {
558 public void visitEnd() {
560 generateJSBody(this);
566 private final class LoadResource extends AnnotationVisitor {
567 public LoadResource(AnnotationVisitor av) {
568 super(Opcodes.ASM4, av);
572 public void visit(String attrName, Object value) {
573 super.visit(attrName, value);
574 String relPath = (String) value;
575 if (relPath.startsWith("/")) {
578 int last = name.lastIndexOf('/');
579 String fullPath = name.substring(0, last + 1) + relPath;
586 private static class ClassWriterEx extends ClassWriter {
588 private ClassLoader loader;
590 public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
591 super(classReader, flags);
596 protected String getCommonSuperClass(final String type1, final String type2) {
599 c = Class.forName(type1.replace('/', '.'), false, loader);
600 d = Class.forName(type2.replace('/', '.'), false, loader);
601 } catch (Exception e) {
602 throw new RuntimeException(e.toString());
604 if (c.isAssignableFrom(d)) {
607 if (d.isAssignableFrom(c)) {
610 if (c.isInterface() || d.isInterface()) {
611 return "java/lang/Object";
614 c = c.getSuperclass();
615 } while (!c.isAssignableFrom(d));
616 return c.getName().replace('.', '/');