boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 24 Nov 2013 15:39:55 +0100
branchpreprocess
changeset 331 72dda6af599b
parent 323 86aabecda7a3
child 332 386429032402
permissions -rw-r--r--
Maven plugin for preprocessing JavaScriptXXX annotations
jaroslav@123
     1
/**
jaroslav@123
     2
 * HTML via Java(tm) Language Bindings
jaroslav@123
     3
 * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
jaroslav@123
     4
 *
jaroslav@123
     5
 * This program is free software: you can redistribute it and/or modify
jaroslav@123
     6
 * it under the terms of the GNU General Public License as published by
jaroslav@123
     7
 * the Free Software Foundation, version 2 of the License.
jaroslav@123
     8
 *
jaroslav@123
     9
 * This program is distributed in the hope that it will be useful,
jaroslav@123
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
jaroslav@123
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
jaroslav@123
    12
 * GNU General Public License for more details. apidesign.org
jaroslav@123
    13
 * designates this particular file as subject to the
jaroslav@123
    14
 * "Classpath" exception as provided by apidesign.org
jaroslav@123
    15
 * in the License file that accompanied this code.
jaroslav@123
    16
 *
jaroslav@123
    17
 * You should have received a copy of the GNU General Public License
jaroslav@123
    18
 * along with this program. Look for COPYING file in the top folder.
jaroslav@123
    19
 * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
jaroslav@123
    20
 */
jaroslav@123
    21
package org.apidesign.html.boot.impl;
jaroslav@123
    22
jaroslav@323
    23
import java.io.Closeable;
jaroslav@163
    24
import java.io.InputStream;
jaroslav@163
    25
import java.io.InputStreamReader;
jaroslav@163
    26
import java.io.Reader;
jaroslav@123
    27
import java.net.URL;
jaroslav@123
    28
import java.util.ArrayList;
jaroslav@123
    29
import java.util.Collections;
jaroslav@123
    30
import java.util.Enumeration;
jaroslav@123
    31
import java.util.List;
jaroslav@331
    32
import java.util.Map;
jaroslav@323
    33
import java.util.concurrent.Callable;
jaroslav@123
    34
import org.apidesign.html.boot.spi.Fn;
jaroslav@323
    35
import org.objectweb.asm.AnnotationVisitor;
jaroslav@323
    36
import org.objectweb.asm.ClassReader;
jaroslav@323
    37
import org.objectweb.asm.ClassVisitor;
jaroslav@323
    38
import org.objectweb.asm.ClassWriter;
jaroslav@323
    39
import org.objectweb.asm.Label;
jaroslav@323
    40
import org.objectweb.asm.MethodVisitor;
jaroslav@323
    41
import org.objectweb.asm.Opcodes;
jaroslav@323
    42
import org.objectweb.asm.Type;
jaroslav@323
    43
import org.objectweb.asm.signature.SignatureReader;
jaroslav@323
    44
import org.objectweb.asm.signature.SignatureVisitor;
jaroslav@323
    45
import org.objectweb.asm.signature.SignatureWriter;
jaroslav@123
    46
jaroslav@123
    47
/**
jaroslav@123
    48
 *
jaroslav@123
    49
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jaroslav@123
    50
 */
jaroslav@323
    51
public final class FnUtils implements Fn.Presenter {
jaroslav@288
    52
    
jaroslav@123
    53
    private FnUtils() {
jaroslav@123
    54
    }
jaroslav@288
    55
    
jaroslav@331
    56
    /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
jaroslav@331
    57
     * in the bytecode and converts them into real code. Used by Maven plugin
jaroslav@331
    58
     * postprocessing classes.
jaroslav@331
    59
     * 
jaroslav@331
    60
     * @param bytecode the original bytecode with javascript specific annotations
jaroslav@331
    61
     * @param resources the resources to load
jaroslav@331
    62
     * @return the transformed bytecode
jaroslav@331
    63
     * @since 0.7
jaroslav@331
    64
     */
jaroslav@331
    65
    public static byte[] transform(byte[] bytecode, Map<String,InputStream> resources) {
jaroslav@331
    66
        return transform(null, bytecode);
jaroslav@331
    67
    }
jaroslav@331
    68
    
jaroslav@288
    69
    public static boolean isJavaScriptCapable(ClassLoader l) {
jaroslav@323
    70
        if (l instanceof JsClassLoader) {
jaroslav@323
    71
            return true;
jaroslav@323
    72
        }
jaroslav@323
    73
        Class<?> clazz;
jaroslav@323
    74
        try (Closeable c = Fn.activate(new FnUtils())) {
jaroslav@323
    75
            clazz = Class.forName(Test.class.getName(), true, l);
jaroslav@323
    76
            final Object is = ((Callable<?>)clazz.newInstance()).call();
jaroslav@323
    77
            return Boolean.TRUE.equals(is);
jaroslav@323
    78
        } catch (Exception ex) {
jaroslav@323
    79
            return false;
jaroslav@323
    80
        }
jaroslav@288
    81
    }
jaroslav@288
    82
    
jaroslav@288
    83
    public static boolean isValid(Fn fn) {
jaroslav@288
    84
        return fn != null && fn.isValid();
jaroslav@123
    85
    }
jaroslav@123
    86
jaroslav@128
    87
    public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
jaroslav@123
    88
        return new JsClassLoader(parent) {
jaroslav@123
    89
            @Override
jaroslav@123
    90
            protected URL findResource(String name) {
jaroslav@123
    91
                List<URL> l = res(name, true);
jaroslav@123
    92
                return l.isEmpty() ? null : l.get(0);
jaroslav@123
    93
            }
jaroslav@123
    94
            
jaroslav@123
    95
            @Override
jaroslav@123
    96
            protected Enumeration<URL> findResources(String name) {
jaroslav@123
    97
                return Collections.enumeration(res(name, false));
jaroslav@123
    98
            }
jaroslav@123
    99
            
jaroslav@123
   100
            private List<URL> res(String name, boolean oneIsEnough) {
jaroslav@123
   101
                List<URL> l = new ArrayList<URL>();
jaroslav@123
   102
                f.findResources(name, l, oneIsEnough);
jaroslav@123
   103
                return l;
jaroslav@123
   104
            }
jaroslav@123
   105
            
jaroslav@123
   106
            @Override
jaroslav@123
   107
            protected Fn defineFn(String code, String... names) {
jaroslav@123
   108
                return d.defineFn(code, names);
jaroslav@123
   109
            }
jaroslav@163
   110
jaroslav@163
   111
            @Override
jaroslav@163
   112
            protected void loadScript(Reader code) throws Exception {
jaroslav@163
   113
                d.loadScript(code);
jaroslav@163
   114
            }
jaroslav@123
   115
        };
jaroslav@123
   116
    }
jaroslav@160
   117
jaroslav@189
   118
    static String callback(final String body) {
jaroslav@184
   119
        return new JsCallback() {
jaroslav@184
   120
            @Override
jaroslav@184
   121
            protected CharSequence callMethod(
jaroslav@184
   122
                String ident, String fqn, String method, String params
jaroslav@184
   123
            ) {
jaroslav@188
   124
                StringBuilder sb = new StringBuilder();
jaroslav@188
   125
                sb.append("vm.").append(mangle(fqn, method, params));
jaroslav@191
   126
                sb.append("(");
jaroslav@191
   127
                if (ident != null) {
jaroslav@191
   128
                    sb.append(ident);
jaroslav@191
   129
                }
jaroslav@188
   130
                return sb;
jaroslav@184
   131
            }
jaroslav@184
   132
jaroslav@184
   133
        }.parse(body);
jaroslav@160
   134
    }
jaroslav@163
   135
jaroslav@323
   136
    static void loadScript(ClassLoader jcl, String resource) {
jaroslav@163
   137
        final InputStream script = jcl.getResourceAsStream(resource);
jaroslav@163
   138
        if (script == null) {
jaroslav@163
   139
            throw new NullPointerException("Can't find " + resource);
jaroslav@163
   140
        }
jaroslav@163
   141
        try {
jaroslav@163
   142
            Reader isr = null;
jaroslav@163
   143
            try {
jaroslav@163
   144
                isr = new InputStreamReader(script, "UTF-8");
jaroslav@323
   145
                FnContext.currentPresenter().loadScript(isr);
jaroslav@163
   146
            } finally {
jaroslav@163
   147
                if (isr != null) {
jaroslav@163
   148
                    isr.close();
jaroslav@163
   149
                }
jaroslav@163
   150
            }
jaroslav@163
   151
        } catch (Exception ex) {
jaroslav@163
   152
            throw new IllegalStateException("Can't execute " + resource, ex);
jaroslav@163
   153
        } 
jaroslav@163
   154
    }
jaroslav@323
   155
jaroslav@323
   156
    @Override
jaroslav@323
   157
    public Fn defineFn(String code, String... names) {
jaroslav@323
   158
        return new TrueFn();
jaroslav@323
   159
    }
jaroslav@323
   160
jaroslav@323
   161
    @Override
jaroslav@323
   162
    public void displayPage(URL page, Runnable onPageLoad) {
jaroslav@323
   163
    }
jaroslav@323
   164
jaroslav@323
   165
    @Override
jaroslav@323
   166
    public void loadScript(Reader code) throws Exception {
jaroslav@323
   167
    }
jaroslav@323
   168
    
jaroslav@323
   169
    private static final class FindInClass extends ClassVisitor {
jaroslav@323
   170
        private String name;
jaroslav@323
   171
        private int found;
jaroslav@323
   172
        private ClassLoader loader;
jaroslav@323
   173
jaroslav@323
   174
        public FindInClass(ClassLoader l, ClassVisitor cv) {
jaroslav@323
   175
            super(Opcodes.ASM4, cv);
jaroslav@323
   176
            this.loader = l;
jaroslav@323
   177
        }
jaroslav@323
   178
jaroslav@323
   179
        @Override
jaroslav@323
   180
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
jaroslav@323
   181
            this.name = name;
jaroslav@323
   182
            super.visit(version, access, name, signature, superName, interfaces);
jaroslav@323
   183
        }
jaroslav@323
   184
jaroslav@323
   185
        @Override
jaroslav@323
   186
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@323
   187
            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
jaroslav@323
   188
                return new LoadResource();
jaroslav@323
   189
            }
jaroslav@323
   190
            return super.visitAnnotation(desc, visible);
jaroslav@323
   191
        }
jaroslav@323
   192
jaroslav@323
   193
        @Override
jaroslav@323
   194
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
jaroslav@323
   195
            return new FindInMethod(access, name, desc,
jaroslav@323
   196
                    super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
jaroslav@323
   197
            );
jaroslav@323
   198
        }
jaroslav@323
   199
jaroslav@323
   200
        private final class FindInMethod extends MethodVisitor {
jaroslav@323
   201
jaroslav@323
   202
            private final String name;
jaroslav@323
   203
            private final String desc;
jaroslav@323
   204
            private final int access;
jaroslav@323
   205
            private List<String> args;
jaroslav@323
   206
            private String body;
jaroslav@323
   207
            private boolean bodyGenerated;
jaroslav@323
   208
jaroslav@323
   209
            public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
jaroslav@323
   210
                super(Opcodes.ASM4, mv);
jaroslav@323
   211
                this.access = access;
jaroslav@323
   212
                this.name = name;
jaroslav@323
   213
                this.desc = desc;
jaroslav@323
   214
            }
jaroslav@323
   215
jaroslav@323
   216
            @Override
jaroslav@323
   217
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@323
   218
                if ("Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
jaroslav@323
   219
                        || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
jaroslav@323
   220
                        ) {
jaroslav@323
   221
                    found++;
jaroslav@323
   222
                    return new FindInAnno();
jaroslav@323
   223
                }
jaroslav@323
   224
                return super.visitAnnotation(desc, visible);
jaroslav@323
   225
            }
jaroslav@323
   226
jaroslav@323
   227
            private void generateJSBody(List<String> args, String body) {
jaroslav@323
   228
                this.args = args;
jaroslav@323
   229
                this.body = body;
jaroslav@323
   230
            }
jaroslav@323
   231
jaroslav@323
   232
            @Override
jaroslav@323
   233
            public void visitCode() {
jaroslav@323
   234
                if (body == null) {
jaroslav@323
   235
                    return;
jaroslav@323
   236
                }
jaroslav@323
   237
                generateBody();
jaroslav@323
   238
            }
jaroslav@323
   239
jaroslav@323
   240
            private boolean generateBody() {
jaroslav@323
   241
                if (bodyGenerated) {
jaroslav@323
   242
                    return false;
jaroslav@323
   243
                }
jaroslav@323
   244
                bodyGenerated = true;
jaroslav@323
   245
jaroslav@323
   246
                super.visitFieldInsn(
jaroslav@323
   247
                        Opcodes.GETSTATIC, FindInClass.this.name,
jaroslav@323
   248
                        "$$fn$$" + name + "_" + found,
jaroslav@323
   249
                        "Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@323
   250
                );
jaroslav@323
   251
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   252
                super.visitMethodInsn(
jaroslav@323
   253
                        Opcodes.INVOKESTATIC,
jaroslav@323
   254
                        "org/apidesign/html/boot/spi/Fn", "isValid",
jaroslav@323
   255
                        "(Lorg/apidesign/html/boot/spi/Fn;)Z"
jaroslav@323
   256
                );
jaroslav@323
   257
                Label ifNotNull = new Label();
jaroslav@323
   258
                super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
jaroslav@323
   259
jaroslav@323
   260
                // init Fn
jaroslav@323
   261
                super.visitInsn(Opcodes.POP);
jaroslav@323
   262
                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jaroslav@323
   263
                super.visitLdcInsn(body);
jaroslav@323
   264
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   265
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
jaroslav@323
   266
                boolean needsVM = false;
jaroslav@323
   267
                for (int i = 0; i < args.size(); i++) {
jaroslav@323
   268
                    assert !needsVM;
jaroslav@323
   269
                    String argName = args.get(i);
jaroslav@323
   270
                    needsVM = "vm".equals(argName);
jaroslav@323
   271
                    super.visitInsn(Opcodes.DUP);
jaroslav@323
   272
                    super.visitIntInsn(Opcodes.BIPUSH, i);
jaroslav@323
   273
                    super.visitLdcInsn(argName);
jaroslav@323
   274
                    super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   275
                }
jaroslav@323
   276
                super.visitMethodInsn(Opcodes.INVOKESTATIC,
jaroslav@323
   277
                        "org/apidesign/html/boot/spi/Fn", "define",
jaroslav@323
   278
                        "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@323
   279
                );
jaroslav@323
   280
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   281
                super.visitFieldInsn(
jaroslav@323
   282
                        Opcodes.PUTSTATIC, FindInClass.this.name,
jaroslav@323
   283
                        "$$fn$$" + name + "_" + found,
jaroslav@323
   284
                        "Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@323
   285
                );
jaroslav@323
   286
                // end of Fn init
jaroslav@323
   287
jaroslav@323
   288
                super.visitLabel(ifNotNull);
jaroslav@323
   289
jaroslav@323
   290
                final int offset;
jaroslav@323
   291
                if ((access & Opcodes.ACC_STATIC) == 0) {
jaroslav@323
   292
                    offset = 1;
jaroslav@323
   293
                    super.visitIntInsn(Opcodes.ALOAD, 0);
jaroslav@323
   294
                } else {
jaroslav@323
   295
                    offset = 0;
jaroslav@323
   296
                    super.visitInsn(Opcodes.ACONST_NULL);
jaroslav@323
   297
                }
jaroslav@323
   298
jaroslav@323
   299
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   300
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
jaroslav@323
   301
jaroslav@323
   302
                class SV extends SignatureVisitor {
jaroslav@323
   303
jaroslav@323
   304
                    private boolean nowReturn;
jaroslav@323
   305
                    private Type returnType;
jaroslav@323
   306
                    private int index;
jaroslav@323
   307
                    private int loadIndex = offset;
jaroslav@323
   308
jaroslav@323
   309
                    public SV() {
jaroslav@323
   310
                        super(Opcodes.ASM4);
jaroslav@323
   311
                    }
jaroslav@323
   312
jaroslav@323
   313
                    @Override
jaroslav@323
   314
                    public void visitBaseType(char descriptor) {
jaroslav@323
   315
                        final Type t = Type.getType("" + descriptor);
jaroslav@323
   316
                        if (nowReturn) {
jaroslav@323
   317
                            returnType = t;
jaroslav@323
   318
                            return;
jaroslav@323
   319
                        }
jaroslav@323
   320
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   321
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   322
                        FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
jaroslav@323
   323
                        String factory;
jaroslav@323
   324
                        switch (descriptor) {
jaroslav@323
   325
                            case 'I':
jaroslav@323
   326
                                factory = "java/lang/Integer";
jaroslav@323
   327
                                break;
jaroslav@323
   328
                            case 'J':
jaroslav@323
   329
                                factory = "java/lang/Long";
jaroslav@323
   330
                                loadIndex++;
jaroslav@323
   331
                                break;
jaroslav@323
   332
                            case 'S':
jaroslav@323
   333
                                factory = "java/lang/Short";
jaroslav@323
   334
                                break;
jaroslav@323
   335
                            case 'F':
jaroslav@323
   336
                                factory = "java/lang/Float";
jaroslav@323
   337
                                break;
jaroslav@323
   338
                            case 'D':
jaroslav@323
   339
                                factory = "java/lang/Double";
jaroslav@323
   340
                                loadIndex++;
jaroslav@323
   341
                                break;
jaroslav@323
   342
                            case 'Z':
jaroslav@323
   343
                                factory = "java/lang/Boolean";
jaroslav@323
   344
                                break;
jaroslav@323
   345
                            case 'C':
jaroslav@323
   346
                                factory = "java/lang/Character";
jaroslav@323
   347
                                break;
jaroslav@323
   348
                            case 'B':
jaroslav@323
   349
                                factory = "java/lang/Byte";
jaroslav@323
   350
                                break;
jaroslav@323
   351
                            default:
jaroslav@323
   352
                                throw new IllegalStateException(t.toString());
jaroslav@323
   353
                        }
jaroslav@323
   354
                        FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
jaroslav@323
   355
                                factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
jaroslav@323
   356
                        );
jaroslav@323
   357
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   358
                    }
jaroslav@323
   359
jaroslav@323
   360
                    @Override
jaroslav@323
   361
                    public SignatureVisitor visitArrayType() {
jaroslav@323
   362
                        if (nowReturn) {
jaroslav@323
   363
                            throw new IllegalStateException("Not supported yet");
jaroslav@323
   364
                        }
jaroslav@323
   365
                        loadObject();
jaroslav@323
   366
                        return new SignatureWriter();
jaroslav@323
   367
                    }
jaroslav@323
   368
jaroslav@323
   369
                    @Override
jaroslav@323
   370
                    public void visitClassType(String name) {
jaroslav@323
   371
                        if (nowReturn) {
jaroslav@323
   372
                            returnType = Type.getObjectType(name);
jaroslav@323
   373
                            return;
jaroslav@323
   374
                        }
jaroslav@323
   375
                        loadObject();
jaroslav@323
   376
                    }
jaroslav@323
   377
jaroslav@323
   378
                    @Override
jaroslav@323
   379
                    public SignatureVisitor visitReturnType() {
jaroslav@323
   380
                        nowReturn = true;
jaroslav@323
   381
                        return this;
jaroslav@323
   382
                    }
jaroslav@323
   383
jaroslav@323
   384
                    private void loadObject() {
jaroslav@323
   385
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   386
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   387
                        FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
jaroslav@323
   388
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   389
                    }
jaroslav@323
   390
jaroslav@323
   391
                }
jaroslav@323
   392
                SV sv = new SV();
jaroslav@323
   393
                SignatureReader sr = new SignatureReader(desc);
jaroslav@323
   394
                sr.accept(sv);
jaroslav@323
   395
jaroslav@323
   396
                if (needsVM) {
jaroslav@323
   397
                    FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   398
                    FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
jaroslav@323
   399
                    int lastSlash = FindInClass.this.name.lastIndexOf('/');
jaroslav@323
   400
                    String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
jaroslav@323
   401
                    FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
jaroslav@323
   402
                    FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
jaroslav@323
   403
                    FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   404
                }
jaroslav@323
   405
jaroslav@323
   406
                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@323
   407
                        "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
jaroslav@323
   408
                );
jaroslav@323
   409
                switch (sv.returnType.getSort()) {
jaroslav@323
   410
                    case Type.VOID:
jaroslav@323
   411
                        super.visitInsn(Opcodes.RETURN);
jaroslav@323
   412
                        break;
jaroslav@323
   413
                    case Type.ARRAY:
jaroslav@323
   414
                    case Type.OBJECT:
jaroslav@323
   415
                        super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
jaroslav@323
   416
                        super.visitInsn(Opcodes.ARETURN);
jaroslav@323
   417
                        break;
jaroslav@323
   418
                    case Type.BOOLEAN:
jaroslav@323
   419
                        super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
jaroslav@323
   420
                        super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@323
   421
                                "java/lang/Boolean", "booleanValue", "()Z"
jaroslav@323
   422
                        );
jaroslav@323
   423
                        super.visitInsn(Opcodes.IRETURN);
jaroslav@323
   424
                        break;
jaroslav@323
   425
                    default:
jaroslav@323
   426
                        super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
jaroslav@323
   427
                        super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@323
   428
                                "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
jaroslav@323
   429
                        );
jaroslav@323
   430
                        super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
jaroslav@323
   431
                }
jaroslav@323
   432
                return true;
jaroslav@323
   433
            }
jaroslav@323
   434
jaroslav@323
   435
            @Override
jaroslav@323
   436
            public void visitEnd() {
jaroslav@323
   437
                super.visitEnd();
jaroslav@323
   438
                if (body != null) {
jaroslav@323
   439
                    if (generateBody()) {
jaroslav@323
   440
                        // native method
jaroslav@323
   441
                        super.visitMaxs(1, 0);
jaroslav@323
   442
                    }
jaroslav@323
   443
                    FindInClass.this.visitField(
jaroslav@323
   444
                            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
jaroslav@323
   445
                            "$$fn$$" + name + "_" + found,
jaroslav@323
   446
                            "Lorg/apidesign/html/boot/spi/Fn;",
jaroslav@323
   447
                            null, null
jaroslav@323
   448
                    );
jaroslav@323
   449
                }
jaroslav@323
   450
            }
jaroslav@323
   451
jaroslav@323
   452
            private final class FindInAnno extends AnnotationVisitor {
jaroslav@323
   453
jaroslav@323
   454
                private List<String> args = new ArrayList<String>();
jaroslav@323
   455
                private String body;
jaroslav@323
   456
                private boolean javacall = false;
jaroslav@323
   457
jaroslav@323
   458
                public FindInAnno() {
jaroslav@323
   459
                    super(Opcodes.ASM4);
jaroslav@323
   460
                }
jaroslav@323
   461
jaroslav@323
   462
                @Override
jaroslav@323
   463
                public void visit(String name, Object value) {
jaroslav@323
   464
                    if (name == null) {
jaroslav@323
   465
                        args.add((String) value);
jaroslav@323
   466
                        return;
jaroslav@323
   467
                    }
jaroslav@323
   468
                    if (name.equals("javacall")) { // NOI18N
jaroslav@323
   469
                        javacall = (Boolean) value;
jaroslav@323
   470
                        return;
jaroslav@323
   471
                    }
jaroslav@323
   472
                    assert name.equals("body");
jaroslav@323
   473
                    body = (String) value;
jaroslav@323
   474
                }
jaroslav@323
   475
jaroslav@323
   476
                @Override
jaroslav@323
   477
                public AnnotationVisitor visitArray(String name) {
jaroslav@323
   478
                    return this;
jaroslav@323
   479
                }
jaroslav@323
   480
jaroslav@323
   481
                @Override
jaroslav@323
   482
                public void visitEnd() {
jaroslav@323
   483
                    if (body != null) {
jaroslav@323
   484
                        if (javacall) {
jaroslav@323
   485
                            body = callback(body);
jaroslav@323
   486
                            args.add("vm");
jaroslav@323
   487
                        }
jaroslav@323
   488
                        generateJSBody(args, body);
jaroslav@323
   489
                    }
jaroslav@323
   490
                }
jaroslav@323
   491
            }
jaroslav@323
   492
        }
jaroslav@323
   493
jaroslav@323
   494
        private final class LoadResource extends AnnotationVisitor {
jaroslav@323
   495
jaroslav@323
   496
            public LoadResource() {
jaroslav@323
   497
                super(Opcodes.ASM4);
jaroslav@323
   498
            }
jaroslav@323
   499
jaroslav@323
   500
            @Override
jaroslav@323
   501
            public void visit(String attrName, Object value) {
jaroslav@323
   502
                String relPath = (String) value;
jaroslav@323
   503
                if (relPath.startsWith("/")) {
jaroslav@323
   504
                    loadScript(loader, relPath);
jaroslav@323
   505
                } else {
jaroslav@323
   506
                    int last = name.lastIndexOf('/');
jaroslav@323
   507
                    String fullPath = name.substring(0, last + 1) + relPath;
jaroslav@323
   508
                    loadScript(loader, fullPath);
jaroslav@323
   509
                }
jaroslav@323
   510
            }
jaroslav@323
   511
        }
jaroslav@323
   512
    }
jaroslav@323
   513
jaroslav@323
   514
    private static class ClassWriterEx extends ClassWriter {
jaroslav@323
   515
jaroslav@323
   516
        private ClassLoader loader;
jaroslav@323
   517
jaroslav@323
   518
        public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
jaroslav@323
   519
            super(classReader, flags);
jaroslav@323
   520
            this.loader = l;
jaroslav@323
   521
        }
jaroslav@323
   522
jaroslav@323
   523
        @Override
jaroslav@323
   524
        protected String getCommonSuperClass(final String type1, final String type2) {
jaroslav@323
   525
            Class<?> c, d;
jaroslav@323
   526
            try {
jaroslav@323
   527
                c = Class.forName(type1.replace('/', '.'), false, loader);
jaroslav@323
   528
                d = Class.forName(type2.replace('/', '.'), false, loader);
jaroslav@323
   529
            } catch (Exception e) {
jaroslav@323
   530
                throw new RuntimeException(e.toString());
jaroslav@323
   531
            }
jaroslav@323
   532
            if (c.isAssignableFrom(d)) {
jaroslav@323
   533
                return type1;
jaroslav@323
   534
            }
jaroslav@323
   535
            if (d.isAssignableFrom(c)) {
jaroslav@323
   536
                return type2;
jaroslav@323
   537
            }
jaroslav@323
   538
            if (c.isInterface() || d.isInterface()) {
jaroslav@323
   539
                return "java/lang/Object";
jaroslav@323
   540
            } else {
jaroslav@323
   541
                do {
jaroslav@323
   542
                    c = c.getSuperclass();
jaroslav@323
   543
                } while (!c.isAssignableFrom(d));
jaroslav@323
   544
                return c.getName().replace('.', '/');
jaroslav@323
   545
            }
jaroslav@323
   546
        }
jaroslav@323
   547
    }
jaroslav@323
   548
jaroslav@323
   549
    static byte[] transform(ClassLoader loader, byte[] arr) {
jaroslav@323
   550
        ClassReader cr = new ClassReader(arr) {
jaroslav@323
   551
            // to allow us to compile with -profile compact1 on 
jaroslav@323
   552
            // JDK8 while processing the class as JDK7, the highest
jaroslav@323
   553
            // class format asm 4.1 understands to
jaroslav@323
   554
            @Override
jaroslav@323
   555
            public short readShort(int index) {
jaroslav@323
   556
                short s = super.readShort(index);
jaroslav@323
   557
                if (index == 6 && s > Opcodes.V1_7) {
jaroslav@323
   558
                    return Opcodes.V1_7;
jaroslav@323
   559
                }
jaroslav@323
   560
                return s;
jaroslav@323
   561
            }
jaroslav@323
   562
        };
jaroslav@323
   563
        FindInClass tst = new FindInClass(loader, null);
jaroslav@323
   564
        cr.accept(tst, 0);
jaroslav@323
   565
        if (tst.found > 0) {
jaroslav@323
   566
            ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
jaroslav@323
   567
            FindInClass fic = new FindInClass(loader, w);
jaroslav@323
   568
            cr.accept(fic, 0);
jaroslav@323
   569
            arr = w.toByteArray();
jaroslav@323
   570
        }
jaroslav@323
   571
        return arr;
jaroslav@323
   572
    }
jaroslav@323
   573
jaroslav@323
   574
    private static final class TrueFn extends Fn {
jaroslav@323
   575
        @Override
jaroslav@323
   576
        public Object invoke(Object thiz, Object... args) throws Exception {
jaroslav@323
   577
            return Boolean.TRUE;
jaroslav@323
   578
        }
jaroslav@323
   579
    } // end of TrueFn
jaroslav@123
   580
}