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