vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 11 Nov 2012 13:23:52 +0100
branchjavap
changeset 151 40f95fe90cdc
parent 137 45184b2f9697
child 152 2cda429aeb49
permissions -rw-r--r--
Almost rewritten to javap: @JavaScriptBody and double/long may not yet be supported
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.vm4brwsr;
    19 
    20 import java.io.IOException;
    21 import java.io.InputStream;
    22 import java.util.ArrayList;
    23 import java.util.Collection;
    24 import java.util.List;
    25 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    26 import org.apidesign.bck2brwsr.core.JavaScriptBody;
    27 import sun.tools.javap.ClassData;
    28 import sun.tools.javap.FieldData;
    29 import sun.tools.javap.MethodData;
    30 import static sun.tools.javap.RuntimeConstants.*;
    31 
    32 /** Translator of the code inside class files to JavaScript.
    33  *
    34  * @author Jaroslav Tulach <jtulach@netbeans.org>
    35  */
    36 public final class ByteCodeToJavaScript {
    37     private final ClassData jc;
    38     private final Appendable out;
    39     private final Collection<? super String> references;
    40 
    41     private ByteCodeToJavaScript(
    42         ClassData jc, Appendable out, Collection<? super String> references
    43     ) {
    44         this.jc = jc;
    45         this.out = out;
    46         this.references = references;
    47     }
    48 
    49     /**
    50      * Converts a given class file to a JavaScript version.
    51      *
    52      * @param classFile input stream with code of the .class file
    53      * @param out a {@link StringBuilder} or similar to generate the output to
    54      * @param references a write only collection where the system adds list of
    55      *   other classes that were referenced and should be loaded in order the
    56      *   generated JavaScript code works properly. The names are in internal 
    57      *   JVM form so String is <code>java/lang/String</code>. Can be <code>null</code>
    58      *   if one is not interested in knowing references
    59      * @param scripts write only collection with names of resources to read
    60      * @return the initialization code for this class, if any. Otherwise <code>null</code>
    61      * 
    62      * @throws IOException if something goes wrong during read or write or translating
    63      */
    64     
    65     public static String compile(
    66         InputStream classFile, Appendable out,
    67         Collection<? super String> references,
    68         Collection<? super String> scripts
    69     ) throws IOException {
    70         ClassData jc = new ClassData(classFile);
    71         /*
    72         final ClassName extraAnn = ClassName.getClassName(ExtraJavaScript.class.getName().replace('.', '/'));
    73         Annotation a = jc.getAnnotation(extraAnn);
    74         if (a != null) {
    75             final ElementValue annVal = a.getComponent("resource").getValue();
    76             String res = ((PrimitiveElementValue)annVal).getValue().getValue().toString();
    77             scripts.add(res);
    78             final AnnotationComponent process = a.getComponent("processByteCode");
    79             if (process != null && "const=0".equals(process.getValue().toString())) {
    80                 return null;
    81             }
    82         }
    83         */
    84         ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(
    85             jc, out, references
    86         );
    87         List<String> toInitilize = new ArrayList<String>();
    88         for (MethodData m : jc.getMethods()) {
    89             if (m.isStatic()) {
    90                 compiler.generateStaticMethod(m, toInitilize);
    91             } else {
    92                 compiler.generateInstanceMethod(m);
    93             }
    94         }
    95         for (FieldData v : jc.getFields()) {
    96             if (v.isStatic()) {
    97                 compiler.generateStaticField(v);
    98             }
    99         }
   100         
   101         final String className = className(jc);
   102         out.append("\nfunction ").append(className);
   103         out.append("() {");
   104         for (FieldData v : jc.getFields()) {
   105             if (!v.isStatic()) {
   106                 out.append("\n  this.fld_").
   107                     append(v.getName()).append(" = 0;");
   108             }
   109         }
   110         out.append("\n}\n\nfunction ").append(className).append("_proto() {");
   111         out.append("\n  if (").append(className).
   112             append(".prototype.$instOf_").append(className).append(") {");
   113         out.append("\n    return new ").append(className).append(";");
   114         out.append("\n  }");
   115         // ClassName sc = jc.getSuperClass();
   116         String sc = jc.getSuperClassName(); // with _
   117         if (sc != null) {
   118             out.append("\n  var p = ").append(className)
   119                .append(".prototype = ").
   120                 append(sc.replace('/', '_')).append("_proto();");
   121         } else {
   122             out.append("\n  var p = ").append(className).append(".prototype;");
   123         }
   124         for (MethodData m : jc.getMethods()) {
   125             if (!m.getName().contains("<init>") && !m.getName().contains("<cinit>")) {
   126                 compiler.generateMethodReference("\n  p.", m);
   127             }
   128         }
   129         out.append("\n  p.$instOf_").append(className).append(" = true;");
   130         for (String superInterface : jc.getSuperInterfaces()) {
   131             out.append("\n  p.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
   132         }
   133         out.append("\n  return new ").append(className).append(";");
   134         out.append("\n}");
   135         out.append("\n").append(className).append("_proto();");
   136         StringBuilder sb = new StringBuilder();
   137         for (String init : toInitilize) {
   138             sb.append("\n").append(init).append("();");
   139         }
   140         return sb.toString();
   141     }
   142     private void generateStaticMethod(MethodData m, List<String> toInitilize) throws IOException {
   143         if (javaScriptBody(m, true)) {
   144             return;
   145         }
   146         int[] argsCnt = { -1 };
   147         final String mn = findMethodName(m, argsCnt);
   148         out.append("\nfunction ").append(
   149             className(jc)
   150         ).append('_').append(mn);
   151         if (mn.equals("classV")) {
   152             toInitilize.add(className(jc) + '_' + mn);
   153         }
   154         out.append('(');
   155         String space = "";
   156         for (int index = 0, i = 0; i < argsCnt[0]; i++) {
   157             out.append(space);
   158             out.append("arg").append(String.valueOf(index));
   159             space = ",";
   160             final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor());
   161             if ("D".equals(desc) || "J".equals(desc)) {
   162                 index += 2;
   163             } else {
   164                 index++;
   165             }
   166         }
   167         out.append(") {").append("\n");
   168         final byte[] code = m.getCode();
   169         if (code != null) {
   170             int len = m.getMaxLocals();
   171             for (int index = argsCnt[0], i = argsCnt[0]; i < len; i++) {
   172                 out.append("  var ");
   173                 out.append("arg").append(String.valueOf(i)).append(";\n");
   174             }
   175             out.append("  var stack = new Array();\n");
   176             produceCode(code);
   177         } else {
   178             out.append("  /* no code found for ").append(m.getInternalSig()).append(" */\n");
   179         }
   180         out.append("}");
   181     }
   182     
   183     private void generateMethodReference(String prefix, MethodData m) throws IOException {
   184         final String name = findMethodName(m, null);
   185         out.append(prefix).append(name).append(" = ")
   186            .append(className(jc))
   187            .append('_').append(name).append(";");
   188     }
   189     
   190     private void generateInstanceMethod(MethodData m) throws IOException {
   191         if (javaScriptBody(m, false)) {
   192             return;
   193         }
   194         int[] argsCnt = { -1 };
   195         out.append("\nfunction ").append(
   196             className(jc)
   197         ).append('_').append(findMethodName(m, argsCnt));
   198         out.append("(arg0");
   199         String space = ",";
   200         for (int index = 1, i = 0; i < argsCnt[0]; i++) {
   201             out.append(space);
   202             out.append("arg").append(String.valueOf(index));
   203             final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor());
   204             if ("D".equals(desc) || "J".equals(desc)) {
   205                 index += 2;
   206             } else {
   207                 index++;
   208             }
   209         }
   210         out.append(") {").append("\n");
   211         final byte[] code = m.getCode();
   212         if (code != null) {
   213             int len = m.getMaxLocals();
   214             for (int index = argsCnt[0], i = argsCnt[0]; i < len; i++) {
   215                 out.append("  var ");
   216                 out.append("arg").append(String.valueOf(i + 1)).append(";\n");
   217             }
   218             out.append(";\n  var stack = new Array();\n");
   219             produceCode(code);
   220         } else {
   221             out.append("  /* no code found for ").append(m.getInternalSig()).append(" */\n");
   222         }
   223         out.append("}");
   224     }
   225 
   226     private void produceCode(byte[] byteCodes) throws IOException {
   227         out.append("\n  var gt = 0;\n  for(;;) switch(gt) {\n");
   228         for (int i = 0; i < byteCodes.length; i++) {
   229             int prev = i;
   230             out.append("    case " + i).append(": ");
   231             final int c = readByte(byteCodes, i);
   232             switch (c) {
   233                 case opc_aload_0:
   234                 case opc_iload_0:
   235                 case opc_lload_0:
   236                 case opc_fload_0:
   237                 case opc_dload_0:
   238                     out.append("stack.push(arg0);");
   239                     break;
   240                 case opc_aload_1:
   241                 case opc_iload_1:
   242                 case opc_lload_1:
   243                 case opc_fload_1:
   244                 case opc_dload_1:
   245                     out.append("stack.push(arg1);");
   246                     break;
   247                 case opc_aload_2:
   248                 case opc_iload_2:
   249                 case opc_lload_2:
   250                 case opc_fload_2:
   251                 case opc_dload_2:
   252                     out.append("stack.push(arg2);");
   253                     break;
   254                 case opc_aload_3:
   255                 case opc_iload_3:
   256                 case opc_lload_3:
   257                 case opc_fload_3:
   258                 case opc_dload_3:
   259                     out.append("stack.push(arg3);");
   260                     break;
   261                 case opc_iload:
   262                 case opc_lload:
   263                 case opc_fload:
   264                 case opc_dload:
   265                 case opc_aload: {
   266                     final int indx = readByte(byteCodes, ++i);
   267                     out.append("stack.push(arg").append(indx + ");");
   268                     break;
   269                 }
   270                 case opc_istore:
   271                 case opc_lstore:
   272                 case opc_fstore:
   273                 case opc_dstore:
   274                 case opc_astore: {
   275                     final int indx = readByte(byteCodes, ++i);
   276                     out.append("arg" + indx).append(" = stack.pop();");
   277                     break;
   278                 }
   279                 case opc_astore_0:
   280                 case opc_istore_0:
   281                 case opc_lstore_0:
   282                 case opc_fstore_0:
   283                 case opc_dstore_0:
   284                     out.append("arg0 = stack.pop();");
   285                     break;
   286                 case opc_astore_1:
   287                 case opc_istore_1:
   288                 case opc_lstore_1:
   289                 case opc_fstore_1:
   290                 case opc_dstore_1:
   291                     out.append("arg1 = stack.pop();");
   292                     break;
   293                 case opc_astore_2:
   294                 case opc_istore_2:
   295                 case opc_lstore_2:
   296                 case opc_fstore_2:
   297                 case opc_dstore_2:
   298                     out.append("arg2 = stack.pop();");
   299                     break;
   300                 case opc_astore_3:
   301                 case opc_istore_3:
   302                 case opc_lstore_3:
   303                 case opc_fstore_3:
   304                 case opc_dstore_3:
   305                     out.append("arg3 = stack.pop();");
   306                     break;
   307                 case opc_iadd:
   308                 case opc_ladd:
   309                 case opc_fadd:
   310                 case opc_dadd:
   311                     out.append("stack.push(stack.pop() + stack.pop());");
   312                     break;
   313                 case opc_isub:
   314                 case opc_lsub:
   315                 case opc_fsub:
   316                 case opc_dsub:
   317                     out.append("{ var tmp = stack.pop(); stack.push(stack.pop() - tmp); }");
   318                     break;
   319                 case opc_imul:
   320                 case opc_lmul:
   321                 case opc_fmul:
   322                 case opc_dmul:
   323                     out.append("stack.push(stack.pop() * stack.pop());");
   324                     break;
   325                 case opc_idiv:
   326                 case opc_ldiv:
   327                     out.append("{ var tmp = stack.pop(); stack.push(Math.floor(stack.pop() / tmp)); }");
   328                     break;
   329                 case opc_fdiv:
   330                 case opc_ddiv:
   331                     out.append("{ var tmp = stack.pop(); stack.push(stack.pop() / tmp); }");
   332                     break;
   333                 case opc_iand:
   334                 case opc_land:
   335                     out.append("stack.push(stack.pop() & stack.pop());");
   336                     break;
   337                 case opc_ior:
   338                 case opc_lor:
   339                     out.append("stack.push(stack.pop() | stack.pop());");
   340                     break;
   341                 case opc_ixor:
   342                 case opc_lxor:
   343                     out.append("stack.push(stack.pop() ^ stack.pop());");
   344                     break;
   345                 case opc_ineg:
   346                 case opc_lneg:
   347                 case opc_fneg:
   348                 case opc_dneg:
   349                     out.append("stack.push(- stack.pop());");
   350                     break;
   351                 case opc_ishl:
   352                 case opc_lshl:
   353                     out.append("{ var v = stack.pop(); stack.push(stack.pop() << v); }");
   354                     break;
   355                 case opc_ishr:
   356                 case opc_lshr:
   357                     out.append("{ var v = stack.pop(); stack.push(stack.pop() >> v); }");
   358                     break;
   359                 case opc_iushr:
   360                 case opc_lushr:
   361                     out.append("{ var v = stack.pop(); stack.push(stack.pop() >>> v); }");
   362                     break;
   363                 case opc_iinc: {
   364                     final int varIndx = readByte(byteCodes, ++i);
   365                     final int incrBy = byteCodes[++i];
   366                     if (incrBy == 1) {
   367                         out.append("arg" + varIndx).append("++;");
   368                     } else {
   369                         out.append("arg" + varIndx).append(" += " + incrBy).append(";");
   370                     }
   371                     break;
   372                 }
   373                 case opc_return:
   374                     out.append("return;");
   375                     break;
   376                 case opc_ireturn:
   377                 case opc_lreturn:
   378                 case opc_freturn:
   379                 case opc_dreturn:
   380                 case opc_areturn:
   381                     out.append("return stack.pop();");
   382                     break;
   383                 case opc_i2l:
   384                 case opc_i2f:
   385                 case opc_i2d:
   386                 case opc_l2i:
   387                     // max int check?
   388                 case opc_l2f:
   389                 case opc_l2d:
   390                 case opc_f2d:
   391                 case opc_d2f:
   392                     out.append("/* number conversion */");
   393                     break;
   394                 case opc_f2i:
   395                 case opc_f2l:
   396                 case opc_d2i:
   397                 case opc_d2l:
   398                     out.append("stack.push(Math.floor(stack.pop()));");
   399                     break;
   400                 case opc_i2b:
   401                 case opc_i2c:
   402                 case opc_i2s:
   403                     out.append("/* number conversion */");
   404                     break;
   405                 case opc_aconst_null:
   406                     out.append("stack.push(null);");
   407                     break;
   408                 case opc_iconst_m1:
   409                     out.append("stack.push(-1);");
   410                     break;
   411                 case opc_iconst_0:
   412                 case opc_dconst_0:
   413                 case opc_lconst_0:
   414                 case opc_fconst_0:
   415                     out.append("stack.push(0);");
   416                     break;
   417                 case opc_iconst_1:
   418                 case opc_lconst_1:
   419                 case opc_fconst_1:
   420                 case opc_dconst_1:
   421                     out.append("stack.push(1);");
   422                     break;
   423                 case opc_iconst_2:
   424                 case opc_fconst_2:
   425                     out.append("stack.push(2);");
   426                     break;
   427                 case opc_iconst_3:
   428                     out.append("stack.push(3);");
   429                     break;
   430                 case opc_iconst_4:
   431                     out.append("stack.push(4);");
   432                     break;
   433                 case opc_iconst_5:
   434                     out.append("stack.push(5);");
   435                     break;
   436                 case opc_ldc: {
   437                     int indx = readByte(byteCodes, ++i);
   438                     String v = encodeConstant(indx);
   439                     out.append("stack.push(").append(v).append(");");
   440                     break;
   441                 }
   442                 case opc_ldc_w:
   443                 case opc_ldc2_w: {
   444                     int indx = readIntArg(byteCodes, i);
   445                     i += 2;
   446                     String v = encodeConstant(indx);
   447                     out.append("stack.push(").append(v).append(");");
   448                     break;
   449                 }
   450                 case opc_lcmp:
   451                 case opc_fcmpl:
   452                 case opc_fcmpg:
   453                 case opc_dcmpl:
   454                 case opc_dcmpg: {
   455                     out.append("{ var delta = stack.pop() - stack.pop(); stack.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }");
   456                     break;
   457                 }
   458                 case opc_if_acmpeq:
   459                     i = generateIf(byteCodes, i, "===");
   460                     break;
   461                 case opc_if_acmpne:
   462                     i = generateIf(byteCodes, i, "!=");
   463                     break;
   464                 case opc_if_icmpeq: {
   465                     i = generateIf(byteCodes, i, "==");
   466                     break;
   467                 }
   468                 case opc_ifeq: {
   469                     int indx = i + readIntArg(byteCodes, i);
   470                     out.append("if (stack.pop() == 0) { gt = " + indx);
   471                     out.append("; continue; }");
   472                     i += 2;
   473                     break;
   474                 }
   475                 case opc_ifne: {
   476                     int indx = i + readIntArg(byteCodes, i);
   477                     out.append("if (stack.pop() != 0) { gt = " + indx);
   478                     out.append("; continue; }");
   479                     i += 2;
   480                     break;
   481                 }
   482                 case opc_iflt: {
   483                     int indx = i + readIntArg(byteCodes, i);
   484                     out.append("if (stack.pop() < 0) { gt = " + indx);
   485                     out.append("; continue; }");
   486                     i += 2;
   487                     break;
   488                 }
   489                 case opc_ifle: {
   490                     int indx = i + readIntArg(byteCodes, i);
   491                     out.append("if (stack.pop() <= 0) { gt = " + indx);
   492                     out.append("; continue; }");
   493                     i += 2;
   494                     break;
   495                 }
   496                 case opc_ifgt: {
   497                     int indx = i + readIntArg(byteCodes, i);
   498                     out.append("if (stack.pop() > 0) { gt = " + indx);
   499                     out.append("; continue; }");
   500                     i += 2;
   501                     break;
   502                 }
   503                 case opc_ifge: {
   504                     int indx = i + readIntArg(byteCodes, i);
   505                     out.append("if (stack.pop() >= 0) { gt = " + indx);
   506                     out.append("; continue; }");
   507                     i += 2;
   508                     break;
   509                 }
   510                 case opc_ifnonnull: {
   511                     int indx = i + readIntArg(byteCodes, i);
   512                     out.append("if (stack.pop()) { gt = " + indx);
   513                     out.append("; continue; }");
   514                     i += 2;
   515                     break;
   516                 }
   517                 case opc_ifnull: {
   518                     int indx = i + readIntArg(byteCodes, i);
   519                     out.append("if (!stack.pop()) { gt = " + indx);
   520                     out.append("; continue; }");
   521                     i += 2;
   522                     break;
   523                 }
   524                 case opc_if_icmpne:
   525                     i = generateIf(byteCodes, i, "!=");
   526                     break;
   527                 case opc_if_icmplt:
   528                     i = generateIf(byteCodes, i, ">");
   529                     break;
   530                 case opc_if_icmple:
   531                     i = generateIf(byteCodes, i, ">=");
   532                     break;
   533                 case opc_if_icmpgt:
   534                     i = generateIf(byteCodes, i, "<");
   535                     break;
   536                 case opc_if_icmpge:
   537                     i = generateIf(byteCodes, i, "<=");
   538                     break;
   539                 case opc_goto: {
   540                     int indx = i + readIntArg(byteCodes, i);
   541                     out.append("gt = " + indx).append("; continue;");
   542                     i += 2;
   543                     break;
   544                 }
   545                 case opc_lookupswitch: {
   546                     int table = i / 4 * 4 + 4;
   547                     int dflt = i + readInt4(byteCodes, table);
   548                     table += 4;
   549                     int n = readInt4(byteCodes, table);
   550                     table += 4;
   551                     out.append("switch (stack.pop()) {\n");
   552                     while (n-- > 0) {
   553                         int cnstnt = readInt4(byteCodes, table);
   554                         table += 4;
   555                         int offset = i + readInt4(byteCodes, table);
   556                         table += 4;
   557                         out.append("  case " + cnstnt).append(": gt = " + offset).append("; continue;\n");
   558                     }
   559                     out.append("  default: gt = " + dflt).append("; continue;\n}");
   560                     i = table - 1;
   561                     break;
   562                 }
   563                 case opc_tableswitch: {
   564                     int table = i / 4 * 4 + 4;
   565                     int dflt = i + readInt4(byteCodes, table);
   566                     table += 4;
   567                     int low = readInt4(byteCodes, table);
   568                     table += 4;
   569                     int high = readInt4(byteCodes, table);
   570                     table += 4;
   571                     out.append("switch (stack.pop()) {\n");
   572                     while (low <= high) {
   573                         int offset = i + readInt4(byteCodes, table);
   574                         table += 4;
   575                         out.append("  case " + low).append(": gt = " + offset).append("; continue;\n");
   576                         low++;
   577                     }
   578                     out.append("  default: gt = " + dflt).append("; continue;\n}");
   579                     i = table - 1;
   580                     break;
   581                 }
   582                 case opc_invokeinterface: {
   583                     i = invokeVirtualMethod(byteCodes, i) + 2;
   584                     break;
   585                 }
   586                 case opc_invokevirtual:
   587                     i = invokeVirtualMethod(byteCodes, i);
   588                     break;
   589                 case opc_invokespecial:
   590                     i = invokeStaticMethod(byteCodes, i, false);
   591                     break;
   592                 case opc_invokestatic:
   593                     i = invokeStaticMethod(byteCodes, i, true);
   594                     break;
   595                 case opc_new: {
   596                     int indx = readIntArg(byteCodes, i);
   597                     String ci = jc.getClassName(indx);
   598                     out.append("stack.push(");
   599                     out.append("new ").append(ci.replace('/','_'));
   600                     out.append(");");
   601                     addReference(ci);
   602                     i += 2;
   603                     break;
   604                 }
   605                 case opc_newarray: {
   606                     int type = byteCodes[i++];
   607                     out.append("stack.push(new Array(stack.pop()));");
   608                     break;
   609                 }
   610                 case opc_anewarray: {
   611                     i += 2; // skip type of array
   612                     out.append("stack.push(new Array(stack.pop()));");
   613                     break;
   614                 }
   615                 case opc_multianewarray: {
   616                     i += 2;
   617                     int dim = readByte(byteCodes, ++i);
   618                     out.append("{ var a0 = new Array(stack.pop());");
   619                     for (int d = 1; d < dim; d++) {
   620                         out.append("\n  var l" + d).append(" = stack.pop();");
   621                         out.append("\n  for (var i" + d).append (" = 0; i" + d).
   622                             append(" < a" + (d - 1)).
   623                             append(".length; i" + d).append("++) {");
   624                         out.append("\n    var a" + d).
   625                             append (" = new Array(l" + d).append(");");
   626                         out.append("\n    a" + (d - 1)).append("[i" + d).append("] = a" + d).
   627                             append(";");
   628                     }
   629                     for (int d = 1; d < dim; d++) {
   630                         out.append("\n  }");
   631                     }
   632                     out.append("\nstack.push(a0); }");
   633                     break;
   634                 }
   635                 case opc_arraylength:
   636                     out.append("stack.push(stack.pop().length);");
   637                     break;
   638                 case opc_iastore:
   639                 case opc_lastore:
   640                 case opc_fastore:
   641                 case opc_dastore:
   642                 case opc_aastore:
   643                 case opc_bastore:
   644                 case opc_castore:
   645                 case opc_sastore: {
   646                     out.append("{ var value = stack.pop(); var indx = stack.pop(); stack.pop()[indx] = value; }");
   647                     break;
   648                 }
   649                 case opc_iaload:
   650                 case opc_laload:
   651                 case opc_faload:
   652                 case opc_daload:
   653                 case opc_aaload:
   654                 case opc_baload:
   655                 case opc_caload:
   656                 case opc_saload: {
   657                     out.append("{ var indx = stack.pop(); stack.push(stack.pop()[indx]); }");
   658                     break;
   659                 }
   660                 case opc_pop2:
   661                     out.append("stack.pop();");
   662                 case opc_pop:
   663                     out.append("stack.pop();");
   664                     break;
   665                 case opc_dup:
   666                     out.append("stack.push(stack[stack.length - 1]);");
   667                     break;
   668                 case opc_bipush:
   669                     out.append("stack.push(" + byteCodes[++i] + ");");
   670                     break;
   671                 case opc_sipush:
   672                     out.append("stack.push(" + readIntArg(byteCodes, i) + ");");
   673                     i += 2;
   674                     break;
   675                 case opc_getfield: {
   676                     int indx = readIntArg(byteCodes, i);
   677                     String[] fi = jc.getFieldInfoName(indx);
   678                     out.append("stack.push(stack.pop().fld_").
   679                         append(fi[1]).append(");");
   680                     i += 2;
   681                     break;
   682                 }
   683                 case opc_getstatic: {
   684                     int indx = readIntArg(byteCodes, i);
   685                     String[] fi = jc.getFieldInfoName(indx);
   686                     out.append("stack.push(").append(fi[0].replace('/', '_'));
   687                     out.append('_').append(fi[1]).append(");");
   688                     i += 2;
   689                     addReference(fi[0]);
   690                     break;
   691                 }
   692                 case opc_putstatic: {
   693                     int indx = readIntArg(byteCodes, i);
   694                     String[] fi = jc.getFieldInfoName(indx);
   695                     out.append(fi[0].replace('/', '_'));
   696                     out.append('_').append(fi[1]).append(" = stack.pop();");
   697                     i += 2;
   698                     addReference(fi[0]);
   699                     break;
   700                 }
   701                 case opc_putfield: {
   702                     int indx = readIntArg(byteCodes, i);
   703                     String[] fi = jc.getFieldInfoName(indx);
   704                     out.append("{ var v = stack.pop(); stack.pop().fld_")
   705                        .append(fi[1]).append(" = v; }");
   706                     i += 2;
   707                     break;
   708                 }
   709                 case opc_checkcast: {
   710                     int indx = readIntArg(byteCodes, i);
   711                     final String type = jc.getClassName(indx);
   712                     if (!type.startsWith("[")) {
   713                         // no way to check arrays right now
   714                         out.append("if(stack[stack.length - 1].$instOf_")
   715                            .append(type.replace('/', '_'))
   716                            .append(" != 1) throw {};"); // XXX proper exception
   717                     }
   718                     i += 2;
   719                     break;
   720                 }
   721                 case opc_instanceof: {
   722                     int indx = readIntArg(byteCodes, i);
   723                     final String type = jc.getClassName(indx);
   724                     out.append("stack.push(stack.pop().$instOf_")
   725                        .append(type.replace('/', '_'))
   726                        .append(" ? 1 : 0);");
   727                     i += 2;
   728                     break;
   729                 }
   730                 case opc_athrow: {
   731                     out.append("{ var t = stack.pop(); stack = new Array(1); stack[0] = t; throw t; }");
   732                     break;
   733                 }
   734                 default: {
   735                     out.append("throw 'unknown bytecode " + c + "';");
   736                 }
   737                     
   738             }
   739             out.append(" //");
   740             for (int j = prev; j <= i; j++) {
   741                 out.append(" ");
   742                 final int cc = readByte(byteCodes, j);
   743                 out.append(Integer.toString(cc));
   744             }
   745             out.append("\n");
   746         }
   747         out.append("  }\n");
   748     }
   749 
   750     private int generateIf(byte[] byteCodes, int i, final String test) throws IOException {
   751         int indx = i + readIntArg(byteCodes, i);
   752         out.append("if (stack.pop() ").append(test).append(" stack.pop()) { gt = " + indx);
   753         out.append("; continue; }");
   754         return i + 2;
   755     }
   756 
   757     private int readIntArg(byte[] byteCodes, int offsetInstruction) {
   758         final int indxHi = byteCodes[offsetInstruction + 1] << 8;
   759         final int indxLo = byteCodes[offsetInstruction + 2];
   760         return (indxHi & 0xffffff00) | (indxLo & 0xff);
   761     }
   762     private int readInt4(byte[] byteCodes, int offsetInstruction) {
   763         final int d = byteCodes[offsetInstruction + 0] << 24;
   764         final int c = byteCodes[offsetInstruction + 1] << 16;
   765         final int b = byteCodes[offsetInstruction + 2] << 8;
   766         final int a = byteCodes[offsetInstruction + 3];
   767         return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
   768     }
   769     private int readByte(byte[] byteCodes, int offsetInstruction) {
   770         return (byteCodes[offsetInstruction] + 256) % 256;
   771     }
   772     
   773     private static int countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig) {
   774         int cnt = 0;
   775         int i = 0;
   776         Boolean count = null;
   777         boolean array = false;
   778         int firstPos = sig.length();
   779         while (i < descriptor.length()) {
   780             char ch = descriptor.charAt(i++);
   781             switch (ch) {
   782                 case '(':
   783                     count = true;
   784                     continue;
   785                 case ')':
   786                     count = false;
   787                     continue;
   788                 case 'A':
   789                     array = true;
   790                     break;
   791                 case 'B': 
   792                 case 'C': 
   793                 case 'D': 
   794                 case 'F': 
   795                 case 'I': 
   796                 case 'J': 
   797                 case 'S': 
   798                 case 'Z': 
   799                     if (count) {
   800                         cnt++;
   801                         if (array) {
   802                             sig.append('A');
   803                         }
   804                         sig.append(ch);
   805                     } else {
   806                         hasReturnType[0] = true;
   807                         sig.insert(firstPos, ch);
   808                         if (array) {
   809                             sig.insert(firstPos, 'A');
   810                         }
   811                     }
   812                     array = false;
   813                     continue;
   814                 case 'V': 
   815                     assert !count;
   816                     hasReturnType[0] = false;
   817                     sig.insert(firstPos, 'V');
   818                     continue;
   819                 case 'L':
   820                     int next = descriptor.indexOf(';', i);
   821                     if (count) {
   822                         cnt++;
   823                         if (array) {
   824                             sig.append('A');
   825                         }
   826                         sig.append(ch);
   827                         sig.append(descriptor.substring(i, next).replace('/', '_'));
   828                     } else {
   829                         sig.insert(firstPos, descriptor.substring(i, next).replace('/', '_'));
   830                         sig.insert(firstPos, ch);
   831                         if (array) {
   832                             sig.insert(firstPos, 'A');
   833                         }
   834                         hasReturnType[0] = true;
   835                     }
   836                     i = next + 1;
   837                     continue;
   838                 case '[':
   839                     //arrays++;
   840                     continue;
   841                 default:
   842                     break; // invalid character
   843             }
   844         }
   845         return cnt;
   846     }
   847 
   848     private void generateStaticField(FieldData v) throws IOException {
   849         out.append("\nvar ")
   850            .append(className(jc))
   851            .append('_').append(v.getName()).append(" = 0;");
   852     }
   853 
   854     private String findMethodName(MethodData m, int[] cnt) {
   855         StringBuilder name = new StringBuilder();
   856         if ("<init>".equals(m.getName())) { // NOI18N
   857             name.append("cons"); // NOI18N
   858         } else if ("<clinit>".equals(m.getName())) { // NOI18N
   859             name.append("class"); // NOI18N
   860         } else {
   861             name.append(m.getName());
   862         } 
   863         
   864         boolean hasReturn[] = { false };
   865         int argsCnt = countArgs(findDescriptor(m.getInternalSig()), hasReturn, name);
   866         if (cnt != null) {
   867             cnt[0] = argsCnt;
   868         }
   869         return name.toString();
   870     }
   871 
   872     private String findMethodName(String[] mi, int[] cnt, boolean[] hasReturn) {
   873         StringBuilder name = new StringBuilder();
   874         String descr = mi[2];//mi.getDescriptor();
   875         String nm= mi[1];
   876         if ("<init>".equals(nm)) { // NOI18N
   877             name.append("cons"); // NOI18N
   878         } else {
   879             name.append(nm);
   880         }
   881         cnt[0] = countArgs(findDescriptor(descr), hasReturn, name);
   882         return name.toString();
   883     }
   884 
   885     private int invokeStaticMethod(byte[] byteCodes, int i, boolean isStatic)
   886     throws IOException {
   887         int methodIndex = readIntArg(byteCodes, i);
   888         String[] mi = jc.getFieldInfoName(methodIndex);
   889         boolean[] hasReturn = { false };
   890         int[] cnt = { 0 };
   891         String mn = findMethodName(mi, cnt, hasReturn);
   892         out.append("{ ");
   893         for (int j = cnt[0] - 1; j >= 0; j--) {
   894             out.append("var v" + j).append(" = stack.pop(); ");
   895         }
   896         
   897         if (hasReturn[0]) {
   898             out.append("stack.push(");
   899         }
   900         final String in = mi[0];
   901         out.append(in.replace('/', '_'));
   902         if (isStatic) {
   903             out.append(".prototype.");
   904         } else {
   905             out.append('_');
   906         }
   907         out.append(mn);
   908         out.append('(');
   909         String sep = "";
   910         if (!isStatic) {
   911             out.append("stack.pop()");
   912             sep = ", ";
   913         }
   914         for (int j = 0; j < cnt[0]; j++) {
   915             out.append(sep);
   916             out.append("v" + j);
   917             sep = ", ";
   918         }
   919         out.append(")");
   920         if (hasReturn[0]) {
   921             out.append(")");
   922         }
   923         out.append("; }");
   924         i += 2;
   925         addReference(in);
   926         return i;
   927     }
   928     private int invokeVirtualMethod(byte[] byteCodes, int i)
   929     throws IOException {
   930         int methodIndex = readIntArg(byteCodes, i);
   931         String[] mi = jc.getFieldInfoName(methodIndex);
   932         boolean[] hasReturn = { false };
   933         int[] cnt = { 0 };
   934         String mn = findMethodName(mi, cnt, hasReturn);
   935         out.append("{ ");
   936         for (int j = cnt[0] - 1; j >= 0; j--) {
   937             out.append("var v" + j).append(" = stack.pop(); ");
   938         }
   939         out.append("var self = stack.pop(); ");
   940         if (hasReturn[0]) {
   941             out.append("stack.push(");
   942         }
   943         out.append("self.");
   944         out.append(mn);
   945         out.append('(');
   946         out.append("self");
   947         for (int j = 0; j < cnt[0]; j++) {
   948             out.append(", ");
   949             out.append("v" + j);
   950         }
   951         out.append(")");
   952         if (hasReturn[0]) {
   953             out.append(")");
   954         }
   955         out.append("; }");
   956         i += 2;
   957         return i;
   958     }
   959     
   960     private void addReference(String cn) throws IOException {
   961         if (references != null) {
   962             if (references.add(cn)) {
   963                 out.append(" /* needs ").append(cn).append(" */");
   964             }
   965         }
   966     }
   967 
   968     private void outType(String d, StringBuilder out) {
   969         int arr = 0;
   970         while (d.charAt(0) == '[') {
   971             out.append('A');
   972             d = d.substring(1);
   973         }
   974         if (d.charAt(0) == 'L') {
   975             assert d.charAt(d.length() - 1) == ';';
   976             out.append(d.replace('/', '_').substring(0, d.length() - 1));
   977         } else {
   978             out.append(d);
   979         }
   980     }
   981 
   982     private String encodeConstant(int entryIndex) {
   983         String s = jc.stringValue(entryIndex, true);
   984         return s;
   985     }
   986 
   987     private String findDescriptor(String d) {
   988         return d.replace('[', 'A');
   989     }
   990 
   991     private boolean javaScriptBody(MethodData m, boolean isStatic) throws IOException {
   992         return false;
   993         /*
   994         final ClassName extraAnn = ClassName.getClassName(JavaScriptBody.class.getName().replace('.', '/'));
   995         Annotation a = m.getAnnotation(extraAnn);
   996         if (a != null) {
   997             final ElementValue annVal = a.getComponent("body").getValue();
   998             String body = ((PrimitiveElementValue) annVal).getValue().getValue().toString();
   999             
  1000             final ArrayElementValue arrVal = (ArrayElementValue) a.getComponent("args").getValue();
  1001             final int len = arrVal.getValues().length;
  1002             String[] names = new String[len];
  1003             for (int i = 0; i < len; i++) {
  1004                 names[i] = ((PrimitiveElementValue) arrVal.getValues()[i]).getValue().getValue().toString();
  1005             }
  1006             out.append("\nfunction ").append(
  1007                 jc.getName().getInternalName().replace('/', '_')).append('_').append(findMethodName(m));
  1008             out.append("(");
  1009             String space;
  1010             int index;
  1011             if (!isStatic) {                
  1012                 out.append(names[0]);
  1013                 space = ",";
  1014                 index = 1;
  1015             } else {
  1016                 space = "";
  1017                 index = 0;
  1018             }
  1019             List<Parameter> args = m.getParameters();
  1020             for (int i = 0; i < args.size(); i++) {
  1021                 out.append(space);
  1022                 out.append(names[index]);
  1023                 final String desc = findDescriptor(args.get(i).getDescriptor());
  1024                 index++;
  1025                 space = ",";
  1026             }
  1027             out.append(") {").append("\n");
  1028             out.append(body);
  1029             out.append("\n}\n");
  1030             return true;
  1031         }
  1032         return false;
  1033         */
  1034     }
  1035     private static String className(ClassData jc) {
  1036         //return jc.getName().getInternalName().replace('/', '_');
  1037         return jc.getClassName().replace('/', '_');
  1038     }
  1039 }