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.apidesign.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/apidesign/html/boot/spi/Fn;"
297 super.visitInsn(Opcodes.DUP);
298 super.visitMethodInsn(
299 Opcodes.INVOKESTATIC,
300 "org/apidesign/html/boot/spi/Fn", "isValid",
301 "(Lorg/apidesign/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/apidesign/html/boot/spi/Fn", "define",
324 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/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/apidesign/html/boot/spi/Fn", "preload",
336 "(Lorg/apidesign/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
339 super.visitInsn(Opcodes.DUP);
340 super.visitFieldInsn(
341 Opcodes.PUTSTATIC, FindInClass.this.name,
342 "$$fn$$" + name + "_" + found,
343 "Lorg/apidesign/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/apidesign/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/apidesign/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/apidesign/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('.', '/');