vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
changeset 22 b9318fe303cd
parent 21 d8807b6a636a
child 30 7efb52f76270
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Mon Sep 24 11:07:38 2012 +0200
     1.3 @@ -0,0 +1,793 @@
     1.4 +/*
     1.5 +Java 4 Browser Bytecode Translator
     1.6 +Copyright (C) 2012-2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     1.7 +
     1.8 +This program is free software: you can redistribute it and/or modify
     1.9 +it under the terms of the GNU General Public License as published by
    1.10 +the Free Software Foundation, version 2 of the License.
    1.11 +
    1.12 +This program is distributed in the hope that it will be useful,
    1.13 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 +GNU General Public License for more details.
    1.16 +
    1.17 +You should have received a copy of the GNU General Public License
    1.18 +along with this program. Look for COPYING file in the top folder.
    1.19 +If not, see http://opensource.org/licenses/GPL-2.0.
    1.20 +*/
    1.21 +package org.apidesign.vm4brwsr;
    1.22 +
    1.23 +import java.io.IOException;
    1.24 +import java.io.InputStream;
    1.25 +import java.util.ArrayList;
    1.26 +import java.util.Collection;
    1.27 +import java.util.List;
    1.28 +import static org.netbeans.modules.classfile.ByteCodes.*;
    1.29 +import org.netbeans.modules.classfile.CPClassInfo;
    1.30 +import org.netbeans.modules.classfile.CPEntry;
    1.31 +import org.netbeans.modules.classfile.CPFieldInfo;
    1.32 +import org.netbeans.modules.classfile.CPMethodInfo;
    1.33 +import org.netbeans.modules.classfile.CPStringInfo;
    1.34 +import org.netbeans.modules.classfile.ClassFile;
    1.35 +import org.netbeans.modules.classfile.ClassName;
    1.36 +import org.netbeans.modules.classfile.Code;
    1.37 +import org.netbeans.modules.classfile.Method;
    1.38 +import org.netbeans.modules.classfile.Parameter;
    1.39 +import org.netbeans.modules.classfile.Variable;
    1.40 +
    1.41 +/** Translator of the code inside class files to JavaScript.
    1.42 + *
    1.43 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.44 + */
    1.45 +public final class ByteCodeToJavaScript {
    1.46 +    private final ClassFile jc;
    1.47 +    private final Appendable out;
    1.48 +    private final Collection<? super String> references;
    1.49 +
    1.50 +    private ByteCodeToJavaScript(
    1.51 +        ClassFile jc, Appendable out, Collection<? super String> references
    1.52 +    ) {
    1.53 +        this.jc = jc;
    1.54 +        this.out = out;
    1.55 +        this.references = references;
    1.56 +    }
    1.57 +
    1.58 +    /**
    1.59 +     * Converts a given class file to a JavaScript version.
    1.60 +     *
    1.61 +     * @param classFile input stream with code of the .class file
    1.62 +     * @param out a {@link StringBuilder} or similar to generate the output to
    1.63 +     * @param references a write only collection where the system adds list of
    1.64 +     *   other classes that were referenced and should be loaded in order the
    1.65 +     *   generated JavaScript code works properly. The names are in internal 
    1.66 +     *   JVM form so String is <code>java/lang/String</code>. Can be <code>null</code>
    1.67 +     *   if one is not interested in knowing references
    1.68 +     * @throws IOException if something goes wrong during read or write or translating
    1.69 +     */
    1.70 +    
    1.71 +    public static void compile(
    1.72 +        InputStream classFile, Appendable out,
    1.73 +        Collection<? super String> references
    1.74 +    ) throws IOException {
    1.75 +        ClassFile jc = new ClassFile(classFile, true);
    1.76 +        ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(
    1.77 +            jc, out, references
    1.78 +        );
    1.79 +        List<String> toInitilize = new ArrayList<String>();
    1.80 +        for (Method m : jc.getMethods()) {
    1.81 +            if (m.isStatic()) {
    1.82 +                compiler.generateStaticMethod(m, toInitilize);
    1.83 +            } else {
    1.84 +                compiler.generateInstanceMethod(m);
    1.85 +            }
    1.86 +        }
    1.87 +        for (Variable v : jc.getVariables()) {
    1.88 +            if (v.isStatic()) {
    1.89 +                compiler.generateStaticField(v);
    1.90 +            }
    1.91 +        }
    1.92 +        
    1.93 +        final String className = jc.getName().getInternalName().replace('/', '_');
    1.94 +        out.append("\nfunction ").append(className);
    1.95 +        out.append("() {");
    1.96 +        for (Method m : jc.getMethods()) {
    1.97 +            if (!m.isStatic()) {
    1.98 +                compiler.generateMethodReference(m);
    1.99 +            }
   1.100 +        }
   1.101 +        for (Variable v : jc.getVariables()) {
   1.102 +            if (!v.isStatic()) {
   1.103 +                out.append("\n  this." + v.getName() + " = 0;");
   1.104 +            }
   1.105 +        }
   1.106 +        out.append("\n  this.$instOf_").append(className).append(" = true;");
   1.107 +        out.append("\n}");
   1.108 +        ClassName sc = jc.getSuperClass();
   1.109 +        if (sc != null) {
   1.110 +            out.append("\n").append(className)
   1.111 +               .append(".prototype = new ").append(sc.getInternalName().replace('/', '_'));
   1.112 +        }
   1.113 +        for (String init : toInitilize) {
   1.114 +            out.append("\n").append(init).append("();");
   1.115 +        }
   1.116 +    }
   1.117 +    private void generateStaticMethod(Method m, List<String> toInitilize) throws IOException {
   1.118 +        final String mn = findMethodName(m);
   1.119 +        out.append("\nfunction ").append(
   1.120 +            jc.getName().getInternalName().replace('/', '_')
   1.121 +        ).append('_').append(mn);
   1.122 +        if (mn.equals("classV")) {
   1.123 +            toInitilize.add(jc.getName().getInternalName().replace('/', '_') + '_' + mn);
   1.124 +        }
   1.125 +        out.append('(');
   1.126 +        String space = "";
   1.127 +        List<Parameter> args = m.getParameters();
   1.128 +        for (int index = 0, i = 0; i < args.size(); i++) {
   1.129 +            out.append(space);
   1.130 +            out.append("arg").append(String.valueOf(index));
   1.131 +            space = ",";
   1.132 +            final String desc = args.get(i).getDescriptor();
   1.133 +            if ("D".equals(desc) || "J".equals(desc)) {
   1.134 +                index += 2;
   1.135 +            } else {
   1.136 +                index++;
   1.137 +            }
   1.138 +        }
   1.139 +        out.append(") {").append("\n");
   1.140 +        final Code code = m.getCode();
   1.141 +        if (code != null) {
   1.142 +            int len = code.getMaxLocals();
   1.143 +            for (int index = args.size(), i = args.size(); i < len; i++) {
   1.144 +                out.append("  var ");
   1.145 +                out.append("arg").append(String.valueOf(i)).append(";\n");
   1.146 +            }
   1.147 +            out.append("  var stack = new Array();\n");
   1.148 +            produceCode(code.getByteCodes());
   1.149 +        } else {
   1.150 +            out.append("  /* no code found for ").append(m.getTypeSignature()).append(" */\n");
   1.151 +        }
   1.152 +        out.append("}");
   1.153 +    }
   1.154 +    
   1.155 +    private void generateMethodReference(Method m) throws IOException {
   1.156 +        final String name = findMethodName(m);
   1.157 +        out.append("\n  this.").append(name).append(" = ")
   1.158 +           .append(jc.getName().getInternalName().replace('/', '_'))
   1.159 +           .append('_').append(name).append(";");
   1.160 +    }
   1.161 +    
   1.162 +    private void generateInstanceMethod(Method m) throws IOException {
   1.163 +        out.append("\nfunction ").append(
   1.164 +            jc.getName().getInternalName().replace('/', '_')
   1.165 +        ).append('_').append(findMethodName(m));
   1.166 +        out.append("(arg0");
   1.167 +        String space = ",";
   1.168 +        List<Parameter> args = m.getParameters();
   1.169 +        for (int index = 1, i = 0; i < args.size(); i++) {
   1.170 +            out.append(space);
   1.171 +            out.append("arg").append(String.valueOf(index));
   1.172 +            final String desc = args.get(i).getDescriptor();
   1.173 +            if ("D".equals(desc) || "J".equals(desc)) {
   1.174 +                index += 2;
   1.175 +            } else {
   1.176 +                index++;
   1.177 +            }
   1.178 +        }
   1.179 +        out.append(") {").append("\n");
   1.180 +        final Code code = m.getCode();
   1.181 +        if (code != null) {
   1.182 +            int len = code.getMaxLocals();
   1.183 +            for (int index = args.size(), i = args.size(); i < len; i++) {
   1.184 +                out.append("  var ");
   1.185 +                out.append("arg").append(String.valueOf(i + 1)).append(";\n");
   1.186 +            }
   1.187 +            out.append(";\n  var stack = new Array(");
   1.188 +            out.append(Integer.toString(code.getMaxStack()));
   1.189 +            out.append(");\n");
   1.190 +            produceCode(code.getByteCodes());
   1.191 +        } else {
   1.192 +            out.append("  /* no code found for ").append(m.getTypeSignature()).append(" */\n");
   1.193 +        }
   1.194 +        out.append("}");
   1.195 +    }
   1.196 +
   1.197 +    private void produceCode(byte[] byteCodes) throws IOException {
   1.198 +        out.append("\n  var gt = 0;\n  for(;;) switch(gt) {\n");
   1.199 +        for (int i = 0; i < byteCodes.length; i++) {
   1.200 +            int prev = i;
   1.201 +            out.append("    case " + i).append(": ");
   1.202 +            final int c = (byteCodes[i] + 256) % 256;
   1.203 +            switch (c) {
   1.204 +                case bc_aload_0:
   1.205 +                case bc_iload_0:
   1.206 +                case bc_lload_0:
   1.207 +                case bc_fload_0:
   1.208 +                case bc_dload_0:
   1.209 +                    out.append("stack.push(arg0);");
   1.210 +                    break;
   1.211 +                case bc_aload_1:
   1.212 +                case bc_iload_1:
   1.213 +                case bc_lload_1:
   1.214 +                case bc_fload_1:
   1.215 +                case bc_dload_1:
   1.216 +                    out.append("stack.push(arg1);");
   1.217 +                    break;
   1.218 +                case bc_aload_2:
   1.219 +                case bc_iload_2:
   1.220 +                case bc_lload_2:
   1.221 +                case bc_fload_2:
   1.222 +                case bc_dload_2:
   1.223 +                    out.append("stack.push(arg2);");
   1.224 +                    break;
   1.225 +                case bc_aload_3:
   1.226 +                case bc_iload_3:
   1.227 +                case bc_lload_3:
   1.228 +                case bc_fload_3:
   1.229 +                case bc_dload_3:
   1.230 +                    out.append("stack.push(arg3);");
   1.231 +                    break;
   1.232 +                case bc_iload:
   1.233 +                case bc_lload:
   1.234 +                case bc_fload:
   1.235 +                case bc_dload:
   1.236 +                case bc_aload: {
   1.237 +                    final int indx = (byteCodes[++i] + 256) % 256;
   1.238 +                    out.append("stack.push(arg").append(indx + ");");
   1.239 +                    break;
   1.240 +                }
   1.241 +                case bc_astore_0:
   1.242 +                case bc_istore_0:
   1.243 +                case bc_lstore_0:
   1.244 +                case bc_fstore_0:
   1.245 +                case bc_dstore_0:
   1.246 +                    out.append("arg0 = stack.pop();");
   1.247 +                    break;
   1.248 +                case bc_astore_1:
   1.249 +                case bc_istore_1:
   1.250 +                case bc_lstore_1:
   1.251 +                case bc_fstore_1:
   1.252 +                case bc_dstore_1:
   1.253 +                    out.append("arg1 = stack.pop();");
   1.254 +                    break;
   1.255 +                case bc_astore_2:
   1.256 +                case bc_istore_2:
   1.257 +                case bc_lstore_2:
   1.258 +                case bc_fstore_2:
   1.259 +                case bc_dstore_2:
   1.260 +                    out.append("arg2 = stack.pop();");
   1.261 +                    break;
   1.262 +                case bc_astore_3:
   1.263 +                case bc_istore_3:
   1.264 +                case bc_lstore_3:
   1.265 +                case bc_fstore_3:
   1.266 +                case bc_dstore_3:
   1.267 +                    out.append("arg3 = stack.pop();");
   1.268 +                    break;
   1.269 +                case bc_iadd:
   1.270 +                case bc_ladd:
   1.271 +                case bc_fadd:
   1.272 +                case bc_dadd:
   1.273 +                    out.append("stack.push(stack.pop() + stack.pop());");
   1.274 +                    break;
   1.275 +                case bc_isub:
   1.276 +                case bc_lsub:
   1.277 +                case bc_fsub:
   1.278 +                case bc_dsub:
   1.279 +                    out.append("{ var tmp = stack.pop(); stack.push(stack.pop() - tmp); }");
   1.280 +                    break;
   1.281 +                case bc_imul:
   1.282 +                case bc_lmul:
   1.283 +                case bc_fmul:
   1.284 +                case bc_dmul:
   1.285 +                    out.append("stack.push(stack.pop() * stack.pop());");
   1.286 +                    break;
   1.287 +                case bc_idiv:
   1.288 +                case bc_ldiv:
   1.289 +                    out.append("{ var tmp = stack.pop(); stack.push(Math.floor(stack.pop() / tmp)); }");
   1.290 +                    break;
   1.291 +                case bc_fdiv:
   1.292 +                case bc_ddiv:
   1.293 +                    out.append("{ var tmp = stack.pop(); stack.push(stack.pop() / tmp); }");
   1.294 +                    break;
   1.295 +                case bc_iand:
   1.296 +                case bc_land:
   1.297 +                    out.append("stack.push(stack.pop() & stack.pop());");
   1.298 +                    break;
   1.299 +                case bc_ior:
   1.300 +                case bc_lor:
   1.301 +                    out.append("stack.push(stack.pop() | stack.pop());");
   1.302 +                    break;
   1.303 +                case bc_ixor:
   1.304 +                case bc_lxor:
   1.305 +                    out.append("stack.push(stack.pop() ^ stack.pop());");
   1.306 +                    break;
   1.307 +                case bc_iinc: {
   1.308 +                    final int varIndx = (byteCodes[++i] + 256) % 256;
   1.309 +                    final int incrBy = (byteCodes[++i] + 256) % 256;
   1.310 +                    if (incrBy == 1) {
   1.311 +                        out.append("arg" + varIndx).append("++;");
   1.312 +                    } else {
   1.313 +                        out.append("arg" + varIndx).append(" += " + incrBy).append(";");
   1.314 +                    }
   1.315 +                    break;
   1.316 +                }
   1.317 +                case bc_return:
   1.318 +                    out.append("return;");
   1.319 +                    break;
   1.320 +                case bc_ireturn:
   1.321 +                case bc_lreturn:
   1.322 +                case bc_freturn:
   1.323 +                case bc_dreturn:
   1.324 +                case bc_areturn:
   1.325 +                    out.append("return stack.pop();");
   1.326 +                    break;
   1.327 +                case bc_i2l:
   1.328 +                case bc_i2f:
   1.329 +                case bc_i2d:
   1.330 +                case bc_l2i:
   1.331 +                    // max int check?
   1.332 +                case bc_l2f:
   1.333 +                case bc_l2d:
   1.334 +                case bc_f2d:
   1.335 +                case bc_d2f:
   1.336 +                    out.append("/* number conversion */");
   1.337 +                    break;
   1.338 +                case bc_f2i:
   1.339 +                case bc_f2l:
   1.340 +                case bc_d2i:
   1.341 +                case bc_d2l:
   1.342 +                    out.append("stack.push(Math.floor(stack.pop()));");
   1.343 +                    break;
   1.344 +                case bc_i2b:
   1.345 +                case bc_i2c:
   1.346 +                case bc_i2s:
   1.347 +                    out.append("/* number conversion */");
   1.348 +                    break;
   1.349 +                case bc_iconst_0:
   1.350 +                case bc_dconst_0:
   1.351 +                case bc_lconst_0:
   1.352 +                case bc_fconst_0:
   1.353 +                    out.append("stack.push(0);");
   1.354 +                    break;
   1.355 +                case bc_iconst_1:
   1.356 +                case bc_lconst_1:
   1.357 +                case bc_fconst_1:
   1.358 +                case bc_dconst_1:
   1.359 +                    out.append("stack.push(1);");
   1.360 +                    break;
   1.361 +                case bc_iconst_2:
   1.362 +                case bc_fconst_2:
   1.363 +                    out.append("stack.push(2);");
   1.364 +                    break;
   1.365 +                case bc_iconst_3:
   1.366 +                    out.append("stack.push(3);");
   1.367 +                    break;
   1.368 +                case bc_iconst_4:
   1.369 +                    out.append("stack.push(4);");
   1.370 +                    break;
   1.371 +                case bc_iconst_5:
   1.372 +                    out.append("stack.push(5);");
   1.373 +                    break;
   1.374 +                case bc_ldc: {
   1.375 +                    int indx = byteCodes[++i];
   1.376 +                    CPEntry entry = jc.getConstantPool().get(indx);
   1.377 +                    String v = encodeConstant(entry);
   1.378 +                    out.append("stack.push(").append(v).append(");");
   1.379 +                    break;
   1.380 +                }
   1.381 +                case bc_ldc_w:
   1.382 +                case bc_ldc2_w: {
   1.383 +                    int indx = readIntArg(byteCodes, i);
   1.384 +                    CPEntry entry = jc.getConstantPool().get(indx);
   1.385 +                    i += 2;
   1.386 +                    String v = encodeConstant(entry);
   1.387 +                    out.append("stack.push(").append(v).append(");");
   1.388 +                    break;
   1.389 +                }
   1.390 +                case bc_lcmp:
   1.391 +                case bc_fcmpl:
   1.392 +                case bc_fcmpg:
   1.393 +                case bc_dcmpl:
   1.394 +                case bc_dcmpg: {
   1.395 +                    out.append("{ var delta = stack.pop() - stack.pop(); stack.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }");
   1.396 +                    break;
   1.397 +                }
   1.398 +                case bc_if_icmpeq: {
   1.399 +                    i = generateIf(byteCodes, i, "==");
   1.400 +                    break;
   1.401 +                }
   1.402 +                case bc_ifeq: {
   1.403 +                    int indx = i + readIntArg(byteCodes, i);
   1.404 +                    out.append("if (stack.pop() == 0) { gt = " + indx);
   1.405 +                    out.append("; continue; }");
   1.406 +                    i += 2;
   1.407 +                    break;
   1.408 +                }
   1.409 +                case bc_ifne: {
   1.410 +                    int indx = i + readIntArg(byteCodes, i);
   1.411 +                    out.append("if (stack.pop() != 0) { gt = " + indx);
   1.412 +                    out.append("; continue; }");
   1.413 +                    i += 2;
   1.414 +                    break;
   1.415 +                }
   1.416 +                case bc_iflt: {
   1.417 +                    int indx = i + readIntArg(byteCodes, i);
   1.418 +                    out.append("if (stack.pop() < 0) { gt = " + indx);
   1.419 +                    out.append("; continue; }");
   1.420 +                    i += 2;
   1.421 +                    break;
   1.422 +                }
   1.423 +                case bc_ifle: {
   1.424 +                    int indx = i + readIntArg(byteCodes, i);
   1.425 +                    out.append("if (stack.pop() <= 0) { gt = " + indx);
   1.426 +                    out.append("; continue; }");
   1.427 +                    i += 2;
   1.428 +                    break;
   1.429 +                }
   1.430 +                case bc_ifgt: {
   1.431 +                    int indx = i + readIntArg(byteCodes, i);
   1.432 +                    out.append("if (stack.pop() > 0) { gt = " + indx);
   1.433 +                    out.append("; continue; }");
   1.434 +                    i += 2;
   1.435 +                    break;
   1.436 +                }
   1.437 +                case bc_ifge: {
   1.438 +                    int indx = i + readIntArg(byteCodes, i);
   1.439 +                    out.append("if (stack.pop() >= 0) { gt = " + indx);
   1.440 +                    out.append("; continue; }");
   1.441 +                    i += 2;
   1.442 +                    break;
   1.443 +                }
   1.444 +                case bc_ifnonnull: {
   1.445 +                    int indx = i + readIntArg(byteCodes, i);
   1.446 +                    out.append("if (stack.pop()) { gt = " + indx);
   1.447 +                    out.append("; continue; }");
   1.448 +                    i += 2;
   1.449 +                    break;
   1.450 +                }
   1.451 +                case bc_ifnull: {
   1.452 +                    int indx = i + readIntArg(byteCodes, i);
   1.453 +                    out.append("if (!stack.pop()) { gt = " + indx);
   1.454 +                    out.append("; continue; }");
   1.455 +                    i += 2;
   1.456 +                    break;
   1.457 +                }
   1.458 +                case bc_if_icmpne:
   1.459 +                    i = generateIf(byteCodes, i, "!=");
   1.460 +                    break;
   1.461 +                case bc_if_icmplt:
   1.462 +                    i = generateIf(byteCodes, i, ">");
   1.463 +                    break;
   1.464 +                case bc_if_icmple:
   1.465 +                    i = generateIf(byteCodes, i, ">=");
   1.466 +                    break;
   1.467 +                case bc_if_icmpgt:
   1.468 +                    i = generateIf(byteCodes, i, "<");
   1.469 +                    break;
   1.470 +                case bc_if_icmpge:
   1.471 +                    i = generateIf(byteCodes, i, "<=");
   1.472 +                    break;
   1.473 +                case bc_goto: {
   1.474 +                    int indx = i + readIntArg(byteCodes, i);
   1.475 +                    out.append("gt = " + indx).append("; continue;");
   1.476 +                    i += 2;
   1.477 +                    break;
   1.478 +                }
   1.479 +                case bc_invokeinterface:
   1.480 +                case bc_invokevirtual:
   1.481 +                    i = invokeVirtualMethod(byteCodes, i);
   1.482 +                    break;
   1.483 +                case bc_invokespecial:
   1.484 +                    i = invokeStaticMethod(byteCodes, i, false);
   1.485 +                    break;
   1.486 +                case bc_invokestatic:
   1.487 +                    i = invokeStaticMethod(byteCodes, i, true);
   1.488 +                    break;
   1.489 +                case bc_new: {
   1.490 +                    int indx = readIntArg(byteCodes, i);
   1.491 +                    CPClassInfo ci = jc.getConstantPool().getClass(indx);
   1.492 +                    out.append("stack.push(");
   1.493 +                    out.append("new ").append(ci.getClassName().getInternalName().replace('/','_'));
   1.494 +                    out.append(");");
   1.495 +                    addReference(ci.getClassName().getInternalName());
   1.496 +                    i += 2;
   1.497 +                    break;
   1.498 +                }
   1.499 +                case bc_newarray: {
   1.500 +                    int type = byteCodes[i++];
   1.501 +                    out.append("stack.push(new Array(stack.pop()));");
   1.502 +                    break;
   1.503 +                }
   1.504 +                case bc_anewarray: {
   1.505 +                    i += 2; // skip type of array
   1.506 +                    out.append("stack.push(new Array(stack.pop()));");
   1.507 +                    break;
   1.508 +                }
   1.509 +                case bc_arraylength:
   1.510 +                    out.append("stack.push(stack.pop().length);");
   1.511 +                    break;
   1.512 +                case bc_iastore:
   1.513 +                case bc_lastore:
   1.514 +                case bc_fastore:
   1.515 +                case bc_dastore:
   1.516 +                case bc_aastore:
   1.517 +                case bc_bastore:
   1.518 +                case bc_castore:
   1.519 +                case bc_sastore: {
   1.520 +                    out.append("{ var value = stack.pop(); var indx = stack.pop(); stack.pop()[indx] = value; }");
   1.521 +                    break;
   1.522 +                }
   1.523 +                case bc_iaload:
   1.524 +                case bc_laload:
   1.525 +                case bc_faload:
   1.526 +                case bc_daload:
   1.527 +                case bc_aaload:
   1.528 +                case bc_baload:
   1.529 +                case bc_caload:
   1.530 +                case bc_saload: {
   1.531 +                    out.append("{ var indx = stack.pop(); stack.push(stack.pop()[indx]); }");
   1.532 +                    break;
   1.533 +                }
   1.534 +                case bc_dup:
   1.535 +                    out.append("stack.push(stack[stack.length - 1]);");
   1.536 +                    break;
   1.537 +                case bc_bipush:
   1.538 +                    out.append("stack.push(" + byteCodes[++i] + ");");
   1.539 +                    break;
   1.540 +                case bc_getfield: {
   1.541 +                    int indx = readIntArg(byteCodes, i);
   1.542 +                    CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
   1.543 +                    out.append("stack.push(stack.pop().").append(fi.getFieldName()).append(");");
   1.544 +                    i += 2;
   1.545 +                    break;
   1.546 +                }
   1.547 +                case bc_getstatic: {
   1.548 +                    int indx = readIntArg(byteCodes, i);
   1.549 +                    CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
   1.550 +                    final String in = fi.getClassName().getInternalName();
   1.551 +                    out.append("stack.push(").append(in.replace('/', '_'));
   1.552 +                    out.append('_').append(fi.getFieldName()).append(");");
   1.553 +                    i += 2;
   1.554 +                    addReference(in);
   1.555 +                    break;
   1.556 +                }
   1.557 +                case bc_putstatic: {
   1.558 +                    int indx = readIntArg(byteCodes, i);
   1.559 +                    CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
   1.560 +                    final String in = fi.getClassName().getInternalName();
   1.561 +                    out.append(in.replace('/', '_'));
   1.562 +                    out.append('_').append(fi.getFieldName()).append(" = stack.pop();");
   1.563 +                    i += 2;
   1.564 +                    addReference(in);
   1.565 +                    break;
   1.566 +                }
   1.567 +                case bc_putfield: {
   1.568 +                    int indx = readIntArg(byteCodes, i);
   1.569 +                    CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
   1.570 +                    out.append("{ var v = stack.pop(); stack.pop().")
   1.571 +                       .append(fi.getFieldName()).append(" = v; }");
   1.572 +                    i += 2;
   1.573 +                    break;
   1.574 +                }
   1.575 +                case bc_instanceof: {
   1.576 +                    int indx = readIntArg(byteCodes, i);
   1.577 +                    CPClassInfo ci = jc.getConstantPool().getClass(indx);
   1.578 +                    out.append("stack.push(stack.pop().$instOf_")
   1.579 +                       .append(ci.getClassName().getInternalName().replace('/', '_'))
   1.580 +                       .append(" ? 1 : 0);");
   1.581 +                    i += 2;
   1.582 +                }
   1.583 +                    
   1.584 +            }
   1.585 +            out.append(" /*");
   1.586 +            for (int j = prev; j <= i; j++) {
   1.587 +                out.append(" ");
   1.588 +                final int cc = (byteCodes[j] + 256) % 256;
   1.589 +                out.append(Integer.toString(cc));
   1.590 +            }
   1.591 +            out.append("*/\n");
   1.592 +        }
   1.593 +        out.append("  }\n");
   1.594 +    }
   1.595 +
   1.596 +    private int generateIf(byte[] byteCodes, int i, final String test) throws IOException {
   1.597 +        int indx = i + readIntArg(byteCodes, i);
   1.598 +        out.append("if (stack.pop() ").append(test).append(" stack.pop()) { gt = " + indx);
   1.599 +        out.append("; continue; }");
   1.600 +        return i + 2;
   1.601 +    }
   1.602 +
   1.603 +    private int readIntArg(byte[] byteCodes, int offsetInstruction) {
   1.604 +        final int indxHi = byteCodes[offsetInstruction + 1] << 8;
   1.605 +        final int indxLo = byteCodes[offsetInstruction + 2];
   1.606 +        return (indxHi & 0xffffff00) | (indxLo & 0xff);
   1.607 +    }
   1.608 +    
   1.609 +    private static int countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig) {
   1.610 +        int cnt = 0;
   1.611 +        int i = 0;
   1.612 +        Boolean count = null;
   1.613 +        int firstPos = sig.length();
   1.614 +        while (i < descriptor.length()) {
   1.615 +            char ch = descriptor.charAt(i++);
   1.616 +            switch (ch) {
   1.617 +                case '(':
   1.618 +                    count = true;
   1.619 +                    continue;
   1.620 +                case ')':
   1.621 +                    count = false;
   1.622 +                    continue;
   1.623 +                case 'B': 
   1.624 +                case 'C': 
   1.625 +                case 'D': 
   1.626 +                case 'F': 
   1.627 +                case 'I': 
   1.628 +                case 'J': 
   1.629 +                case 'S': 
   1.630 +                case 'Z': 
   1.631 +                    if (count) {
   1.632 +                        cnt++;
   1.633 +                        sig.append(ch);
   1.634 +                    } else {
   1.635 +                        hasReturnType[0] = true;
   1.636 +                        sig.insert(firstPos, ch);
   1.637 +                    }
   1.638 +                    continue;
   1.639 +                case 'V': 
   1.640 +                    assert !count;
   1.641 +                    hasReturnType[0] = false;
   1.642 +                    sig.insert(firstPos, 'V');
   1.643 +                    continue;
   1.644 +                case 'L':
   1.645 +                    int next = descriptor.indexOf(';', i);
   1.646 +                    if (count) {
   1.647 +                        cnt++;
   1.648 +                        sig.append(ch);
   1.649 +                        sig.append(descriptor.substring(i, next).replace('/', '_'));
   1.650 +                    } else {
   1.651 +                        sig.insert(firstPos, descriptor.substring(i, next).replace('/', '_'));
   1.652 +                        sig.insert(firstPos, ch);
   1.653 +                        hasReturnType[0] = true;
   1.654 +                    }
   1.655 +                    i = next + 1;
   1.656 +                    continue;
   1.657 +                case '[':
   1.658 +                    //arrays++;
   1.659 +                    continue;
   1.660 +                default:
   1.661 +                    break; // invalid character
   1.662 +            }
   1.663 +        }
   1.664 +        return cnt;
   1.665 +    }
   1.666 +
   1.667 +    private void generateStaticField(Variable v) throws IOException {
   1.668 +        out.append("\nvar ")
   1.669 +           .append(jc.getName().getInternalName().replace('/', '_'))
   1.670 +           .append('_').append(v.getName()).append(" = 0;");
   1.671 +    }
   1.672 +
   1.673 +    private String findMethodName(Method m) {
   1.674 +        StringBuilder tmp = new StringBuilder();
   1.675 +        if ("<init>".equals(m.getName())) { // NOI18N
   1.676 +            tmp.append("consV"); // NOI18N
   1.677 +        } else if ("<clinit>".equals(m.getName())) { // NOI18N
   1.678 +            tmp.append("classV"); // NOI18N
   1.679 +        } else {
   1.680 +            tmp.append(m.getName());
   1.681 +            outType(m.getReturnType(), tmp);
   1.682 +        } 
   1.683 +        List<Parameter> args = m.getParameters();
   1.684 +        for (Parameter t : args) {
   1.685 +            outType(t.getDescriptor(), tmp);
   1.686 +        }
   1.687 +        return tmp.toString();
   1.688 +    }
   1.689 +
   1.690 +    private String findMethodName(CPMethodInfo mi, int[] cnt, boolean[] hasReturn) {
   1.691 +        StringBuilder name = new StringBuilder();
   1.692 +        if ("<init>".equals(mi.getName())) { // NOI18N
   1.693 +            name.append("cons"); // NOI18N
   1.694 +        } else {
   1.695 +            name.append(mi.getName());
   1.696 +        }
   1.697 +        cnt[0] = countArgs(mi.getDescriptor(), hasReturn, name);
   1.698 +        return name.toString();
   1.699 +    }
   1.700 +
   1.701 +    private int invokeStaticMethod(byte[] byteCodes, int i, boolean isStatic)
   1.702 +    throws IOException {
   1.703 +        int methodIndex = readIntArg(byteCodes, i);
   1.704 +        CPMethodInfo mi = (CPMethodInfo) jc.getConstantPool().get(methodIndex);
   1.705 +        boolean[] hasReturn = { false };
   1.706 +        int[] cnt = { 0 };
   1.707 +        String mn = findMethodName(mi, cnt, hasReturn);
   1.708 +        out.append("{ ");
   1.709 +        for (int j = cnt[0] - 1; j >= 0; j--) {
   1.710 +            out.append("var v" + j).append(" = stack.pop(); ");
   1.711 +        }
   1.712 +        
   1.713 +        if (hasReturn[0]) {
   1.714 +            out.append("stack.push(");
   1.715 +        }
   1.716 +        final String in = mi.getClassName().getInternalName();
   1.717 +        out.append(in.replace('/', '_'));
   1.718 +        out.append('_');
   1.719 +        out.append(mn);
   1.720 +        out.append('(');
   1.721 +        String sep = "";
   1.722 +        if (!isStatic) {
   1.723 +            out.append("stack.pop()");
   1.724 +            sep = ", ";
   1.725 +        }
   1.726 +        for (int j = 0; j < cnt[0]; j++) {
   1.727 +            out.append(sep);
   1.728 +            out.append("v" + j);
   1.729 +            sep = ", ";
   1.730 +        }
   1.731 +        out.append(")");
   1.732 +        if (hasReturn[0]) {
   1.733 +            out.append(")");
   1.734 +        }
   1.735 +        out.append("; }");
   1.736 +        i += 2;
   1.737 +        addReference(in);
   1.738 +        return i;
   1.739 +    }
   1.740 +    private int invokeVirtualMethod(byte[] byteCodes, int i)
   1.741 +    throws IOException {
   1.742 +        int methodIndex = readIntArg(byteCodes, i);
   1.743 +        CPMethodInfo mi = (CPMethodInfo) jc.getConstantPool().get(methodIndex);
   1.744 +        boolean[] hasReturn = { false };
   1.745 +        int[] cnt = { 0 };
   1.746 +        String mn = findMethodName(mi, cnt, hasReturn);
   1.747 +        out.append("{ ");
   1.748 +        for (int j = cnt[0] - 1; j >= 0; j--) {
   1.749 +            out.append("var v" + j).append(" = stack.pop(); ");
   1.750 +        }
   1.751 +        out.append("var self = stack.pop(); ");
   1.752 +        if (hasReturn[0]) {
   1.753 +            out.append("stack.push(");
   1.754 +        }
   1.755 +        out.append("self.");
   1.756 +        out.append(mn);
   1.757 +        out.append('(');
   1.758 +        out.append("self");
   1.759 +        for (int j = 0; j < cnt[0]; j++) {
   1.760 +            out.append(", ");
   1.761 +            out.append("v" + j);
   1.762 +        }
   1.763 +        out.append(")");
   1.764 +        if (hasReturn[0]) {
   1.765 +            out.append(")");
   1.766 +        }
   1.767 +        out.append("; }");
   1.768 +        i += 2;
   1.769 +        return i;
   1.770 +    }
   1.771 +    
   1.772 +    private void addReference(String cn) {
   1.773 +        if (references != null) {
   1.774 +            references.add(cn);
   1.775 +        }
   1.776 +    }
   1.777 +
   1.778 +    private void outType(final String d, StringBuilder out) {
   1.779 +        if (d.charAt(0) == 'L') {
   1.780 +            assert d.charAt(d.length() - 1) == ';';
   1.781 +            out.append(d.replace('/', '_').substring(0, d.length() - 1));
   1.782 +        } else {
   1.783 +            out.append(d);
   1.784 +        }
   1.785 +    }
   1.786 +
   1.787 +    private String encodeConstant(CPEntry entry) {
   1.788 +        final String v;
   1.789 +        if (entry instanceof CPStringInfo) {
   1.790 +            v = "\"" + entry.getValue().toString().replace("\"", "\\\"") + "\"";
   1.791 +        } else {
   1.792 +            v = entry.getValue().toString();
   1.793 +        }
   1.794 +        return v;
   1.795 +    }
   1.796 +}