boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jtulach@netbeans.org>
Mon, 15 Feb 2016 05:23:17 +0100
changeset 1051 d0e6c8f97dc3
parent 1019 e66e962fed13
child 1087 7865ad598184
permissions -rw-r--r--
Returning primitive array should yield ClassCastException during runtime
jaroslav@123
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@123
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@123
     5
 *
jaroslav@358
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@358
     7
 * Other names may be trademarks of their respective owners.
jaroslav@123
     8
 *
jaroslav@358
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@358
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@358
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@358
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@358
    13
 * License. You can obtain a copy of the License at
jaroslav@358
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@358
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@358
    16
 * specific language governing permissions and limitations under the
jaroslav@358
    17
 * License.  When distributing the software, include this License Header
jaroslav@358
    18
 * Notice in each file and include the License file at
jaroslav@358
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@358
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@358
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@358
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@358
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@358
    24
 * your own identifying information:
jaroslav@358
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@358
    26
 *
jaroslav@358
    27
 * Contributor(s):
jaroslav@358
    28
 *
jaroslav@358
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@358
    31
 *
jaroslav@358
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@358
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@358
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@358
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@358
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@358
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@358
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@358
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@358
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@358
    41
 * made subject to such option by the copyright holder.
jaroslav@123
    42
 */
jaroslav@362
    43
package org.netbeans.html.boot.impl;
jaroslav@123
    44
jtulach@932
    45
import java.io.IOException;
jaroslav@163
    46
import java.io.InputStream;
jaroslav@163
    47
import java.io.InputStreamReader;
jaroslav@163
    48
import java.io.Reader;
jaroslav@123
    49
import java.net.URL;
jaroslav@123
    50
import java.util.ArrayList;
jaroslav@123
    51
import java.util.Collections;
jaroslav@123
    52
import java.util.Enumeration;
jaroslav@123
    53
import java.util.List;
jaroslav@534
    54
import net.java.html.js.JavaScriptBody;
jaroslav@534
    55
import net.java.html.js.JavaScriptResource;
jtulach@838
    56
import org.netbeans.html.boot.spi.Fn;
jaroslav@323
    57
import org.objectweb.asm.AnnotationVisitor;
jaroslav@323
    58
import org.objectweb.asm.ClassReader;
jaroslav@323
    59
import org.objectweb.asm.ClassVisitor;
jaroslav@323
    60
import org.objectweb.asm.ClassWriter;
jtulach@1019
    61
import org.objectweb.asm.FieldVisitor;
jaroslav@323
    62
import org.objectweb.asm.Label;
jaroslav@323
    63
import org.objectweb.asm.MethodVisitor;
jaroslav@323
    64
import org.objectweb.asm.Opcodes;
jaroslav@323
    65
import org.objectweb.asm.Type;
jaroslav@323
    66
import org.objectweb.asm.signature.SignatureReader;
jaroslav@323
    67
import org.objectweb.asm.signature.SignatureVisitor;
jaroslav@323
    68
import org.objectweb.asm.signature.SignatureWriter;
jaroslav@123
    69
jtulach@932
    70
/** Utilities related to bytecode transformations. Depend on asm.jar which
jtulach@932
    71
 * needs to be added to be provided to classpath to make methods in this 
jtulach@932
    72
 * class useful.
jaroslav@123
    73
 *
jtulach@790
    74
 * @author Jaroslav Tulach
jaroslav@123
    75
 */
jaroslav@455
    76
public final class FnUtils {
jaroslav@288
    77
    
jaroslav@123
    78
    private FnUtils() {
jaroslav@123
    79
    }
jaroslav@288
    80
    
jaroslav@331
    81
    /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
jaroslav@331
    82
     * in the bytecode and converts them into real code. Used by Maven plugin
jaroslav@331
    83
     * postprocessing classes.
jaroslav@331
    84
     * 
jaroslav@331
    85
     * @param bytecode the original bytecode with javascript specific annotations
jaroslav@352
    86
     * @param loader the loader to load resources (scripts and classes) when needed
jaroslav@331
    87
     * @return the transformed bytecode
jaroslav@331
    88
     * @since 0.7
jaroslav@331
    89
     */
jaroslav@352
    90
    public static byte[] transform(byte[] bytecode, ClassLoader loader) {
jaroslav@352
    91
        ClassReader cr = new ClassReader(bytecode) {
jaroslav@352
    92
            // to allow us to compile with -profile compact1 on 
jaroslav@352
    93
            // JDK8 while processing the class as JDK7, the highest
jaroslav@352
    94
            // class format asm 4.1 understands to
jaroslav@352
    95
            @Override
jaroslav@352
    96
            public short readShort(int index) {
jaroslav@352
    97
                short s = super.readShort(index);
jaroslav@352
    98
                if (index == 6 && s > Opcodes.V1_7) {
jaroslav@352
    99
                    return Opcodes.V1_7;
jaroslav@352
   100
                }
jaroslav@352
   101
                return s;
jaroslav@352
   102
            }
jaroslav@352
   103
        };
jaroslav@352
   104
        FindInClass tst = new FindInClass(loader, null);
jaroslav@352
   105
        cr.accept(tst, 0);
jaroslav@352
   106
        if (tst.found > 0) {
jaroslav@352
   107
            ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
jaroslav@352
   108
            FindInClass fic = new FindInClass(loader, w);
jaroslav@352
   109
            cr.accept(fic, 0);
jaroslav@352
   110
            bytecode = w.toByteArray();
jaroslav@352
   111
        }
jaroslav@352
   112
        return bytecode;
jaroslav@331
   113
    }
jaroslav@331
   114
    
jaroslav@128
   115
    public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
jtulach@932
   116
        return new JsClassLoaderImpl(parent, f, d);
jaroslav@123
   117
    }
jaroslav@160
   118
jaroslav@189
   119
    static String callback(final String body) {
jaroslav@184
   120
        return new JsCallback() {
jaroslav@184
   121
            @Override
jaroslav@184
   122
            protected CharSequence callMethod(
jaroslav@184
   123
                String ident, String fqn, String method, String params
jaroslav@184
   124
            ) {
jaroslav@188
   125
                StringBuilder sb = new StringBuilder();
jtulach@911
   126
                if (ident != null) {
jtulach@911
   127
                    sb.append("vm.raw$");
jtulach@911
   128
                } else {
jtulach@911
   129
                    sb.append("vm.");
jtulach@911
   130
                }
jtulach@911
   131
                sb.append(mangle(fqn, method, params));
jaroslav@191
   132
                sb.append("(");
jaroslav@191
   133
                if (ident != null) {
jaroslav@191
   134
                    sb.append(ident);
jaroslav@191
   135
                }
jaroslav@188
   136
                return sb;
jaroslav@184
   137
            }
jaroslav@184
   138
jaroslav@184
   139
        }.parse(body);
jaroslav@160
   140
    }
jaroslav@163
   141
jaroslav@323
   142
    private static final class FindInClass extends ClassVisitor {
jaroslav@323
   143
        private String name;
jaroslav@323
   144
        private int found;
jaroslav@349
   145
        private String resource;
jaroslav@323
   146
jaroslav@323
   147
        public FindInClass(ClassLoader l, ClassVisitor cv) {
jaroslav@323
   148
            super(Opcodes.ASM4, cv);
jaroslav@323
   149
        }
jaroslav@323
   150
jaroslav@323
   151
        @Override
jaroslav@323
   152
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
jaroslav@323
   153
            this.name = name;
jaroslav@323
   154
            super.visit(version, access, name, signature, superName, interfaces);
jaroslav@323
   155
        }
jaroslav@323
   156
jaroslav@323
   157
        @Override
jaroslav@323
   158
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@520
   159
            final AnnotationVisitor del = super.visitAnnotation(desc, visible);
jaroslav@323
   160
            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
jaroslav@520
   161
                return new LoadResource(del);
jaroslav@323
   162
            }
jaroslav@520
   163
            return del;
jaroslav@323
   164
        }
jaroslav@323
   165
jaroslav@323
   166
        @Override
jaroslav@323
   167
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
jaroslav@323
   168
            return new FindInMethod(access, name, desc,
jaroslav@323
   169
                    super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
jaroslav@323
   170
            );
jaroslav@323
   171
        }
jaroslav@323
   172
jtulach@1019
   173
        @Override
jtulach@1019
   174
        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
jtulach@1019
   175
            if (name.startsWith("$$fn$$")) {
jtulach@1019
   176
                return null;
jtulach@1019
   177
            }
jtulach@1019
   178
            return superField(access, name, desc, signature, value);
jtulach@1019
   179
        }
jtulach@1019
   180
jtulach@1019
   181
        final FieldVisitor superField(int access, String name, String desc, String signature, Object value) {
jtulach@1019
   182
            return super.visitField(access, name, desc, signature, value);
jtulach@1019
   183
        }
jtulach@1019
   184
jaroslav@323
   185
        private final class FindInMethod extends MethodVisitor {
jaroslav@323
   186
jaroslav@323
   187
            private final String name;
jaroslav@323
   188
            private final String desc;
jaroslav@323
   189
            private final int access;
jaroslav@520
   190
            private FindInAnno fia;
jaroslav@323
   191
            private boolean bodyGenerated;
jaroslav@323
   192
jaroslav@323
   193
            public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
jaroslav@323
   194
                super(Opcodes.ASM4, mv);
jaroslav@323
   195
                this.access = access;
jaroslav@323
   196
                this.name = name;
jaroslav@323
   197
                this.desc = desc;
jaroslav@323
   198
            }
jaroslav@323
   199
jaroslav@323
   200
            @Override
jaroslav@323
   201
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@441
   202
                if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
jaroslav@323
   203
                    found++;
jaroslav@323
   204
                    return new FindInAnno();
jaroslav@323
   205
                }
jaroslav@323
   206
                return super.visitAnnotation(desc, visible);
jaroslav@323
   207
            }
jaroslav@323
   208
jaroslav@520
   209
            private void generateJSBody(FindInAnno fia) {
jaroslav@520
   210
                this.fia = fia;
jaroslav@323
   211
            }
jaroslav@323
   212
jaroslav@323
   213
            @Override
jaroslav@323
   214
            public void visitCode() {
jaroslav@520
   215
                if (fia == null) {
jaroslav@323
   216
                    return;
jaroslav@323
   217
                }
jaroslav@451
   218
                generateBody(true);
jaroslav@323
   219
            }
jaroslav@323
   220
jaroslav@451
   221
            private boolean generateBody(boolean hasCode) {
jaroslav@323
   222
                if (bodyGenerated) {
jaroslav@323
   223
                    return false;
jaroslav@323
   224
                }
jaroslav@323
   225
                bodyGenerated = true;
jaroslav@520
   226
                if (mv != null) {
jaroslav@520
   227
                    AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
jaroslav@520
   228
                    AnnotationVisitor varr = va.visitArray("args");
jaroslav@520
   229
                    for (String argName : fia.args) {
jaroslav@520
   230
                        varr.visit(null, argName);
jaroslav@520
   231
                    }
jaroslav@520
   232
                    varr.visitEnd();
jaroslav@520
   233
                    va.visit("javacall", fia.javacall);
jaroslav@520
   234
                    va.visit("body", fia.body);
jaroslav@520
   235
                    va.visitEnd();
jaroslav@520
   236
                }
jaroslav@520
   237
                
jaroslav@520
   238
                String body;
jaroslav@520
   239
                List<String> args;
jaroslav@520
   240
                if (fia.javacall) {
jaroslav@520
   241
                    body = callback(fia.body);
jaroslav@520
   242
                    args = new ArrayList<String>(fia.args);
jaroslav@520
   243
                    args.add("vm");
jaroslav@520
   244
                } else {
jaroslav@520
   245
                    body = fia.body;
jaroslav@520
   246
                    args = fia.args;
jaroslav@520
   247
                }
jaroslav@323
   248
jaroslav@323
   249
                super.visitFieldInsn(
jaroslav@323
   250
                        Opcodes.GETSTATIC, FindInClass.this.name,
jaroslav@323
   251
                        "$$fn$$" + name + "_" + found,
jtulach@838
   252
                        "Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   253
                );
jaroslav@323
   254
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   255
                super.visitMethodInsn(
jaroslav@323
   256
                        Opcodes.INVOKESTATIC,
jtulach@838
   257
                        "org/netbeans/html/boot/spi/Fn", "isValid",
jtulach@838
   258
                        "(Lorg/netbeans/html/boot/spi/Fn;)Z"
jaroslav@323
   259
                );
jaroslav@323
   260
                Label ifNotNull = new Label();
jaroslav@323
   261
                super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
jaroslav@323
   262
jaroslav@323
   263
                // init Fn
jaroslav@323
   264
                super.visitInsn(Opcodes.POP);
jaroslav@323
   265
                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jtulach@900
   266
                super.visitInsn(fia.keepAlive ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
jaroslav@323
   267
                super.visitLdcInsn(body);
jaroslav@323
   268
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   269
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
jaroslav@323
   270
                boolean needsVM = false;
jaroslav@323
   271
                for (int i = 0; i < args.size(); i++) {
jaroslav@323
   272
                    assert !needsVM;
jaroslav@323
   273
                    String argName = args.get(i);
jaroslav@323
   274
                    needsVM = "vm".equals(argName);
jaroslav@323
   275
                    super.visitInsn(Opcodes.DUP);
jaroslav@323
   276
                    super.visitIntInsn(Opcodes.BIPUSH, i);
jaroslav@323
   277
                    super.visitLdcInsn(argName);
jaroslav@323
   278
                    super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   279
                }
jaroslav@323
   280
                super.visitMethodInsn(Opcodes.INVOKESTATIC,
jtulach@838
   281
                        "org/netbeans/html/boot/spi/Fn", "define",
jtulach@900
   282
                        "(Ljava/lang/Class;ZLjava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   283
                );
jaroslav@451
   284
                Label noPresenter = new Label();
jtulach@851
   285
                super.visitInsn(Opcodes.DUP);
jtulach@851
   286
                super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
jaroslav@349
   287
                if (resource != null) {
jaroslav@349
   288
                    super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jaroslav@349
   289
                    super.visitLdcInsn(resource);
jaroslav@349
   290
                    super.visitMethodInsn(Opcodes.INVOKESTATIC,
jtulach@838
   291
                            "org/netbeans/html/boot/spi/Fn", "preload",
jtulach@838
   292
                            "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@349
   293
                    );
jaroslav@349
   294
                }
jaroslav@323
   295
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   296
                super.visitFieldInsn(
jaroslav@323
   297
                        Opcodes.PUTSTATIC, FindInClass.this.name,
jaroslav@323
   298
                        "$$fn$$" + name + "_" + found,
jtulach@838
   299
                        "Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   300
                );
jaroslav@323
   301
                // end of Fn init
jaroslav@323
   302
jaroslav@323
   303
                super.visitLabel(ifNotNull);
jaroslav@323
   304
jaroslav@323
   305
                final int offset;
jaroslav@323
   306
                if ((access & Opcodes.ACC_STATIC) == 0) {
jaroslav@323
   307
                    offset = 1;
jaroslav@323
   308
                    super.visitIntInsn(Opcodes.ALOAD, 0);
jaroslav@323
   309
                } else {
jaroslav@323
   310
                    offset = 0;
jaroslav@323
   311
                    super.visitInsn(Opcodes.ACONST_NULL);
jaroslav@323
   312
                }
jaroslav@323
   313
jaroslav@323
   314
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   315
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
jaroslav@323
   316
jaroslav@323
   317
                class SV extends SignatureVisitor {
jaroslav@323
   318
jaroslav@323
   319
                    private boolean nowReturn;
jaroslav@323
   320
                    private Type returnType;
jaroslav@323
   321
                    private int index;
jaroslav@323
   322
                    private int loadIndex = offset;
jaroslav@323
   323
jaroslav@323
   324
                    public SV() {
jaroslav@323
   325
                        super(Opcodes.ASM4);
jaroslav@323
   326
                    }
jaroslav@323
   327
jaroslav@323
   328
                    @Override
jaroslav@323
   329
                    public void visitBaseType(char descriptor) {
jaroslav@323
   330
                        final Type t = Type.getType("" + descriptor);
jaroslav@323
   331
                        if (nowReturn) {
jaroslav@323
   332
                            returnType = t;
jaroslav@323
   333
                            return;
jaroslav@323
   334
                        }
jaroslav@323
   335
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   336
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   337
                        FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
jaroslav@323
   338
                        String factory;
jaroslav@323
   339
                        switch (descriptor) {
jaroslav@323
   340
                            case 'I':
jaroslav@323
   341
                                factory = "java/lang/Integer";
jaroslav@323
   342
                                break;
jaroslav@323
   343
                            case 'J':
jaroslav@323
   344
                                factory = "java/lang/Long";
jaroslav@323
   345
                                loadIndex++;
jaroslav@323
   346
                                break;
jaroslav@323
   347
                            case 'S':
jaroslav@323
   348
                                factory = "java/lang/Short";
jaroslav@323
   349
                                break;
jaroslav@323
   350
                            case 'F':
jaroslav@323
   351
                                factory = "java/lang/Float";
jaroslav@323
   352
                                break;
jaroslav@323
   353
                            case 'D':
jaroslav@323
   354
                                factory = "java/lang/Double";
jaroslav@323
   355
                                loadIndex++;
jaroslav@323
   356
                                break;
jaroslav@323
   357
                            case 'Z':
jaroslav@323
   358
                                factory = "java/lang/Boolean";
jaroslav@323
   359
                                break;
jaroslav@323
   360
                            case 'C':
jaroslav@323
   361
                                factory = "java/lang/Character";
jaroslav@323
   362
                                break;
jaroslav@323
   363
                            case 'B':
jaroslav@323
   364
                                factory = "java/lang/Byte";
jaroslav@323
   365
                                break;
jaroslav@323
   366
                            default:
jaroslav@323
   367
                                throw new IllegalStateException(t.toString());
jaroslav@323
   368
                        }
jaroslav@323
   369
                        FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
jaroslav@323
   370
                                factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
jaroslav@323
   371
                        );
jaroslav@323
   372
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   373
                    }
jaroslav@323
   374
jaroslav@323
   375
                    @Override
jaroslav@323
   376
                    public SignatureVisitor visitArrayType() {
jaroslav@323
   377
                        if (nowReturn) {
jaroslav@439
   378
                            return new SignatureVisitor(Opcodes.ASM4) {
jaroslav@439
   379
                                @Override
jaroslav@439
   380
                                public void visitClassType(String name) {
jaroslav@439
   381
                                    returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
jaroslav@439
   382
                                }
jtulach@1051
   383
jtulach@1051
   384
                                @Override
jtulach@1051
   385
                                public void visitBaseType(char descriptor) {
jtulach@1051
   386
                                    returnType = Type.getType("[" + descriptor);
jtulach@1051
   387
                                }
jaroslav@439
   388
                            };
jaroslav@323
   389
                        }
jaroslav@323
   390
                        loadObject();
jaroslav@323
   391
                        return new SignatureWriter();
jaroslav@323
   392
                    }
jaroslav@323
   393
jaroslav@323
   394
                    @Override
jaroslav@323
   395
                    public void visitClassType(String name) {
jaroslav@323
   396
                        if (nowReturn) {
jaroslav@323
   397
                            returnType = Type.getObjectType(name);
jaroslav@323
   398
                            return;
jaroslav@323
   399
                        }
jaroslav@323
   400
                        loadObject();
jaroslav@323
   401
                    }
jaroslav@323
   402
jaroslav@323
   403
                    @Override
jaroslav@323
   404
                    public SignatureVisitor visitReturnType() {
jaroslav@323
   405
                        nowReturn = true;
jaroslav@323
   406
                        return this;
jaroslav@323
   407
                    }
jaroslav@323
   408
jaroslav@323
   409
                    private void loadObject() {
jaroslav@323
   410
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   411
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   412
                        FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
jaroslav@323
   413
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   414
                    }
jaroslav@323
   415
jaroslav@323
   416
                }
jaroslav@323
   417
                SV sv = new SV();
jaroslav@323
   418
                SignatureReader sr = new SignatureReader(desc);
jaroslav@323
   419
                sr.accept(sv);
jaroslav@323
   420
jaroslav@323
   421
                if (needsVM) {
jaroslav@323
   422
                    FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   423
                    FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
jaroslav@323
   424
                    int lastSlash = FindInClass.this.name.lastIndexOf('/');
jaroslav@323
   425
                    String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
jaroslav@323
   426
                    FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
jaroslav@323
   427
                    FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
jaroslav@323
   428
                    FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   429
                }
jaroslav@323
   430
jaroslav@570
   431
                if (fia.wait4js) {
jaroslav@570
   432
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jtulach@838
   433
                            "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
jaroslav@570
   434
                    );
jaroslav@570
   435
                    switch (sv.returnType.getSort()) {
jaroslav@570
   436
                        case Type.VOID:
jaroslav@570
   437
                            super.visitInsn(Opcodes.RETURN);
jaroslav@570
   438
                            break;
jaroslav@570
   439
                        case Type.ARRAY:
jaroslav@570
   440
                        case Type.OBJECT:
jaroslav@570
   441
                            super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
jaroslav@570
   442
                            super.visitInsn(Opcodes.ARETURN);
jaroslav@570
   443
                            break;
jaroslav@570
   444
                        case Type.BOOLEAN:
jaroslav@570
   445
                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
jaroslav@570
   446
                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@570
   447
                                    "java/lang/Boolean", "booleanValue", "()Z"
jaroslav@570
   448
                            );
jaroslav@570
   449
                            super.visitInsn(Opcodes.IRETURN);
jaroslav@570
   450
                            break;
jaroslav@570
   451
                        default:
jaroslav@570
   452
                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
jaroslav@570
   453
                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@570
   454
                                    "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
jaroslav@570
   455
                            );
jaroslav@570
   456
                            super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
jaroslav@570
   457
                    }
jaroslav@570
   458
                } else {
jaroslav@570
   459
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jtulach@838
   460
                            "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
jaroslav@570
   461
                    );
jaroslav@570
   462
                    super.visitInsn(Opcodes.RETURN);
jaroslav@323
   463
                }
jtulach@851
   464
                super.visitLabel(noPresenter);
jaroslav@451
   465
                if (hasCode) {
jaroslav@451
   466
                    super.visitCode();
jtulach@851
   467
                } else {
jtulach@851
   468
                    super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
jtulach@851
   469
                    super.visitInsn(Opcodes.DUP);
jtulach@851
   470
                    super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
jtulach@851
   471
                    super.visitMethodInsn(Opcodes.INVOKESPECIAL, 
jtulach@851
   472
                        "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
jtulach@851
   473
                    );
jtulach@851
   474
                    this.visitInsn(Opcodes.ATHROW);
jaroslav@451
   475
                }
jaroslav@323
   476
                return true;
jaroslav@323
   477
            }
jtulach@1019
   478
            
jaroslav@323
   479
            @Override
jaroslav@323
   480
            public void visitEnd() {
jaroslav@323
   481
                super.visitEnd();
jaroslav@520
   482
                if (fia != null) {
jaroslav@451
   483
                    if (generateBody(false)) {
jaroslav@323
   484
                        // native method
jaroslav@323
   485
                        super.visitMaxs(1, 0);
jaroslav@323
   486
                    }
jtulach@1019
   487
                    FindInClass.this.superField(
jaroslav@323
   488
                            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
jaroslav@323
   489
                            "$$fn$$" + name + "_" + found,
jtulach@838
   490
                            "Lorg/netbeans/html/boot/spi/Fn;",
jaroslav@323
   491
                            null, null
jaroslav@323
   492
                    );
jaroslav@323
   493
                }
jaroslav@323
   494
            }
jaroslav@323
   495
jaroslav@323
   496
            private final class FindInAnno extends AnnotationVisitor {
jaroslav@323
   497
jaroslav@520
   498
                List<String> args = new ArrayList<String>();
jaroslav@520
   499
                String body;
jaroslav@520
   500
                boolean javacall = false;
jaroslav@570
   501
                boolean wait4js = true;
jtulach@906
   502
                boolean keepAlive = true;
jaroslav@323
   503
jaroslav@323
   504
                public FindInAnno() {
jaroslav@323
   505
                    super(Opcodes.ASM4);
jaroslav@323
   506
                }
jaroslav@323
   507
jaroslav@323
   508
                @Override
jaroslav@323
   509
                public void visit(String name, Object value) {
jaroslav@323
   510
                    if (name == null) {
jaroslav@323
   511
                        args.add((String) value);
jaroslav@323
   512
                        return;
jaroslav@323
   513
                    }
jaroslav@323
   514
                    if (name.equals("javacall")) { // NOI18N
jaroslav@323
   515
                        javacall = (Boolean) value;
jaroslav@323
   516
                        return;
jaroslav@323
   517
                    }
jaroslav@570
   518
                    if (name.equals("wait4js")) { // NOI18N
jaroslav@570
   519
                        wait4js = (Boolean) value;
jaroslav@570
   520
                        return;
jaroslav@570
   521
                    }
jtulach@900
   522
                    if (name.equals("keepAlive")) { // NOI18N
jtulach@900
   523
                        keepAlive = (Boolean) value;
jtulach@900
   524
                        return;
jtulach@900
   525
                    }
jtulach@900
   526
                    assert name.equals("body"); // NOI18N
jaroslav@323
   527
                    body = (String) value;
jaroslav@323
   528
                }
jaroslav@323
   529
jaroslav@323
   530
                @Override
jaroslav@323
   531
                public AnnotationVisitor visitArray(String name) {
jaroslav@323
   532
                    return this;
jaroslav@323
   533
                }
jaroslav@323
   534
jaroslav@323
   535
                @Override
jaroslav@323
   536
                public void visitEnd() {
jaroslav@323
   537
                    if (body != null) {
jaroslav@520
   538
                        generateJSBody(this);
jaroslav@323
   539
                    }
jaroslav@323
   540
                }
jaroslav@323
   541
            }
jaroslav@323
   542
        }
jaroslav@323
   543
jaroslav@323
   544
        private final class LoadResource extends AnnotationVisitor {
jaroslav@520
   545
            public LoadResource(AnnotationVisitor av) {
jaroslav@520
   546
                super(Opcodes.ASM4, av);
jaroslav@323
   547
            }
jaroslav@323
   548
jaroslav@323
   549
            @Override
jaroslav@323
   550
            public void visit(String attrName, Object value) {
jaroslav@520
   551
                super.visit(attrName, value);
jaroslav@323
   552
                String relPath = (String) value;
jaroslav@323
   553
                if (relPath.startsWith("/")) {
jaroslav@349
   554
                    resource = relPath;
jaroslav@323
   555
                } else {
jaroslav@323
   556
                    int last = name.lastIndexOf('/');
jaroslav@323
   557
                    String fullPath = name.substring(0, last + 1) + relPath;
jaroslav@349
   558
                    resource = fullPath;
jaroslav@323
   559
                }
jaroslav@323
   560
            }
jaroslav@323
   561
        }
jaroslav@323
   562
    }
jaroslav@323
   563
jaroslav@323
   564
    private static class ClassWriterEx extends ClassWriter {
jaroslav@323
   565
jtulach@932
   566
        private final ClassLoader loader;
jaroslav@323
   567
jaroslav@323
   568
        public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
jaroslav@323
   569
            super(classReader, flags);
jaroslav@323
   570
            this.loader = l;
jaroslav@323
   571
        }
jaroslav@323
   572
jaroslav@323
   573
        @Override
jaroslav@323
   574
        protected String getCommonSuperClass(final String type1, final String type2) {
jaroslav@323
   575
            Class<?> c, d;
jaroslav@323
   576
            try {
jaroslav@323
   577
                c = Class.forName(type1.replace('/', '.'), false, loader);
jaroslav@323
   578
                d = Class.forName(type2.replace('/', '.'), false, loader);
jaroslav@323
   579
            } catch (Exception e) {
jaroslav@323
   580
                throw new RuntimeException(e.toString());
jaroslav@323
   581
            }
jaroslav@323
   582
            if (c.isAssignableFrom(d)) {
jaroslav@323
   583
                return type1;
jaroslav@323
   584
            }
jaroslav@323
   585
            if (d.isAssignableFrom(c)) {
jaroslav@323
   586
                return type2;
jaroslav@323
   587
            }
jaroslav@323
   588
            if (c.isInterface() || d.isInterface()) {
jaroslav@323
   589
                return "java/lang/Object";
jaroslav@323
   590
            } else {
jaroslav@323
   591
                do {
jaroslav@323
   592
                    c = c.getSuperclass();
jaroslav@323
   593
                } while (!c.isAssignableFrom(d));
jaroslav@323
   594
                return c.getName().replace('.', '/');
jaroslav@323
   595
            }
jaroslav@323
   596
        }
jaroslav@323
   597
    }
jtulach@932
   598
jtulach@932
   599
    static class JsClassLoaderImpl extends JsClassLoader {
jtulach@932
   600
jtulach@932
   601
        private final FindResources f;
jtulach@932
   602
        private final Fn.Presenter d;
jtulach@932
   603
jtulach@932
   604
        public JsClassLoaderImpl(ClassLoader parent, FindResources f, Fn.Presenter d) {
jtulach@932
   605
            super(parent);
jtulach@932
   606
            setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
jtulach@932
   607
            this.f = f;
jtulach@932
   608
            this.d = d;
jtulach@932
   609
        }
jtulach@932
   610
jtulach@932
   611
        @Override
jtulach@932
   612
        protected URL findResource(String name) {
jtulach@932
   613
            List<URL> l = res(name, true);
jtulach@932
   614
            return l.isEmpty() ? null : l.get(0);
jtulach@932
   615
        }
jtulach@932
   616
jtulach@932
   617
        @Override
jtulach@932
   618
        protected Enumeration<URL> findResources(String name) {
jtulach@932
   619
            return Collections.enumeration(res(name, false));
jtulach@932
   620
        }
jtulach@932
   621
        
jtulach@932
   622
        private List<URL> res(String name, boolean oneIsEnough) {
jtulach@932
   623
            List<URL> l = new ArrayList<URL>();
jtulach@932
   624
            f.findResources(name, l, oneIsEnough);
jtulach@932
   625
            return l;
jtulach@932
   626
        }
jtulach@932
   627
    
jtulach@932
   628
        @Override
jtulach@932
   629
        protected Class<?> findClass(String name) throws ClassNotFoundException {
jtulach@932
   630
            if (name.startsWith("javafx")) {
jtulach@932
   631
                return Class.forName(name);
jtulach@932
   632
            }
jtulach@932
   633
            if (name.startsWith("netscape")) {
jtulach@932
   634
                return Class.forName(name);
jtulach@932
   635
            }
jtulach@932
   636
            if (name.startsWith("com.sun")) {
jtulach@932
   637
                return Class.forName(name);
jtulach@932
   638
            }
jtulach@932
   639
            if (name.startsWith("org.netbeans.html.context.spi")) {
jtulach@932
   640
                return Class.forName(name);
jtulach@932
   641
            }
jtulach@932
   642
            if (name.startsWith("net.java.html.BrwsrCtx")) {
jtulach@932
   643
                return Class.forName(name);
jtulach@932
   644
            }
jtulach@932
   645
            if (name.equals(JsClassLoader.class.getName())) {
jtulach@932
   646
                return JsClassLoader.class;
jtulach@932
   647
            }
jtulach@932
   648
            if (name.equals(Fn.class.getName())) {
jtulach@932
   649
                return Fn.class;
jtulach@932
   650
            }
jtulach@932
   651
            if (name.equals(Fn.Presenter.class.getName())) {
jtulach@932
   652
                return Fn.Presenter.class;
jtulach@932
   653
            }
jtulach@932
   654
            if (name.equals(Fn.ToJavaScript.class.getName())) {
jtulach@932
   655
                return Fn.ToJavaScript.class;
jtulach@932
   656
            }
jtulach@932
   657
            if (name.equals(Fn.FromJavaScript.class.getName())) {
jtulach@932
   658
                return Fn.FromJavaScript.class;
jtulach@932
   659
            }
jtulach@932
   660
            if (name.equals(FnUtils.class.getName())) {
jtulach@932
   661
                return FnUtils.class;
jtulach@932
   662
            }
jtulach@932
   663
            if (
jtulach@932
   664
                name.equals("org.netbeans.html.boot.spi.Fn") ||
jtulach@932
   665
                name.equals("org.netbeans.html.boot.impl.FnUtils") ||
jtulach@932
   666
                name.equals("org.netbeans.html.boot.impl.FnContext")
jtulach@932
   667
            ) {
jtulach@932
   668
                return Class.forName(name);
jtulach@932
   669
            }
jtulach@932
   670
            URL u = findResource(name.replace('.', '/') + ".class");
jtulach@932
   671
            if (u != null) {
jtulach@932
   672
                InputStream is = null;
jtulach@932
   673
                try {
jtulach@932
   674
                    is = u.openStream();
jtulach@932
   675
                    byte[] arr = new byte[is.available()];
jtulach@932
   676
                    int len = 0;
jtulach@932
   677
                    while (len < arr.length) {
jtulach@932
   678
                        int read = is.read(arr, len, arr.length - len);
jtulach@932
   679
                        if (read == -1) {
jtulach@932
   680
                            throw new IOException("Can't read " + u);
jtulach@932
   681
                        }
jtulach@932
   682
                        len += read;
jtulach@932
   683
                    }
jtulach@932
   684
                    is.close();
jtulach@932
   685
                    is = null;
jtulach@932
   686
                    if (JsPkgCache.process(this, name)) {
jtulach@932
   687
                        arr = FnUtils.transform(arr, this);
jtulach@932
   688
                    }
jtulach@932
   689
                    return defineClass(name, arr, 0, arr.length);
jtulach@932
   690
                } catch (IOException ex) {
jtulach@932
   691
                    throw new ClassNotFoundException("Can't load " + name, ex);
jtulach@932
   692
                } finally {
jtulach@932
   693
                    try {
jtulach@932
   694
                        if (is != null) is.close();
jtulach@932
   695
                    } catch (IOException ex) {
jtulach@932
   696
                        throw new ClassNotFoundException(null, ex);
jtulach@932
   697
                    }
jtulach@932
   698
                }
jtulach@932
   699
            }
jtulach@932
   700
            return super.findClass(name);
jtulach@932
   701
        }
jtulach@932
   702
    
jtulach@932
   703
        protected Fn defineFn(String code, String... names) {
jtulach@932
   704
            return d.defineFn(code, names);
jtulach@932
   705
        }
jtulach@932
   706
        
jtulach@932
   707
        protected void loadScript(Reader code) throws Exception {
jtulach@932
   708
            d.loadScript(code);
jtulach@932
   709
        }
jtulach@932
   710
    }
jaroslav@123
   711
}