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