boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 790 30f20d9c0986
child 851 69ed96e7f41b
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
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
jaroslav@163
    45
import java.io.InputStream;
jaroslav@163
    46
import java.io.InputStreamReader;
jaroslav@163
    47
import java.io.Reader;
jaroslav@123
    48
import java.net.URL;
jaroslav@123
    49
import java.util.ArrayList;
jaroslav@123
    50
import java.util.Collections;
jaroslav@123
    51
import java.util.Enumeration;
jaroslav@123
    52
import java.util.List;
jaroslav@534
    53
import net.java.html.js.JavaScriptBody;
jaroslav@534
    54
import net.java.html.js.JavaScriptResource;
jtulach@838
    55
import org.netbeans.html.boot.spi.Fn;
jaroslav@323
    56
import org.objectweb.asm.AnnotationVisitor;
jaroslav@323
    57
import org.objectweb.asm.ClassReader;
jaroslav@323
    58
import org.objectweb.asm.ClassVisitor;
jaroslav@323
    59
import org.objectweb.asm.ClassWriter;
jaroslav@323
    60
import org.objectweb.asm.Label;
jaroslav@323
    61
import org.objectweb.asm.MethodVisitor;
jaroslav@323
    62
import org.objectweb.asm.Opcodes;
jaroslav@323
    63
import org.objectweb.asm.Type;
jaroslav@323
    64
import org.objectweb.asm.signature.SignatureReader;
jaroslav@323
    65
import org.objectweb.asm.signature.SignatureVisitor;
jaroslav@323
    66
import org.objectweb.asm.signature.SignatureWriter;
jaroslav@123
    67
jaroslav@123
    68
/**
jaroslav@123
    69
 *
jtulach@790
    70
 * @author Jaroslav Tulach
jaroslav@123
    71
 */
jaroslav@455
    72
public final class FnUtils {
jaroslav@288
    73
    
jaroslav@123
    74
    private FnUtils() {
jaroslav@123
    75
    }
jaroslav@288
    76
    
jaroslav@331
    77
    /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
jaroslav@331
    78
     * in the bytecode and converts them into real code. Used by Maven plugin
jaroslav@331
    79
     * postprocessing classes.
jaroslav@331
    80
     * 
jaroslav@331
    81
     * @param bytecode the original bytecode with javascript specific annotations
jaroslav@352
    82
     * @param loader the loader to load resources (scripts and classes) when needed
jaroslav@331
    83
     * @return the transformed bytecode
jaroslav@331
    84
     * @since 0.7
jaroslav@331
    85
     */
jaroslav@352
    86
    public static byte[] transform(byte[] bytecode, ClassLoader loader) {
jaroslav@352
    87
        ClassReader cr = new ClassReader(bytecode) {
jaroslav@352
    88
            // to allow us to compile with -profile compact1 on 
jaroslav@352
    89
            // JDK8 while processing the class as JDK7, the highest
jaroslav@352
    90
            // class format asm 4.1 understands to
jaroslav@352
    91
            @Override
jaroslav@352
    92
            public short readShort(int index) {
jaroslav@352
    93
                short s = super.readShort(index);
jaroslav@352
    94
                if (index == 6 && s > Opcodes.V1_7) {
jaroslav@352
    95
                    return Opcodes.V1_7;
jaroslav@352
    96
                }
jaroslav@352
    97
                return s;
jaroslav@352
    98
            }
jaroslav@352
    99
        };
jaroslav@352
   100
        FindInClass tst = new FindInClass(loader, null);
jaroslav@352
   101
        cr.accept(tst, 0);
jaroslav@352
   102
        if (tst.found > 0) {
jaroslav@352
   103
            ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
jaroslav@352
   104
            FindInClass fic = new FindInClass(loader, w);
jaroslav@352
   105
            cr.accept(fic, 0);
jaroslav@352
   106
            bytecode = w.toByteArray();
jaroslav@352
   107
        }
jaroslav@352
   108
        return bytecode;
jaroslav@331
   109
    }
jaroslav@331
   110
    
jaroslav@288
   111
    public static boolean isJavaScriptCapable(ClassLoader l) {
jaroslav@323
   112
        if (l instanceof JsClassLoader) {
jaroslav@323
   113
            return true;
jaroslav@323
   114
        }
jaroslav@509
   115
        if (l.getResource("META-INF/net.java.html.js.classes") != null) {
jaroslav@509
   116
            return false;
jaroslav@509
   117
        }
jaroslav@510
   118
        return true;
jaroslav@288
   119
    }
jaroslav@288
   120
    
jaroslav@288
   121
    public static boolean isValid(Fn fn) {
jaroslav@288
   122
        return fn != null && fn.isValid();
jaroslav@123
   123
    }
jaroslav@123
   124
jaroslav@128
   125
    public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
jaroslav@123
   126
        return new JsClassLoader(parent) {
jaroslav@123
   127
            @Override
jaroslav@123
   128
            protected URL findResource(String name) {
jaroslav@123
   129
                List<URL> l = res(name, true);
jaroslav@123
   130
                return l.isEmpty() ? null : l.get(0);
jaroslav@123
   131
            }
jaroslav@123
   132
            
jaroslav@123
   133
            @Override
jaroslav@123
   134
            protected Enumeration<URL> findResources(String name) {
jaroslav@123
   135
                return Collections.enumeration(res(name, false));
jaroslav@123
   136
            }
jaroslav@123
   137
            
jaroslav@123
   138
            private List<URL> res(String name, boolean oneIsEnough) {
jaroslav@123
   139
                List<URL> l = new ArrayList<URL>();
jaroslav@123
   140
                f.findResources(name, l, oneIsEnough);
jaroslav@123
   141
                return l;
jaroslav@123
   142
            }
jaroslav@123
   143
            
jaroslav@123
   144
            @Override
jaroslav@123
   145
            protected Fn defineFn(String code, String... names) {
jaroslav@123
   146
                return d.defineFn(code, names);
jaroslav@123
   147
            }
jaroslav@163
   148
jaroslav@163
   149
            @Override
jaroslav@163
   150
            protected void loadScript(Reader code) throws Exception {
jaroslav@163
   151
                d.loadScript(code);
jaroslav@163
   152
            }
jaroslav@123
   153
        };
jaroslav@123
   154
    }
jaroslav@160
   155
jaroslav@189
   156
    static String callback(final String body) {
jaroslav@184
   157
        return new JsCallback() {
jaroslav@184
   158
            @Override
jaroslav@184
   159
            protected CharSequence callMethod(
jaroslav@184
   160
                String ident, String fqn, String method, String params
jaroslav@184
   161
            ) {
jaroslav@188
   162
                StringBuilder sb = new StringBuilder();
jaroslav@188
   163
                sb.append("vm.").append(mangle(fqn, method, params));
jaroslav@191
   164
                sb.append("(");
jaroslav@191
   165
                if (ident != null) {
jaroslav@191
   166
                    sb.append(ident);
jaroslav@191
   167
                }
jaroslav@188
   168
                return sb;
jaroslav@184
   169
            }
jaroslav@184
   170
jaroslav@184
   171
        }.parse(body);
jaroslav@160
   172
    }
jaroslav@163
   173
jaroslav@323
   174
    static void loadScript(ClassLoader jcl, String resource) {
jaroslav@163
   175
        final InputStream script = jcl.getResourceAsStream(resource);
jaroslav@163
   176
        if (script == null) {
jaroslav@163
   177
            throw new NullPointerException("Can't find " + resource);
jaroslav@163
   178
        }
jaroslav@163
   179
        try {
jaroslav@163
   180
            Reader isr = null;
jaroslav@163
   181
            try {
jaroslav@163
   182
                isr = new InputStreamReader(script, "UTF-8");
jaroslav@335
   183
                FnContext.currentPresenter(false).loadScript(isr);
jaroslav@163
   184
            } finally {
jaroslav@163
   185
                if (isr != null) {
jaroslav@163
   186
                    isr.close();
jaroslav@163
   187
                }
jaroslav@163
   188
            }
jaroslav@163
   189
        } catch (Exception ex) {
jaroslav@163
   190
            throw new IllegalStateException("Can't execute " + resource, ex);
jaroslav@163
   191
        } 
jaroslav@163
   192
    }
jaroslav@323
   193
    
jaroslav@456
   194
    
jaroslav@323
   195
    private static final class FindInClass extends ClassVisitor {
jaroslav@323
   196
        private String name;
jaroslav@323
   197
        private int found;
jaroslav@323
   198
        private ClassLoader loader;
jaroslav@349
   199
        private String resource;
jaroslav@323
   200
jaroslav@323
   201
        public FindInClass(ClassLoader l, ClassVisitor cv) {
jaroslav@323
   202
            super(Opcodes.ASM4, cv);
jaroslav@323
   203
            this.loader = l;
jaroslav@323
   204
        }
jaroslav@323
   205
jaroslav@323
   206
        @Override
jaroslav@323
   207
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
jaroslav@323
   208
            this.name = name;
jaroslav@323
   209
            super.visit(version, access, name, signature, superName, interfaces);
jaroslav@323
   210
        }
jaroslav@323
   211
jaroslav@323
   212
        @Override
jaroslav@323
   213
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@520
   214
            final AnnotationVisitor del = super.visitAnnotation(desc, visible);
jaroslav@323
   215
            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
jaroslav@520
   216
                return new LoadResource(del);
jaroslav@323
   217
            }
jaroslav@520
   218
            return del;
jaroslav@323
   219
        }
jaroslav@323
   220
jaroslav@323
   221
        @Override
jaroslav@323
   222
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
jaroslav@323
   223
            return new FindInMethod(access, name, desc,
jaroslav@323
   224
                    super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
jaroslav@323
   225
            );
jaroslav@323
   226
        }
jaroslav@323
   227
jaroslav@323
   228
        private final class FindInMethod extends MethodVisitor {
jaroslav@323
   229
jaroslav@323
   230
            private final String name;
jaroslav@323
   231
            private final String desc;
jaroslav@323
   232
            private final int access;
jaroslav@520
   233
            private FindInAnno fia;
jaroslav@323
   234
            private boolean bodyGenerated;
jaroslav@323
   235
jaroslav@323
   236
            public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
jaroslav@323
   237
                super(Opcodes.ASM4, mv);
jaroslav@323
   238
                this.access = access;
jaroslav@323
   239
                this.name = name;
jaroslav@323
   240
                this.desc = desc;
jaroslav@323
   241
            }
jaroslav@323
   242
jaroslav@323
   243
            @Override
jaroslav@323
   244
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
jaroslav@441
   245
                if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
jaroslav@323
   246
                    found++;
jaroslav@323
   247
                    return new FindInAnno();
jaroslav@323
   248
                }
jaroslav@323
   249
                return super.visitAnnotation(desc, visible);
jaroslav@323
   250
            }
jaroslav@323
   251
jaroslav@520
   252
            private void generateJSBody(FindInAnno fia) {
jaroslav@520
   253
                this.fia = fia;
jaroslav@323
   254
            }
jaroslav@323
   255
jaroslav@323
   256
            @Override
jaroslav@323
   257
            public void visitCode() {
jaroslav@520
   258
                if (fia == null) {
jaroslav@323
   259
                    return;
jaroslav@323
   260
                }
jaroslav@451
   261
                generateBody(true);
jaroslav@323
   262
            }
jaroslav@323
   263
jaroslav@451
   264
            private boolean generateBody(boolean hasCode) {
jaroslav@323
   265
                if (bodyGenerated) {
jaroslav@323
   266
                    return false;
jaroslav@323
   267
                }
jaroslav@323
   268
                bodyGenerated = true;
jaroslav@520
   269
                if (mv != null) {
jaroslav@520
   270
                    AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
jaroslav@520
   271
                    AnnotationVisitor varr = va.visitArray("args");
jaroslav@520
   272
                    for (String argName : fia.args) {
jaroslav@520
   273
                        varr.visit(null, argName);
jaroslav@520
   274
                    }
jaroslav@520
   275
                    varr.visitEnd();
jaroslav@520
   276
                    va.visit("javacall", fia.javacall);
jaroslav@520
   277
                    va.visit("body", fia.body);
jaroslav@520
   278
                    va.visitEnd();
jaroslav@520
   279
                }
jaroslav@520
   280
                
jaroslav@520
   281
                String body;
jaroslav@520
   282
                List<String> args;
jaroslav@520
   283
                if (fia.javacall) {
jaroslav@520
   284
                    body = callback(fia.body);
jaroslav@520
   285
                    args = new ArrayList<String>(fia.args);
jaroslav@520
   286
                    args.add("vm");
jaroslav@520
   287
                } else {
jaroslav@520
   288
                    body = fia.body;
jaroslav@520
   289
                    args = fia.args;
jaroslav@520
   290
                }
jaroslav@323
   291
jaroslav@323
   292
                super.visitFieldInsn(
jaroslav@323
   293
                        Opcodes.GETSTATIC, FindInClass.this.name,
jaroslav@323
   294
                        "$$fn$$" + name + "_" + found,
jtulach@838
   295
                        "Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   296
                );
jaroslav@323
   297
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   298
                super.visitMethodInsn(
jaroslav@323
   299
                        Opcodes.INVOKESTATIC,
jtulach@838
   300
                        "org/netbeans/html/boot/spi/Fn", "isValid",
jtulach@838
   301
                        "(Lorg/netbeans/html/boot/spi/Fn;)Z"
jaroslav@323
   302
                );
jaroslav@323
   303
                Label ifNotNull = new Label();
jaroslav@323
   304
                super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
jaroslav@323
   305
jaroslav@323
   306
                // init Fn
jaroslav@323
   307
                super.visitInsn(Opcodes.POP);
jaroslav@323
   308
                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jaroslav@323
   309
                super.visitLdcInsn(body);
jaroslav@323
   310
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   311
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
jaroslav@323
   312
                boolean needsVM = false;
jaroslav@323
   313
                for (int i = 0; i < args.size(); i++) {
jaroslav@323
   314
                    assert !needsVM;
jaroslav@323
   315
                    String argName = args.get(i);
jaroslav@323
   316
                    needsVM = "vm".equals(argName);
jaroslav@323
   317
                    super.visitInsn(Opcodes.DUP);
jaroslav@323
   318
                    super.visitIntInsn(Opcodes.BIPUSH, i);
jaroslav@323
   319
                    super.visitLdcInsn(argName);
jaroslav@323
   320
                    super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   321
                }
jaroslav@323
   322
                super.visitMethodInsn(Opcodes.INVOKESTATIC,
jtulach@838
   323
                        "org/netbeans/html/boot/spi/Fn", "define",
jtulach@838
   324
                        "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   325
                );
jaroslav@451
   326
                Label noPresenter = new Label();
jaroslav@451
   327
                if (hasCode) {
jaroslav@451
   328
                    super.visitInsn(Opcodes.DUP);
jaroslav@451
   329
                    super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
jaroslav@451
   330
                }
jaroslav@349
   331
                if (resource != null) {
jaroslav@349
   332
                    super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
jaroslav@349
   333
                    super.visitLdcInsn(resource);
jaroslav@349
   334
                    super.visitMethodInsn(Opcodes.INVOKESTATIC,
jtulach@838
   335
                            "org/netbeans/html/boot/spi/Fn", "preload",
jtulach@838
   336
                            "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@349
   337
                    );
jaroslav@349
   338
                }
jaroslav@323
   339
                super.visitInsn(Opcodes.DUP);
jaroslav@323
   340
                super.visitFieldInsn(
jaroslav@323
   341
                        Opcodes.PUTSTATIC, FindInClass.this.name,
jaroslav@323
   342
                        "$$fn$$" + name + "_" + found,
jtulach@838
   343
                        "Lorg/netbeans/html/boot/spi/Fn;"
jaroslav@323
   344
                );
jaroslav@323
   345
                // end of Fn init
jaroslav@323
   346
jaroslav@323
   347
                super.visitLabel(ifNotNull);
jaroslav@323
   348
jaroslav@323
   349
                final int offset;
jaroslav@323
   350
                if ((access & Opcodes.ACC_STATIC) == 0) {
jaroslav@323
   351
                    offset = 1;
jaroslav@323
   352
                    super.visitIntInsn(Opcodes.ALOAD, 0);
jaroslav@323
   353
                } else {
jaroslav@323
   354
                    offset = 0;
jaroslav@323
   355
                    super.visitInsn(Opcodes.ACONST_NULL);
jaroslav@323
   356
                }
jaroslav@323
   357
jaroslav@323
   358
                super.visitIntInsn(Opcodes.SIPUSH, args.size());
jaroslav@323
   359
                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
jaroslav@323
   360
jaroslav@323
   361
                class SV extends SignatureVisitor {
jaroslav@323
   362
jaroslav@323
   363
                    private boolean nowReturn;
jaroslav@323
   364
                    private Type returnType;
jaroslav@323
   365
                    private int index;
jaroslav@323
   366
                    private int loadIndex = offset;
jaroslav@323
   367
jaroslav@323
   368
                    public SV() {
jaroslav@323
   369
                        super(Opcodes.ASM4);
jaroslav@323
   370
                    }
jaroslav@323
   371
jaroslav@323
   372
                    @Override
jaroslav@323
   373
                    public void visitBaseType(char descriptor) {
jaroslav@323
   374
                        final Type t = Type.getType("" + descriptor);
jaroslav@323
   375
                        if (nowReturn) {
jaroslav@323
   376
                            returnType = t;
jaroslav@323
   377
                            return;
jaroslav@323
   378
                        }
jaroslav@323
   379
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   380
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   381
                        FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
jaroslav@323
   382
                        String factory;
jaroslav@323
   383
                        switch (descriptor) {
jaroslav@323
   384
                            case 'I':
jaroslav@323
   385
                                factory = "java/lang/Integer";
jaroslav@323
   386
                                break;
jaroslav@323
   387
                            case 'J':
jaroslav@323
   388
                                factory = "java/lang/Long";
jaroslav@323
   389
                                loadIndex++;
jaroslav@323
   390
                                break;
jaroslav@323
   391
                            case 'S':
jaroslav@323
   392
                                factory = "java/lang/Short";
jaroslav@323
   393
                                break;
jaroslav@323
   394
                            case 'F':
jaroslav@323
   395
                                factory = "java/lang/Float";
jaroslav@323
   396
                                break;
jaroslav@323
   397
                            case 'D':
jaroslav@323
   398
                                factory = "java/lang/Double";
jaroslav@323
   399
                                loadIndex++;
jaroslav@323
   400
                                break;
jaroslav@323
   401
                            case 'Z':
jaroslav@323
   402
                                factory = "java/lang/Boolean";
jaroslav@323
   403
                                break;
jaroslav@323
   404
                            case 'C':
jaroslav@323
   405
                                factory = "java/lang/Character";
jaroslav@323
   406
                                break;
jaroslav@323
   407
                            case 'B':
jaroslav@323
   408
                                factory = "java/lang/Byte";
jaroslav@323
   409
                                break;
jaroslav@323
   410
                            default:
jaroslav@323
   411
                                throw new IllegalStateException(t.toString());
jaroslav@323
   412
                        }
jaroslav@323
   413
                        FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
jaroslav@323
   414
                                factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
jaroslav@323
   415
                        );
jaroslav@323
   416
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   417
                    }
jaroslav@323
   418
jaroslav@323
   419
                    @Override
jaroslav@323
   420
                    public SignatureVisitor visitArrayType() {
jaroslav@323
   421
                        if (nowReturn) {
jaroslav@439
   422
                            return new SignatureVisitor(Opcodes.ASM4) {
jaroslav@439
   423
                                @Override
jaroslav@439
   424
                                public void visitClassType(String name) {
jaroslav@439
   425
                                    returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
jaroslav@439
   426
                                }
jaroslav@439
   427
                            };
jaroslav@323
   428
                        }
jaroslav@323
   429
                        loadObject();
jaroslav@323
   430
                        return new SignatureWriter();
jaroslav@323
   431
                    }
jaroslav@323
   432
jaroslav@323
   433
                    @Override
jaroslav@323
   434
                    public void visitClassType(String name) {
jaroslav@323
   435
                        if (nowReturn) {
jaroslav@323
   436
                            returnType = Type.getObjectType(name);
jaroslav@323
   437
                            return;
jaroslav@323
   438
                        }
jaroslav@323
   439
                        loadObject();
jaroslav@323
   440
                    }
jaroslav@323
   441
jaroslav@323
   442
                    @Override
jaroslav@323
   443
                    public SignatureVisitor visitReturnType() {
jaroslav@323
   444
                        nowReturn = true;
jaroslav@323
   445
                        return this;
jaroslav@323
   446
                    }
jaroslav@323
   447
jaroslav@323
   448
                    private void loadObject() {
jaroslav@323
   449
                        FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   450
                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
jaroslav@323
   451
                        FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
jaroslav@323
   452
                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   453
                    }
jaroslav@323
   454
jaroslav@323
   455
                }
jaroslav@323
   456
                SV sv = new SV();
jaroslav@323
   457
                SignatureReader sr = new SignatureReader(desc);
jaroslav@323
   458
                sr.accept(sv);
jaroslav@323
   459
jaroslav@323
   460
                if (needsVM) {
jaroslav@323
   461
                    FindInMethod.super.visitInsn(Opcodes.DUP);
jaroslav@323
   462
                    FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
jaroslav@323
   463
                    int lastSlash = FindInClass.this.name.lastIndexOf('/');
jaroslav@323
   464
                    String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
jaroslav@323
   465
                    FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
jaroslav@323
   466
                    FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
jaroslav@323
   467
                    FindInMethod.super.visitInsn(Opcodes.AASTORE);
jaroslav@323
   468
                }
jaroslav@323
   469
jaroslav@570
   470
                if (fia.wait4js) {
jaroslav@570
   471
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jtulach@838
   472
                            "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
jaroslav@570
   473
                    );
jaroslav@570
   474
                    switch (sv.returnType.getSort()) {
jaroslav@570
   475
                        case Type.VOID:
jaroslav@570
   476
                            super.visitInsn(Opcodes.RETURN);
jaroslav@570
   477
                            break;
jaroslav@570
   478
                        case Type.ARRAY:
jaroslav@570
   479
                        case Type.OBJECT:
jaroslav@570
   480
                            super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
jaroslav@570
   481
                            super.visitInsn(Opcodes.ARETURN);
jaroslav@570
   482
                            break;
jaroslav@570
   483
                        case Type.BOOLEAN:
jaroslav@570
   484
                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
jaroslav@570
   485
                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@570
   486
                                    "java/lang/Boolean", "booleanValue", "()Z"
jaroslav@570
   487
                            );
jaroslav@570
   488
                            super.visitInsn(Opcodes.IRETURN);
jaroslav@570
   489
                            break;
jaroslav@570
   490
                        default:
jaroslav@570
   491
                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
jaroslav@570
   492
                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jaroslav@570
   493
                                    "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
jaroslav@570
   494
                            );
jaroslav@570
   495
                            super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
jaroslav@570
   496
                    }
jaroslav@570
   497
                } else {
jaroslav@570
   498
                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
jtulach@838
   499
                            "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
jaroslav@570
   500
                    );
jaroslav@570
   501
                    super.visitInsn(Opcodes.RETURN);
jaroslav@323
   502
                }
jaroslav@451
   503
                if (hasCode) {
jaroslav@451
   504
                    super.visitLabel(noPresenter);
jaroslav@451
   505
                    super.visitCode();
jaroslav@451
   506
                }
jaroslav@323
   507
                return true;
jaroslav@323
   508
            }
jaroslav@323
   509
jaroslav@323
   510
            @Override
jaroslav@323
   511
            public void visitEnd() {
jaroslav@323
   512
                super.visitEnd();
jaroslav@520
   513
                if (fia != null) {
jaroslav@451
   514
                    if (generateBody(false)) {
jaroslav@323
   515
                        // native method
jaroslav@323
   516
                        super.visitMaxs(1, 0);
jaroslav@323
   517
                    }
jaroslav@323
   518
                    FindInClass.this.visitField(
jaroslav@323
   519
                            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
jaroslav@323
   520
                            "$$fn$$" + name + "_" + found,
jtulach@838
   521
                            "Lorg/netbeans/html/boot/spi/Fn;",
jaroslav@323
   522
                            null, null
jaroslav@323
   523
                    );
jaroslav@323
   524
                }
jaroslav@323
   525
            }
jaroslav@323
   526
jaroslav@323
   527
            private final class FindInAnno extends AnnotationVisitor {
jaroslav@323
   528
jaroslav@520
   529
                List<String> args = new ArrayList<String>();
jaroslav@520
   530
                String body;
jaroslav@520
   531
                boolean javacall = false;
jaroslav@570
   532
                boolean wait4js = true;
jaroslav@323
   533
jaroslav@323
   534
                public FindInAnno() {
jaroslav@323
   535
                    super(Opcodes.ASM4);
jaroslav@323
   536
                }
jaroslav@323
   537
jaroslav@323
   538
                @Override
jaroslav@323
   539
                public void visit(String name, Object value) {
jaroslav@323
   540
                    if (name == null) {
jaroslav@323
   541
                        args.add((String) value);
jaroslav@323
   542
                        return;
jaroslav@323
   543
                    }
jaroslav@323
   544
                    if (name.equals("javacall")) { // NOI18N
jaroslav@323
   545
                        javacall = (Boolean) value;
jaroslav@323
   546
                        return;
jaroslav@323
   547
                    }
jaroslav@570
   548
                    if (name.equals("wait4js")) { // NOI18N
jaroslav@570
   549
                        wait4js = (Boolean) value;
jaroslav@570
   550
                        return;
jaroslav@570
   551
                    }
jaroslav@323
   552
                    assert name.equals("body");
jaroslav@323
   553
                    body = (String) value;
jaroslav@323
   554
                }
jaroslav@323
   555
jaroslav@323
   556
                @Override
jaroslav@323
   557
                public AnnotationVisitor visitArray(String name) {
jaroslav@323
   558
                    return this;
jaroslav@323
   559
                }
jaroslav@323
   560
jaroslav@323
   561
                @Override
jaroslav@323
   562
                public void visitEnd() {
jaroslav@323
   563
                    if (body != null) {
jaroslav@520
   564
                        generateJSBody(this);
jaroslav@323
   565
                    }
jaroslav@323
   566
                }
jaroslav@323
   567
            }
jaroslav@323
   568
        }
jaroslav@323
   569
jaroslav@323
   570
        private final class LoadResource extends AnnotationVisitor {
jaroslav@520
   571
            public LoadResource(AnnotationVisitor av) {
jaroslav@520
   572
                super(Opcodes.ASM4, av);
jaroslav@323
   573
            }
jaroslav@323
   574
jaroslav@323
   575
            @Override
jaroslav@323
   576
            public void visit(String attrName, Object value) {
jaroslav@520
   577
                super.visit(attrName, value);
jaroslav@323
   578
                String relPath = (String) value;
jaroslav@323
   579
                if (relPath.startsWith("/")) {
jaroslav@349
   580
                    resource = relPath;
jaroslav@323
   581
                } else {
jaroslav@323
   582
                    int last = name.lastIndexOf('/');
jaroslav@323
   583
                    String fullPath = name.substring(0, last + 1) + relPath;
jaroslav@349
   584
                    resource = fullPath;
jaroslav@323
   585
                }
jaroslav@323
   586
            }
jaroslav@323
   587
        }
jaroslav@323
   588
    }
jaroslav@323
   589
jaroslav@323
   590
    private static class ClassWriterEx extends ClassWriter {
jaroslav@323
   591
jaroslav@323
   592
        private ClassLoader loader;
jaroslav@323
   593
jaroslav@323
   594
        public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
jaroslav@323
   595
            super(classReader, flags);
jaroslav@323
   596
            this.loader = l;
jaroslav@323
   597
        }
jaroslav@323
   598
jaroslav@323
   599
        @Override
jaroslav@323
   600
        protected String getCommonSuperClass(final String type1, final String type2) {
jaroslav@323
   601
            Class<?> c, d;
jaroslav@323
   602
            try {
jaroslav@323
   603
                c = Class.forName(type1.replace('/', '.'), false, loader);
jaroslav@323
   604
                d = Class.forName(type2.replace('/', '.'), false, loader);
jaroslav@323
   605
            } catch (Exception e) {
jaroslav@323
   606
                throw new RuntimeException(e.toString());
jaroslav@323
   607
            }
jaroslav@323
   608
            if (c.isAssignableFrom(d)) {
jaroslav@323
   609
                return type1;
jaroslav@323
   610
            }
jaroslav@323
   611
            if (d.isAssignableFrom(c)) {
jaroslav@323
   612
                return type2;
jaroslav@323
   613
            }
jaroslav@323
   614
            if (c.isInterface() || d.isInterface()) {
jaroslav@323
   615
                return "java/lang/Object";
jaroslav@323
   616
            } else {
jaroslav@323
   617
                do {
jaroslav@323
   618
                    c = c.getSuperclass();
jaroslav@323
   619
                } while (!c.isAssignableFrom(d));
jaroslav@323
   620
                return c.getName().replace('.', '/');
jaroslav@323
   621
            }
jaroslav@323
   622
        }
jaroslav@323
   623
    }
jaroslav@123
   624
}