2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2010 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-2013 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.Closeable;
46 import java.io.InputStream;
47 import java.io.InputStreamReader;
48 import java.io.Reader;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.Enumeration;
53 import java.util.List;
54 import java.util.concurrent.Callable;
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 <jtulach@netbeans.org>
72 public final class FnUtils implements Fn.Presenter {
77 public static boolean isJavaScriptCapable(ClassLoader l) {
78 if (l instanceof JsClassLoader) {
82 try (Closeable c = Fn.activate(new FnUtils())) {
83 clazz = Class.forName(Test.class.getName(), true, l);
84 final Object is = ((Callable<?>)clazz.newInstance()).call();
85 return Boolean.TRUE.equals(is);
86 } catch (Exception ex) {
91 public static boolean isValid(Fn fn) {
92 return fn != null && fn.isValid();
95 public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
96 return new JsClassLoader(parent) {
98 protected URL findResource(String name) {
99 List<URL> l = res(name, true);
100 return l.isEmpty() ? null : l.get(0);
104 protected Enumeration<URL> findResources(String name) {
105 return Collections.enumeration(res(name, false));
108 private List<URL> res(String name, boolean oneIsEnough) {
109 List<URL> l = new ArrayList<URL>();
110 f.findResources(name, l, oneIsEnough);
115 protected Fn defineFn(String code, String... names) {
116 return d.defineFn(code, names);
120 protected void loadScript(Reader code) throws Exception {
126 static String callback(final String body) {
127 return new JsCallback() {
129 protected CharSequence callMethod(
130 String ident, String fqn, String method, String params
132 StringBuilder sb = new StringBuilder();
133 sb.append("vm.").append(mangle(fqn, method, params));
144 static void loadScript(ClassLoader jcl, String resource) {
145 final InputStream script = jcl.getResourceAsStream(resource);
146 if (script == null) {
147 throw new NullPointerException("Can't find " + resource);
152 isr = new InputStreamReader(script, "UTF-8");
153 FnContext.currentPresenter().loadScript(isr);
159 } catch (Exception ex) {
160 throw new IllegalStateException("Can't execute " + resource, ex);
165 public Fn defineFn(String code, String... names) {
170 public void displayPage(URL page, Runnable onPageLoad) {
174 public void loadScript(Reader code) throws Exception {
177 private static final class FindInClass extends ClassVisitor {
180 private ClassLoader loader;
181 private String resource;
183 public FindInClass(ClassLoader l, ClassVisitor cv) {
184 super(Opcodes.ASM4, cv);
189 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
191 super.visit(version, access, name, signature, superName, interfaces);
195 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
196 if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
197 return new LoadResource();
199 return super.visitAnnotation(desc, visible);
203 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
204 return new FindInMethod(access, name, desc,
205 super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
209 private final class FindInMethod extends MethodVisitor {
211 private final String name;
212 private final String desc;
213 private final int access;
214 private List<String> args;
216 private boolean bodyGenerated;
218 public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
219 super(Opcodes.ASM4, mv);
220 this.access = access;
226 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
227 if ("Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
228 || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
231 return new FindInAnno();
233 return super.visitAnnotation(desc, visible);
236 private void generateJSBody(List<String> args, String body) {
242 public void visitCode() {
249 private boolean generateBody() {
253 bodyGenerated = true;
255 super.visitFieldInsn(
256 Opcodes.GETSTATIC, FindInClass.this.name,
257 "$$fn$$" + name + "_" + found,
258 "Lorg/apidesign/html/boot/spi/Fn;"
260 super.visitInsn(Opcodes.DUP);
261 super.visitMethodInsn(
262 Opcodes.INVOKESTATIC,
263 "org/apidesign/html/boot/spi/Fn", "isValid",
264 "(Lorg/apidesign/html/boot/spi/Fn;)Z"
266 Label ifNotNull = new Label();
267 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
270 super.visitInsn(Opcodes.POP);
271 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
272 super.visitLdcInsn(body);
273 super.visitIntInsn(Opcodes.SIPUSH, args.size());
274 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
275 boolean needsVM = false;
276 for (int i = 0; i < args.size(); i++) {
278 String argName = args.get(i);
279 needsVM = "vm".equals(argName);
280 super.visitInsn(Opcodes.DUP);
281 super.visitIntInsn(Opcodes.BIPUSH, i);
282 super.visitLdcInsn(argName);
283 super.visitInsn(Opcodes.AASTORE);
285 super.visitMethodInsn(Opcodes.INVOKESTATIC,
286 "org/apidesign/html/boot/spi/Fn", "define",
287 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
289 if (resource != null) {
290 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
291 super.visitLdcInsn(resource);
292 super.visitMethodInsn(Opcodes.INVOKESTATIC,
293 "org/apidesign/html/boot/spi/Fn", "preload",
294 "(Lorg/apidesign/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
297 super.visitInsn(Opcodes.DUP);
298 super.visitFieldInsn(
299 Opcodes.PUTSTATIC, FindInClass.this.name,
300 "$$fn$$" + name + "_" + found,
301 "Lorg/apidesign/html/boot/spi/Fn;"
305 super.visitLabel(ifNotNull);
308 if ((access & Opcodes.ACC_STATIC) == 0) {
310 super.visitIntInsn(Opcodes.ALOAD, 0);
313 super.visitInsn(Opcodes.ACONST_NULL);
316 super.visitIntInsn(Opcodes.SIPUSH, args.size());
317 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
319 class SV extends SignatureVisitor {
321 private boolean nowReturn;
322 private Type returnType;
324 private int loadIndex = offset;
331 public void visitBaseType(char descriptor) {
332 final Type t = Type.getType("" + descriptor);
337 FindInMethod.super.visitInsn(Opcodes.DUP);
338 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
339 FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
341 switch (descriptor) {
343 factory = "java/lang/Integer";
346 factory = "java/lang/Long";
350 factory = "java/lang/Short";
353 factory = "java/lang/Float";
356 factory = "java/lang/Double";
360 factory = "java/lang/Boolean";
363 factory = "java/lang/Character";
366 factory = "java/lang/Byte";
369 throw new IllegalStateException(t.toString());
371 FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
372 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
374 FindInMethod.super.visitInsn(Opcodes.AASTORE);
378 public SignatureVisitor visitArrayType() {
380 throw new IllegalStateException("Not supported yet");
383 return new SignatureWriter();
387 public void visitClassType(String name) {
389 returnType = Type.getObjectType(name);
396 public SignatureVisitor visitReturnType() {
401 private void loadObject() {
402 FindInMethod.super.visitInsn(Opcodes.DUP);
403 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
404 FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
405 FindInMethod.super.visitInsn(Opcodes.AASTORE);
410 SignatureReader sr = new SignatureReader(desc);
414 FindInMethod.super.visitInsn(Opcodes.DUP);
415 FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
416 int lastSlash = FindInClass.this.name.lastIndexOf('/');
417 String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
418 FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
419 FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
420 FindInMethod.super.visitInsn(Opcodes.AASTORE);
423 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
424 "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
426 switch (sv.returnType.getSort()) {
428 super.visitInsn(Opcodes.RETURN);
432 super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
433 super.visitInsn(Opcodes.ARETURN);
436 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
437 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
438 "java/lang/Boolean", "booleanValue", "()Z"
440 super.visitInsn(Opcodes.IRETURN);
443 super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
444 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
445 "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
447 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
453 public void visitEnd() {
456 if (generateBody()) {
458 super.visitMaxs(1, 0);
460 FindInClass.this.visitField(
461 Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
462 "$$fn$$" + name + "_" + found,
463 "Lorg/apidesign/html/boot/spi/Fn;",
469 private final class FindInAnno extends AnnotationVisitor {
471 private List<String> args = new ArrayList<String>();
473 private boolean javacall = false;
475 public FindInAnno() {
480 public void visit(String name, Object value) {
482 args.add((String) value);
485 if (name.equals("javacall")) { // NOI18N
486 javacall = (Boolean) value;
489 assert name.equals("body");
490 body = (String) value;
494 public AnnotationVisitor visitArray(String name) {
499 public void visitEnd() {
502 body = callback(body);
505 generateJSBody(args, body);
511 private final class LoadResource extends AnnotationVisitor {
513 public LoadResource() {
518 public void visit(String attrName, Object value) {
519 String relPath = (String) value;
520 if (relPath.startsWith("/")) {
523 int last = name.lastIndexOf('/');
524 String fullPath = name.substring(0, last + 1) + relPath;
531 private static class ClassWriterEx extends ClassWriter {
533 private ClassLoader loader;
535 public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
536 super(classReader, flags);
541 protected String getCommonSuperClass(final String type1, final String type2) {
544 c = Class.forName(type1.replace('/', '.'), false, loader);
545 d = Class.forName(type2.replace('/', '.'), false, loader);
546 } catch (Exception e) {
547 throw new RuntimeException(e.toString());
549 if (c.isAssignableFrom(d)) {
552 if (d.isAssignableFrom(c)) {
555 if (c.isInterface() || d.isInterface()) {
556 return "java/lang/Object";
559 c = c.getSuperclass();
560 } while (!c.isAssignableFrom(d));
561 return c.getName().replace('.', '/');
566 static byte[] transform(ClassLoader loader, byte[] arr) {
567 ClassReader cr = new ClassReader(arr) {
568 // to allow us to compile with -profile compact1 on
569 // JDK8 while processing the class as JDK7, the highest
570 // class format asm 4.1 understands to
572 public short readShort(int index) {
573 short s = super.readShort(index);
574 if (index == 6 && s > Opcodes.V1_7) {
580 FindInClass tst = new FindInClass(loader, null);
583 ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
584 FindInClass fic = new FindInClass(loader, w);
586 arr = w.toByteArray();
591 private static final class TrueFn extends Fn {
593 public Object invoke(Object thiz, Object... args) throws Exception {