boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 10 Oct 2013 14:02:18 +0200
changeset 309 7025177bd67e
parent 288 8c5b40231d26
child 323 86aabecda7a3
permissions -rw-r--r--
FnUtils are bloated and contain references to Asm classes. Separate the code needed by generated JsCallbacks into own class
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@123
    23
import org.apidesign.html.boot.spi.Fn;
jaroslav@123
    24
import java.io.IOException;
jaroslav@123
    25
import java.io.InputStream;
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.Enumeration;
jaroslav@123
    30
import java.util.List;
jaroslav@123
    31
import org.objectweb.asm.AnnotationVisitor;
jaroslav@123
    32
import org.objectweb.asm.ClassReader;
jaroslav@123
    33
import org.objectweb.asm.ClassVisitor;
jaroslav@123
    34
import org.objectweb.asm.ClassWriter;
jaroslav@123
    35
import org.objectweb.asm.Label;
jaroslav@123
    36
import org.objectweb.asm.MethodVisitor;
jaroslav@123
    37
import org.objectweb.asm.Opcodes;
jaroslav@123
    38
import org.objectweb.asm.Type;
jaroslav@123
    39
import org.objectweb.asm.signature.SignatureReader;
jaroslav@123
    40
import org.objectweb.asm.signature.SignatureVisitor;
jaroslav@161
    41
import org.objectweb.asm.signature.SignatureWriter;
jaroslav@123
    42
jaroslav@123
    43
/** 
jaroslav@123
    44
 *
jaroslav@123
    45
 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
jaroslav@123
    46
 */
jaroslav@123
    47
abstract class JsClassLoader extends ClassLoader {
jaroslav@123
    48
    JsClassLoader(ClassLoader parent) {
jaroslav@123
    49
        super(parent);
jaroslav@143
    50
        setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
jaroslav@123
    51
    }
jaroslav@123
    52
    
jaroslav@123
    53
    @Override
jaroslav@123
    54
    protected abstract URL findResource(String name);
jaroslav@123
    55
    
jaroslav@123
    56
    @Override
jaroslav@123
    57
    protected abstract Enumeration<URL> findResources(String name);
jaroslav@123
    58
jaroslav@123
    59
    @Override
jaroslav@123
    60
    protected Class<?> findClass(String name) throws ClassNotFoundException {
jaroslav@123
    61
        if (name.startsWith("javafx")) {
jaroslav@123
    62
            return Class.forName(name);
jaroslav@123
    63
        }
jaroslav@123
    64
        if (name.startsWith("netscape")) {
jaroslav@123
    65
            return Class.forName(name);
jaroslav@123
    66
        }
jaroslav@123
    67
        if (name.startsWith("com.sun")) {
jaroslav@123
    68
            return Class.forName(name);
jaroslav@123
    69
        }
jaroslav@123
    70
        if (name.equals(JsClassLoader.class.getName())) {
jaroslav@123
    71
            return JsClassLoader.class;
jaroslav@123
    72
        }
jaroslav@123
    73
        if (name.equals(Fn.class.getName())) {
jaroslav@123
    74
            return Fn.class;
jaroslav@123
    75
        }
jaroslav@288
    76
        if (name.equals(Fn.Presenter.class.getName())) {
jaroslav@288
    77
            return Fn.Presenter.class;
jaroslav@288
    78
        }
jaroslav@124
    79
        if (name.equals(FnUtils.class.getName())) {
jaroslav@124
    80
            return FnUtils.class;
jaroslav@124
    81
        }
jaroslav@309
    82
        if (
jaroslav@309
    83
            name.equals("org.apidesign.html.boot.spi.Fn") ||
jaroslav@309
    84
            name.equals("org.apidesign.html.boot.impl.FnUtils") ||
jaroslav@309
    85
            name.equals("org.apidesign.html.boot.impl.FnContext")
jaroslav@309
    86
        ) {
jaroslav@309
    87
            return Class.forName(name);
jaroslav@309
    88
        }
jaroslav@123
    89
        URL u = findResource(name.replace('.', '/') + ".class");
jaroslav@123
    90
        if (u != null) {
jaroslav@123
    91
            InputStream is = null;
jaroslav@123
    92
            try {
jaroslav@123
    93
                is = u.openStream();
jaroslav@123
    94
                byte[] arr = new byte[is.available()];
jaroslav@123
    95
                int len = 0;
jaroslav@123
    96
                while (len < arr.length) {
jaroslav@123
    97
                    int read = is.read(arr, len, arr.length - len);
jaroslav@123
    98
                    if (read == -1) {
jaroslav@123
    99
                        throw new IOException("Can't read " + u);
jaroslav@123
   100
                    }
jaroslav@123
   101
                    len += read;
jaroslav@123
   102
                }
jaroslav@123
   103
                is.close();
jaroslav@123
   104
                is = null;
jaroslav@283
   105
                ClassReader cr = new ClassReader(arr) {
jaroslav@283
   106
                    // to allow us to compile with -profile compact1 on 
jaroslav@283
   107
                    // JDK8 while processing the class as JDK7, the highest
jaroslav@283
   108
                    // class format asm 4.1 understands to
jaroslav@283
   109
                    @Override
jaroslav@283
   110
                    public short readShort(int index) {
jaroslav@283
   111
                        short s = super.readShort(index);
jaroslav@283
   112
                        if (index == 6 && s > Opcodes.V1_7) {
jaroslav@283
   113
                            return Opcodes.V1_7;
jaroslav@283
   114
                        }
jaroslav@283
   115
                        return s;
jaroslav@283
   116
                    }
jaroslav@283
   117
                };
jaroslav@189
   118
                FindInClass tst = new FindInClass(null);
jaroslav@123
   119
                cr.accept(tst, 0);
jaroslav@123
   120
                if (tst.found > 0) {
jaroslav@123
   121
                    ClassWriter w = new ClassWriterEx(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
jaroslav@189
   122
                    FindInClass fic = new FindInClass(w);
jaroslav@123
   123
                    cr.accept(fic, 0);
jaroslav@123
   124
                    arr = w.toByteArray();
jaroslav@123
   125
                }
jaroslav@123
   126
                if (arr != null) {
jaroslav@123
   127
                    return defineClass(name, arr, 0, arr.length);
jaroslav@123
   128
                }
jaroslav@123
   129
            } catch (IOException ex) {
jaroslav@123
   130
                throw new ClassNotFoundException("Can't load " + name, ex);
jaroslav@123
   131
            } finally {
jaroslav@123
   132
                try {
jaroslav@123
   133
                    if (is != null) is.close();
jaroslav@123
   134
                } catch (IOException ex) {
jaroslav@123
   135
                    throw new ClassNotFoundException(null, ex);
jaroslav@123
   136
                }
jaroslav@123
   137
            }
jaroslav@123
   138
        }
jaroslav@123
   139
        return super.findClass(name);
jaroslav@123
   140
    }
jaroslav@123
   141
    
jaroslav@123
   142
    protected abstract Fn defineFn(String code, String... names);
jaroslav@163
   143
    protected abstract void loadScript(Reader code) throws Exception;
jaroslav@123
   144
    
jaroslav@160
   145
    private final class FindInClass extends ClassVisitor {
jaroslav@123
   146
        private String name;
jaroslav@123
   147
        private int found;
jaroslav@123
   148
        
jaroslav@189
   149
        public FindInClass(ClassVisitor cv) {
jaroslav@123
   150
            super(Opcodes.ASM4, cv);
jaroslav@123
   151
        }
jaroslav@123
   152
jaroslav@123
   153
        @Override
jaroslav@123
   154
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
jaroslav@123
   155
            this.name = name;
jaroslav@123
   156
            super.visit(version, access, name, signature, superName, interfaces);
jaroslav@123
   157
        }
jaroslav@163
   158
jaroslav@163
   159
        @Override
jaroslav@163
   160
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@163
   161
            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
jaroslav@163
   162
                return new LoadResource();
jaroslav@163
   163
            }
jaroslav@163
   164
            return super.visitAnnotation(desc, visible);
jaroslav@163
   165
        }
jaroslav@123
   166
        
jaroslav@123
   167
jaroslav@123
   168
        @Override
jaroslav@123
   169
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
jaroslav@123
   170
            return new FindInMethod(access, name, desc,
jaroslav@123
   171
                super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
jaroslav@123
   172
            );
jaroslav@123
   173
        }
jaroslav@123
   174
        
jaroslav@123
   175
        private final class FindInMethod extends MethodVisitor {
jaroslav@123
   176
            private final String name;
jaroslav@123
   177
            private final String desc;
jaroslav@123
   178
            private final int access;
jaroslav@123
   179
            private List<String> args;
jaroslav@123
   180
            private String body;
jaroslav@123
   181
            private boolean bodyGenerated;
jaroslav@123
   182
            
jaroslav@123
   183
            public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
jaroslav@123
   184
                super(Opcodes.ASM4, mv);
jaroslav@123
   185
                this.access = access;
jaroslav@123
   186
                this.name = name;
jaroslav@123
   187
                this.desc = desc;
jaroslav@123
   188
            }
jaroslav@123
   189
jaroslav@123
   190
            @Override
jaroslav@123
   191
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@132
   192
                if (
jaroslav@132
   193
                    "Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
jaroslav@132
   194
                    || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
jaroslav@132
   195
                ) {
jaroslav@123
   196
                    found++;
jaroslav@123
   197
                    return new FindInAnno();
jaroslav@123
   198
                }
jaroslav@123
   199
                return super.visitAnnotation(desc, visible);
jaroslav@123
   200
            }
jaroslav@123
   201
jaroslav@123
   202
            private void generateJSBody(List<String> args, String body) {
jaroslav@123
   203
                this.args = args;
jaroslav@123
   204
                this.body = body;
jaroslav@123
   205
            }
jaroslav@123
   206
            
jaroslav@123
   207
            @Override
jaroslav@123
   208
            public void visitCode() {
jaroslav@123
   209
                if (body == null) {
jaroslav@123
   210
                    return;
jaroslav@123
   211
                } 
jaroslav@123
   212
                generateBody();
jaroslav@123
   213
            }
jaroslav@123
   214
            
jaroslav@123
   215
            private boolean generateBody() {
jaroslav@123
   216
                if (bodyGenerated) {
jaroslav@123
   217
                    return false;
jaroslav@123
   218
                }
jaroslav@123
   219
                bodyGenerated = true;
jaroslav@123
   220
                
jaroslav@123
   221
                super.visitFieldInsn(
jaroslav@123
   222
                    Opcodes.GETSTATIC, FindInClass.this.name, 
jaroslav@123
   223
                    "$$fn$$" + name + "_" + found, 
jaroslav@123
   224
                    "Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@123
   225
                );
jaroslav@123
   226
                super.visitInsn(Opcodes.DUP);
jaroslav@288
   227
                super.visitMethodInsn(
jaroslav@288
   228
                    Opcodes.INVOKESTATIC, 
jaroslav@288
   229
                    "org/apidesign/html/boot/impl/FnUtils", "isValid", 
jaroslav@288
   230
                    "(Lorg/apidesign/html/boot/spi/Fn;)Z"
jaroslav@288
   231
                );
jaroslav@123
   232
                Label ifNotNull = new Label();
jaroslav@288
   233
                super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
jaroslav@123
   234
                
jaroslav@123
   235
                // init Fn
jaroslav@123
   236
                super.visitInsn(Opcodes.POP);
jaroslav@123
   237
                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jaroslav@123
   238
                super.visitLdcInsn(body);
jaroslav@123
   239
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@123
   240
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
jaroslav@188
   241
                boolean needsVM = false;
jaroslav@123
   242
                for (int i = 0; i < args.size(); i++) {
jaroslav@188
   243
                    assert !needsVM;
jaroslav@188
   244
                    String argName = args.get(i);
jaroslav@188
   245
                    needsVM = "vm".equals(argName);
jaroslav@123
   246
                    super.visitInsn(Opcodes.DUP);
jaroslav@123
   247
                    super.visitIntInsn(Opcodes.BIPUSH, i);
jaroslav@188
   248
                    super.visitLdcInsn(argName);
jaroslav@123
   249
                    super.visitInsn(Opcodes.AASTORE);
jaroslav@123
   250
                }
jaroslav@123
   251
                super.visitMethodInsn(Opcodes.INVOKESTATIC, 
jaroslav@124
   252
                    "org/apidesign/html/boot/impl/FnUtils", "define", 
jaroslav@123
   253
                    "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@123
   254
                );
jaroslav@271
   255
                super.visitInsn(Opcodes.DUP);
jaroslav@271
   256
                super.visitFieldInsn(
jaroslav@271
   257
                    Opcodes.PUTSTATIC, FindInClass.this.name, 
jaroslav@271
   258
                    "$$fn$$" + name + "_" + found, 
jaroslav@271
   259
                    "Lorg/apidesign/html/boot/spi/Fn;"
jaroslav@271
   260
                );
jaroslav@123
   261
                // end of Fn init
jaroslav@123
   262
                
jaroslav@123
   263
                super.visitLabel(ifNotNull);
jaroslav@123
   264
                
jaroslav@123
   265
                final int offset;
jaroslav@123
   266
                if ((access & Opcodes.ACC_STATIC) == 0) {
jaroslav@123
   267
                    offset = 1;
jaroslav@123
   268
                    super.visitIntInsn(Opcodes.ALOAD, 0);
jaroslav@123
   269
                } else {
jaroslav@123
   270
                    offset = 0;
jaroslav@123
   271
                    super.visitInsn(Opcodes.ACONST_NULL);
jaroslav@123
   272
                }
jaroslav@123
   273
                
jaroslav@123
   274
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@123
   275
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
jaroslav@123
   276
                
jaroslav@123
   277
                class SV extends SignatureVisitor {
jaroslav@123
   278
                    private boolean nowReturn;
jaroslav@123
   279
                    private Type returnType;
jaroslav@123
   280
                    private int index;
jaroslav@192
   281
                    private int loadIndex = offset;
jaroslav@123
   282
                    
jaroslav@123
   283
                    public SV() {
jaroslav@123
   284
                        super(Opcodes.ASM4);
jaroslav@123
   285
                    }
jaroslav@123
   286
                    
jaroslav@123
   287
                    @Override
jaroslav@123
   288
                    public void visitBaseType(char descriptor) {
jaroslav@123
   289
                        final Type t = Type.getType("" + descriptor);
jaroslav@123
   290
                        if (nowReturn) {
jaroslav@123
   291
                            returnType = t;
jaroslav@123
   292
                            return;
jaroslav@123
   293
                        }
jaroslav@123
   294
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@192
   295
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@192
   296
                        FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
jaroslav@123
   297
                        String factory;
jaroslav@123
   298
                        switch (descriptor) {
jaroslav@123
   299
                        case 'I': factory = "java/lang/Integer"; break;
jaroslav@192
   300
                        case 'J': factory = "java/lang/Long"; loadIndex++; break;
jaroslav@123
   301
                        case 'S': factory = "java/lang/Short"; break;
jaroslav@123
   302
                        case 'F': factory = "java/lang/Float"; break;
jaroslav@192
   303
                        case 'D': factory = "java/lang/Double"; loadIndex++; break;
jaroslav@123
   304
                        case 'Z': factory = "java/lang/Boolean"; break;
jaroslav@123
   305
                        case 'C': factory = "java/lang/Character"; break;
jaroslav@123
   306
                        case 'B': factory = "java/lang/Byte"; break;
jaroslav@123
   307
                        default: throw new IllegalStateException(t.toString());
jaroslav@123
   308
                        }
jaroslav@123
   309
                        FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
jaroslav@123
   310
                            factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
jaroslav@123
   311
                        );
jaroslav@123
   312
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@123
   313
                    }
jaroslav@123
   314
jaroslav@123
   315
                    @Override
jaroslav@161
   316
                    public SignatureVisitor visitArrayType() {
jaroslav@161
   317
                        if (nowReturn) {
jaroslav@161
   318
                            throw new IllegalStateException("Not supported yet");
jaroslav@161
   319
                        }
jaroslav@161
   320
                        loadObject();
jaroslav@161
   321
                        return new SignatureWriter();
jaroslav@161
   322
                    }
jaroslav@161
   323
jaroslav@161
   324
                    @Override
jaroslav@123
   325
                    public void visitClassType(String name) {
jaroslav@123
   326
                        if (nowReturn) {
jaroslav@123
   327
                            returnType = Type.getObjectType(name);
jaroslav@123
   328
                            return;
jaroslav@123
   329
                        }
jaroslav@161
   330
                        loadObject();
jaroslav@123
   331
                    }
jaroslav@123
   332
jaroslav@123
   333
                    @Override
jaroslav@123
   334
                    public SignatureVisitor visitReturnType() {
jaroslav@123
   335
                        nowReturn = true;
jaroslav@123
   336
                        return this;
jaroslav@123
   337
                    }
jaroslav@161
   338
jaroslav@161
   339
                    private void loadObject() {
jaroslav@161
   340
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@192
   341
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@192
   342
                        FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
jaroslav@161
   343
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@161
   344
                    }
jaroslav@123
   345
                    
jaroslav@123
   346
                }
jaroslav@123
   347
                SV sv = new SV();
jaroslav@123
   348
                SignatureReader sr = new SignatureReader(desc);
jaroslav@123
   349
                sr.accept(sv);
jaroslav@123
   350
                
jaroslav@188
   351
                if (needsVM) {
jaroslav@188
   352
                    FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@188
   353
                    FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
jaroslav@188
   354
                    int lastSlash = FindInClass.this.name.lastIndexOf('/');
jaroslav@188
   355
                    String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
jaroslav@188
   356
                    FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
jaroslav@288
   357
                    FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
jaroslav@188
   358
                    FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@188
   359
                }
jaroslav@188
   360
                
jaroslav@123
   361
                super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
jaroslav@123
   362
                    "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
jaroslav@123
   363
                );
jaroslav@123
   364
                switch (sv.returnType.getSort()) {
jaroslav@123
   365
                case Type.VOID: 
jaroslav@123
   366
                    super.visitInsn(Opcodes.RETURN);
jaroslav@123
   367
                    break;
jaroslav@123
   368
                case Type.ARRAY:
jaroslav@123
   369
                case Type.OBJECT:
jaroslav@123
   370
                    super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
jaroslav@123
   371
                    super.visitInsn(Opcodes.ARETURN);
jaroslav@123
   372
                    break;
jaroslav@153
   373
                case Type.BOOLEAN:
jaroslav@153
   374
                    super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
jaroslav@153
   375
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
jaroslav@153
   376
                        "java/lang/Boolean", "booleanValue", "()Z"
jaroslav@153
   377
                    );
jaroslav@153
   378
                    super.visitInsn(Opcodes.IRETURN);
jaroslav@153
   379
                    break;
jaroslav@123
   380
                default:
jaroslav@123
   381
                    super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
jaroslav@123
   382
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 
jaroslav@123
   383
                        "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
jaroslav@123
   384
                    );
jaroslav@123
   385
                    super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
jaroslav@123
   386
                }
jaroslav@123
   387
                return true;
jaroslav@123
   388
            }
jaroslav@123
   389
jaroslav@123
   390
            @Override
jaroslav@123
   391
            public void visitEnd() {
jaroslav@123
   392
                super.visitEnd();
jaroslav@123
   393
                if (body != null) {
jaroslav@123
   394
                    if (generateBody()) {
jaroslav@123
   395
                        // native method
jaroslav@123
   396
                        super.visitMaxs(1, 0);
jaroslav@123
   397
                    }
jaroslav@123
   398
                    FindInClass.this.visitField(
jaroslav@123
   399
                        Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 
jaroslav@123
   400
                        "$$fn$$" + name + "_" + found, 
jaroslav@123
   401
                        "Lorg/apidesign/html/boot/spi/Fn;", 
jaroslav@123
   402
                        null, null
jaroslav@123
   403
                    );
jaroslav@123
   404
                }
jaroslav@123
   405
            }
jaroslav@123
   406
            
jaroslav@123
   407
            
jaroslav@123
   408
            
jaroslav@123
   409
            
jaroslav@123
   410
        
jaroslav@123
   411
            private final class FindInAnno extends AnnotationVisitor {
jaroslav@123
   412
                private List<String> args = new ArrayList<String>();
jaroslav@123
   413
                private String body;
jaroslav@160
   414
                private boolean javacall = false;
jaroslav@123
   415
jaroslav@123
   416
                public FindInAnno() {
jaroslav@123
   417
                    super(Opcodes.ASM4);
jaroslav@123
   418
                }
jaroslav@123
   419
jaroslav@123
   420
                @Override
jaroslav@123
   421
                public void visit(String name, Object value) {
jaroslav@123
   422
                    if (name == null) {
jaroslav@123
   423
                        args.add((String) value);
jaroslav@123
   424
                        return;
jaroslav@123
   425
                    }
jaroslav@160
   426
                    if (name.equals("javacall")) { // NOI18N
jaroslav@160
   427
                        javacall = (Boolean)value;
jaroslav@160
   428
                        return;
jaroslav@160
   429
                    }
jaroslav@123
   430
                    assert name.equals("body");
jaroslav@123
   431
                    body = (String) value;
jaroslav@123
   432
                }
jaroslav@123
   433
jaroslav@123
   434
                @Override
jaroslav@123
   435
                public AnnotationVisitor visitArray(String name) {
jaroslav@123
   436
                    return this;
jaroslav@123
   437
                }
jaroslav@123
   438
jaroslav@123
   439
                @Override
jaroslav@123
   440
                public void visitEnd() {
jaroslav@123
   441
                    if (body != null) {
jaroslav@188
   442
                        if (javacall) {
jaroslav@189
   443
                            body = FnUtils.callback(body);
jaroslav@188
   444
                            args.add("vm");
jaroslav@188
   445
                        }
jaroslav@188
   446
                        generateJSBody(args, body);
jaroslav@123
   447
                    }
jaroslav@123
   448
                }
jaroslav@123
   449
            }
jaroslav@123
   450
        }
jaroslav@163
   451
        
jaroslav@163
   452
        private final class LoadResource extends AnnotationVisitor {
jaroslav@163
   453
            public LoadResource() {
jaroslav@163
   454
                super(Opcodes.ASM4);
jaroslav@163
   455
            }
jaroslav@163
   456
            
jaroslav@163
   457
            @Override
jaroslav@163
   458
            public void visit(String attrName, Object value)  {
jaroslav@163
   459
                String relPath = (String) value;
jaroslav@163
   460
                if (relPath.startsWith("/")) {
jaroslav@163
   461
                    FnUtils.loadScript(JsClassLoader.this, relPath);
jaroslav@163
   462
                } else {
jaroslav@163
   463
                    int last = name.lastIndexOf('/');
jaroslav@163
   464
                    String fullPath = name.substring(0, last + 1) + relPath;
jaroslav@163
   465
                    FnUtils.loadScript(JsClassLoader.this, fullPath);
jaroslav@163
   466
                }
jaroslav@163
   467
            }
jaroslav@163
   468
        }
jaroslav@123
   469
    }
jaroslav@123
   470
    
jaroslav@123
   471
    private class ClassWriterEx extends ClassWriter {
jaroslav@123
   472
jaroslav@123
   473
        public ClassWriterEx(ClassReader classReader, int flags) {
jaroslav@123
   474
            super(classReader, flags);
jaroslav@123
   475
        }
jaroslav@123
   476
        
jaroslav@123
   477
        @Override
jaroslav@123
   478
        protected String getCommonSuperClass(final String type1, final String type2) {
jaroslav@123
   479
            Class<?> c, d;
jaroslav@123
   480
            ClassLoader classLoader = JsClassLoader.this;
jaroslav@123
   481
            try {
jaroslav@123
   482
                c = Class.forName(type1.replace('/', '.'), false, classLoader);
jaroslav@123
   483
                d = Class.forName(type2.replace('/', '.'), false, classLoader);
jaroslav@123
   484
            } catch (Exception e) {
jaroslav@123
   485
                throw new RuntimeException(e.toString());
jaroslav@123
   486
            }
jaroslav@123
   487
            if (c.isAssignableFrom(d)) {
jaroslav@123
   488
                return type1;
jaroslav@123
   489
            }
jaroslav@123
   490
            if (d.isAssignableFrom(c)) {
jaroslav@123
   491
                return type2;
jaroslav@123
   492
            }
jaroslav@123
   493
            if (c.isInterface() || d.isInterface()) {
jaroslav@123
   494
                return "java/lang/Object";
jaroslav@123
   495
            } else {
jaroslav@123
   496
                do {
jaroslav@123
   497
                    c = c.getSuperclass();
jaroslav@123
   498
                } while (!c.isAssignableFrom(d));
jaroslav@123
   499
                return c.getName().replace('.', '/');
jaroslav@123
   500
            }
jaroslav@123
   501
        }        
jaroslav@123
   502
    }
jaroslav@123
   503
}