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