vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 26 Jan 2013 08:47:05 +0100
changeset 592 5e13b1ac2886
parent 587 a7a45e5e5e77
child 593 b42911b78a16
child 639 960ecf7cea5d
child 652 f095ea52f417
permissions -rw-r--r--
In order to support fields of the same name in subclasses we are now prefixing them with name of the class that defines them. To provide convenient way to access them from generated bytecode and also directly from JavaScript, there is a getter/setter function for each field. It starts with _ followed by the field name. If called with a parameter, it sets the field, with a parameter it just returns it.
     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 import org.apidesign.javap.TrapData;
    30 import org.apidesign.javap.TrapDataIterator;
    31 
    32 /** Translator of the code inside class files to JavaScript.
    33  *
    34  * @author Jaroslav Tulach <jtulach@netbeans.org>
    35  */
    36 abstract class ByteCodeToJavaScript {
    37     private ClassData jc;
    38     final Appendable out;
    39 
    40     protected ByteCodeToJavaScript(Appendable out) {
    41         this.out = out;
    42     }
    43     
    44     /* Collects additional required resources.
    45      * 
    46      * @param internalClassName classes that were referenced and should be loaded in order the
    47      *   generated JavaScript code works properly. The names are in internal 
    48      *   JVM form so String is <code>java/lang/String</code>. 
    49      */
    50     protected abstract boolean requireReference(String internalClassName);
    51     
    52     /*
    53      * @param resourcePath name of resources to read
    54      */
    55     protected abstract void requireScript(String resourcePath) throws IOException;
    56     
    57     /** Allows subclasses to redefine what field a function representing a
    58      * class gets assigned. By default it returns the suggested name followed
    59      * by <code>" = "</code>;
    60      * 
    61      * @param className suggested name of the class
    62      */
    63     /* protected */ String assignClass(String className) {
    64         return className + " = ";
    65     }
    66     /* protected */ String accessClass(String classOperation) {
    67         return classOperation;
    68     }
    69     
    70     /** Prints out a debug message. 
    71      * 
    72      * @param msg the message
    73      * @return true if the message has been printed
    74      * @throws IOException 
    75      */
    76     boolean debug(String msg) throws IOException {
    77         out.append(msg);
    78         return true;
    79     }
    80 
    81     /**
    82      * Converts a given class file to a JavaScript version.
    83      *
    84      * @param classFile input stream with code of the .class file
    85      * @return the initialization code for this class, if any. Otherwise <code>null</code>
    86      * 
    87      * @throws IOException if something goes wrong during read or write or translating
    88      */
    89     
    90     public String compile(InputStream classFile) throws IOException {
    91         this.jc = new ClassData(classFile);
    92         if (jc.getMajor_version() < 50) {
    93             throw new IOException("Can't compile " + jc.getClassName() + ". Class file version " + jc.getMajor_version() + "."
    94                 + jc.getMinor_version() + " - recompile with -target 1.6 (at least)."
    95             );
    96         }
    97         byte[] arrData = jc.findAnnotationData(true);
    98         String[] arr = findAnnotation(arrData, jc, 
    99             "org.apidesign.bck2brwsr.core.ExtraJavaScript", 
   100             "resource", "processByteCode"
   101         );
   102         if (arr != null) {
   103             requireScript(arr[0]);
   104             if ("0".equals(arr[1])) {
   105                 return null;
   106             }
   107         }
   108         String[] proto = findAnnotation(arrData, jc, 
   109             "org.apidesign.bck2brwsr.core.JavaScriptPrototype", 
   110             "container", "prototype"
   111         );
   112         StringArray toInitilize = new StringArray();
   113         final String className = className(jc);
   114         out.append("\n\n").append(assignClass(className));
   115         out.append("function CLS() {");
   116         out.append("\n  if (!CLS.prototype.$instOf_").append(className).append(") {");
   117         if (proto == null) {
   118             String sc = jc.getSuperClassName(); // with _
   119             out.append("\n    var pp = ").
   120                 append(accessClass(sc.replace('/', '_'))).append("(true);");
   121             out.append("\n    var p = CLS.prototype = pp;");
   122             out.append("\n    var c = p;");
   123             out.append("\n    var sprcls = pp.constructor.$class;");
   124         } else {
   125             out.append("\n    var p = CLS.prototype = ").append(proto[1]).append(";");
   126             if (proto[0] == null) {
   127                 proto[0] = "p";
   128             }
   129             out.append("\n    var c = ").append(proto[0]).append(";");
   130             out.append("\n    var sprcls = null;");
   131         }
   132         for (FieldData v : jc.getFields()) {
   133             if (v.isStatic()) {
   134                 out.append("\n  CLS.").append(v.getName()).append(initField(v));
   135             } else {
   136                 out.append("\n  c._").append(v.getName()).append(" = function (v) {")
   137                    .append("  if (arguments.length == 1) this.fld_").
   138                     append(className).append('_').append(v.getName())
   139                    .append(" = v; return this.fld_").
   140                     append(className).append('_').append(v.getName())
   141                    .append("; };");
   142             }
   143         }
   144         for (MethodData m : jc.getMethods()) {
   145             byte[] onlyArr = m.findAnnotationData(true);
   146             String[] only = findAnnotation(onlyArr, jc, 
   147                 "org.apidesign.bck2brwsr.core.JavaScriptOnly", 
   148                 "name", "value"
   149             );
   150             if (only != null) {
   151                 if (only[0] != null && only[1] != null) {
   152                     out.append("\n    p.").append(only[0]).append(" = ")
   153                         .append(only[1]).append(";");
   154                 }
   155                 continue;
   156             }
   157             String prefix;
   158             String mn;
   159             if (m.isStatic()) {
   160                 prefix = "\n    c.";
   161                 mn = generateStaticMethod(prefix, m, toInitilize);
   162             } else {
   163                 if (m.isConstructor()) {
   164                     prefix = "\n    CLS.";
   165                     mn = generateInstanceMethod(prefix, m);
   166                 } else {
   167                     prefix = "\n    c.";
   168                     mn = generateInstanceMethod(prefix, m);
   169                 }
   170             }
   171             byte[] runAnno = m.findAnnotationData(false);
   172             if (runAnno != null) {
   173                 out.append(prefix).append(mn).append(".anno = {");
   174                 generateAnno(jc, out, runAnno);
   175                 out.append("\n    };");
   176             }
   177             out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";");
   178         }
   179         out.append("\n    c.constructor = CLS;");
   180         out.append("\n    c.$instOf_").append(className).append(" = true;");
   181         for (String superInterface : jc.getSuperInterfaces()) {
   182             out.append("\n    c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
   183         }
   184         out.append("\n    CLS.$class = ");
   185         out.append(accessClass("java_lang_Class(true);"));
   186         out.append("\n    CLS.$class.jvmName = '").append(jc.getClassName()).append("';");
   187         out.append("\n    CLS.$class.superclass = sprcls;");
   188         out.append("\n    CLS.$class.access = ").append(jc.getAccessFlags()+";");
   189         out.append("\n    CLS.$class.cnstr = CLS;");
   190         byte[] classAnno = jc.findAnnotationData(false);
   191         if (classAnno != null) {
   192             out.append("\n    CLS.$class.anno = {");
   193             generateAnno(jc, out, classAnno);
   194             out.append("\n    };");
   195         }
   196         out.append("\n  }");
   197         out.append("\n  if (arguments.length === 0) {");
   198         out.append("\n    if (!(this instanceof CLS)) {");
   199         out.append("\n      return new CLS();");
   200         out.append("\n    }");
   201         for (FieldData v : jc.getFields()) {
   202             byte[] onlyArr = v.findAnnotationData(true);
   203             String[] only = findAnnotation(onlyArr, jc, 
   204                 "org.apidesign.bck2brwsr.core.JavaScriptOnly", 
   205                 "name", "value"
   206             );
   207             if (only != null) {
   208                 if (only[0] != null && only[1] != null) {
   209                     out.append("\n    p.").append(only[0]).append(" = ")
   210                         .append(only[1]).append(";");
   211                 }
   212                 continue;
   213             }
   214             if (!v.isStatic()) {
   215                 out.append("\n    this.fld_").
   216                     append(className).append('_').
   217                     append(v.getName()).append(initField(v));
   218             }
   219         }
   220         out.append("\n    return this;");
   221         out.append("\n  }");
   222         out.append("\n  return arguments[0] ? new CLS() : CLS.prototype;");
   223         out.append("\n};");
   224         StringBuilder sb = new StringBuilder();
   225         for (String init : toInitilize.toArray()) {
   226             sb.append("\n").append(init).append("();");
   227         }
   228         return sb.toString();
   229     }
   230     private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException {
   231         String jsb = javaScriptBody(prefix, m, true);
   232         if (jsb != null) {
   233             return jsb;
   234         }
   235         final String mn = findMethodName(m, new StringBuilder());
   236         if (mn.equals("class__V")) {
   237             toInitilize.add(accessClass(className(jc)) + "(false)." + mn);
   238         }
   239         generateMethod(prefix, mn, m);
   240         return mn;
   241     }
   242 
   243     private String generateInstanceMethod(String prefix, MethodData m) throws IOException {
   244         String jsb = javaScriptBody(prefix, m, false);
   245         if (jsb != null) {
   246             return jsb;
   247         }
   248         final String mn = findMethodName(m, new StringBuilder());
   249         generateMethod(prefix, mn, m);
   250         return mn;
   251     }
   252 
   253     private void generateMethod(String prefix, String name, MethodData m)
   254             throws IOException {
   255         final StackMapIterator stackMapIterator = m.createStackMapIterator();
   256         TrapDataIterator trap = m.getTrapDataIterator();
   257         final LocalsMapper lmapper =
   258                 new LocalsMapper(stackMapIterator.getArguments());
   259 
   260         out.append(prefix).append(name).append(" = function(");
   261         lmapper.outputArguments(out, m.isStatic());
   262         out.append(") {").append("\n");
   263 
   264         final byte[] byteCodes = m.getCode();
   265         if (byteCodes == null) {
   266             out.append("  throw 'no code found for ")
   267                .append(jc.getClassName()).append('.')
   268                .append(m.getName()).append("';\n");
   269             out.append("};");
   270             return;
   271         }
   272 
   273         final StackMapper smapper = new StackMapper();
   274 
   275         if (!m.isStatic()) {
   276             out.append("  var ").append(" lcA0 = this;\n");
   277         }
   278 
   279         int lastStackFrame = -1;
   280         TrapData[] previousTrap = null;
   281         boolean wide = false;
   282         
   283         out.append("\n  var gt = 0;\n  for(;;) switch(gt) {\n");
   284         for (int i = 0; i < byteCodes.length; i++) {
   285             int prev = i;
   286             stackMapIterator.advanceTo(i);
   287             boolean changeInCatch = trap.advanceTo(i);
   288             if (changeInCatch || lastStackFrame != stackMapIterator.getFrameIndex()) {
   289                 if (previousTrap != null) {
   290                     generateCatch(previousTrap);
   291                     previousTrap = null;
   292                 }
   293             }
   294             if (lastStackFrame != stackMapIterator.getFrameIndex()) {
   295                 lastStackFrame = stackMapIterator.getFrameIndex();
   296                 lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals());
   297                 smapper.syncWithFrameStack(stackMapIterator.getFrameStack());
   298                 out.append("    case " + i).append(": ");            
   299                 changeInCatch = true;
   300             } else {
   301                 debug("    /* " + i + " */ ");
   302             }
   303             if (changeInCatch && trap.useTry()) {
   304                 out.append("try {");
   305                 previousTrap = trap.current();
   306             }
   307             final int c = readUByte(byteCodes, i);
   308             switch (c) {
   309                 case opc_aload_0:
   310                     emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(0));
   311                     break;
   312                 case opc_iload_0:
   313                     emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(0));
   314                     break;
   315                 case opc_lload_0:
   316                     emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(0));
   317                     break;
   318                 case opc_fload_0:
   319                     emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(0));
   320                     break;
   321                 case opc_dload_0:
   322                     emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(0));
   323                     break;
   324                 case opc_aload_1:
   325                     emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(1));
   326                     break;
   327                 case opc_iload_1:
   328                     emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(1));
   329                     break;
   330                 case opc_lload_1:
   331                     emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(1));
   332                     break;
   333                 case opc_fload_1:
   334                     emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(1));
   335                     break;
   336                 case opc_dload_1:
   337                     emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(1));
   338                     break;
   339                 case opc_aload_2:
   340                     emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(2));
   341                     break;
   342                 case opc_iload_2:
   343                     emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(2));
   344                     break;
   345                 case opc_lload_2:
   346                     emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(2));
   347                     break;
   348                 case opc_fload_2:
   349                     emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(2));
   350                     break;
   351                 case opc_dload_2:
   352                     emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(2));
   353                     break;
   354                 case opc_aload_3:
   355                     emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(3));
   356                     break;
   357                 case opc_iload_3:
   358                     emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(3));
   359                     break;
   360                 case opc_lload_3:
   361                     emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(3));
   362                     break;
   363                 case opc_fload_3:
   364                     emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(3));
   365                     break;
   366                 case opc_dload_3:
   367                     emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(3));
   368                     break;
   369                 case opc_iload: {
   370                     ++i;
   371                     final int indx = wide ? readUShort(byteCodes, i++)
   372                                           : readUByte(byteCodes, i);
   373                     wide = false;
   374                     emit(out, "var @1 = @2;",
   375                          smapper.pushI(), lmapper.getI(indx));
   376                     break;
   377                 }
   378                 case opc_lload: {
   379                     ++i;
   380                     final int indx = wide ? readUShort(byteCodes, i++)
   381                                           : readUByte(byteCodes, i);
   382                     wide = false;
   383                     emit(out, "var @1 = @2;",
   384                          smapper.pushL(), lmapper.getL(indx));
   385                     break;
   386                 }
   387                 case opc_fload: {
   388                     ++i;
   389                     final int indx = wide ? readUShort(byteCodes, i++)
   390                                           : readUByte(byteCodes, i);
   391                     wide = false;
   392                     emit(out, "var @1 = @2;",
   393                          smapper.pushF(), lmapper.getF(indx));
   394                     break;
   395                 }
   396                 case opc_dload: {
   397                     ++i;
   398                     final int indx = wide ? readUShort(byteCodes, i++)
   399                                           : readUByte(byteCodes, i);
   400                     wide = false;
   401                     emit(out, "var @1 = @2;",
   402                          smapper.pushD(), lmapper.getD(indx));
   403                     break;
   404                 }
   405                 case opc_aload: {
   406                     ++i;
   407                     final int indx = wide ? readUShort(byteCodes, i++)
   408                                           : readUByte(byteCodes, i);
   409                     wide = false;
   410                     emit(out, "var @1 = @2;",
   411                          smapper.pushA(), lmapper.getA(indx));
   412                     break;
   413                 }
   414                 case opc_istore: {
   415                     ++i;
   416                     final int indx = wide ? readUShort(byteCodes, i++)
   417                                           : readUByte(byteCodes, i);
   418                     wide = false;
   419                     emit(out, "var @1 = @2;",
   420                          lmapper.setI(indx), smapper.popI());
   421                     break;
   422                 }
   423                 case opc_lstore: {
   424                     ++i;
   425                     final int indx = wide ? readUShort(byteCodes, i++)
   426                                           : readUByte(byteCodes, i);
   427                     wide = false;
   428                     emit(out, "var @1 = @2;",
   429                          lmapper.setL(indx), smapper.popL());
   430                     break;
   431                 }
   432                 case opc_fstore: {
   433                     ++i;
   434                     final int indx = wide ? readUShort(byteCodes, i++)
   435                                           : readUByte(byteCodes, i);
   436                     wide = false;
   437                     emit(out, "var @1 = @2;",
   438                          lmapper.setF(indx), smapper.popF());
   439                     break;
   440                 }
   441                 case opc_dstore: {
   442                     ++i;
   443                     final int indx = wide ? readUShort(byteCodes, i++)
   444                                           : readUByte(byteCodes, i);
   445                     wide = false;
   446                     emit(out, "var @1 = @2;",
   447                          lmapper.setD(indx), smapper.popD());
   448                     break;
   449                 }
   450                 case opc_astore: {
   451                     ++i;
   452                     final int indx = wide ? readUShort(byteCodes, i++)
   453                                           : readUByte(byteCodes, i);
   454                     wide = false;
   455                     emit(out, "var @1 = @2;",
   456                          lmapper.setA(indx), smapper.popA());
   457                     break;
   458                 }
   459                 case opc_astore_0:
   460                     emit(out, "var @1 = @2;", lmapper.setA(0), smapper.popA());
   461                     break;
   462                 case opc_istore_0:
   463                     emit(out, "var @1 = @2;", lmapper.setI(0), smapper.popI());
   464                     break;
   465                 case opc_lstore_0:
   466                     emit(out, "var @1 = @2;", lmapper.setL(0), smapper.popL());
   467                     break;
   468                 case opc_fstore_0:
   469                     emit(out, "var @1 = @2;", lmapper.setF(0), smapper.popF());
   470                     break;
   471                 case opc_dstore_0:
   472                     emit(out, "var @1 = @2;", lmapper.setD(0), smapper.popD());
   473                     break;
   474                 case opc_astore_1:
   475                     emit(out, "var @1 = @2;", lmapper.setA(1), smapper.popA());
   476                     break;
   477                 case opc_istore_1:
   478                     emit(out, "var @1 = @2;", lmapper.setI(1), smapper.popI());
   479                     break;
   480                 case opc_lstore_1:
   481                     emit(out, "var @1 = @2;", lmapper.setL(1), smapper.popL());
   482                     break;
   483                 case opc_fstore_1:
   484                     emit(out, "var @1 = @2;", lmapper.setF(1), smapper.popF());
   485                     break;
   486                 case opc_dstore_1:
   487                     emit(out, "var @1 = @2;", lmapper.setD(1), smapper.popD());
   488                     break;
   489                 case opc_astore_2:
   490                     emit(out, "var @1 = @2;", lmapper.setA(2), smapper.popA());
   491                     break;
   492                 case opc_istore_2:
   493                     emit(out, "var @1 = @2;", lmapper.setI(2), smapper.popI());
   494                     break;
   495                 case opc_lstore_2:
   496                     emit(out, "var @1 = @2;", lmapper.setL(2), smapper.popL());
   497                     break;
   498                 case opc_fstore_2:
   499                     emit(out, "var @1 = @2;", lmapper.setF(2), smapper.popF());
   500                     break;
   501                 case opc_dstore_2:
   502                     emit(out, "var @1 = @2;", lmapper.setD(2), smapper.popD());
   503                     break;
   504                 case opc_astore_3:
   505                     emit(out, "var @1 = @2;", lmapper.setA(3), smapper.popA());
   506                     break;
   507                 case opc_istore_3:
   508                     emit(out, "var @1 = @2;", lmapper.setI(3), smapper.popI());
   509                     break;
   510                 case opc_lstore_3:
   511                     emit(out, "var @1 = @2;", lmapper.setL(3), smapper.popL());
   512                     break;
   513                 case opc_fstore_3:
   514                     emit(out, "var @1 = @2;", lmapper.setF(3), smapper.popF());
   515                     break;
   516                 case opc_dstore_3:
   517                     emit(out, "var @1 = @2;", lmapper.setD(3), smapper.popD());
   518                     break;
   519                 case opc_iadd:
   520                     emit(out, "@1 = @1.add32(@2);", smapper.getI(1), smapper.popI());
   521                     break;
   522                 case opc_ladd:
   523                     emit(out, "@1 += @2;", smapper.getL(1), smapper.popL());
   524                     break;
   525                 case opc_fadd:
   526                     emit(out, "@1 += @2;", smapper.getF(1), smapper.popF());
   527                     break;
   528                 case opc_dadd:
   529                     emit(out, "@1 += @2;", smapper.getD(1), smapper.popD());
   530                     break;
   531                 case opc_isub:
   532                     emit(out, "@1 = @1.sub32(@2);", smapper.getI(1), smapper.popI());
   533                     break;
   534                 case opc_lsub:
   535                     emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL());
   536                     break;
   537                 case opc_fsub:
   538                     emit(out, "@1 -= @2;", smapper.getF(1), smapper.popF());
   539                     break;
   540                 case opc_dsub:
   541                     emit(out, "@1 -= @2;", smapper.getD(1), smapper.popD());
   542                     break;
   543                 case opc_imul:
   544                     emit(out, "@1 = @1.mul32(@2);", smapper.getI(1), smapper.popI());
   545                     break;
   546                 case opc_lmul:
   547                     emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL());
   548                     break;
   549                 case opc_fmul:
   550                     emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF());
   551                     break;
   552                 case opc_dmul:
   553                     emit(out, "@1 *= @2;", smapper.getD(1), smapper.popD());
   554                     break;
   555                 case opc_idiv:
   556                     emit(out, "@1 = Math.floor(@1 / @2);",
   557                          smapper.getI(1), smapper.popI());
   558                     break;
   559                 case opc_ldiv:
   560                     emit(out, "@1 = Math.floor(@1 / @2);",
   561                          smapper.getL(1), smapper.popL());
   562                     break;
   563                 case opc_fdiv:
   564                     emit(out, "@1 /= @2;", smapper.getF(1), smapper.popF());
   565                     break;
   566                 case opc_ddiv:
   567                     emit(out, "@1 /= @2;", smapper.getD(1), smapper.popD());
   568                     break;
   569                 case opc_irem:
   570                     emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI());
   571                     break;
   572                 case opc_lrem:
   573                     emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL());
   574                     break;
   575                 case opc_frem:
   576                     emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF());
   577                     break;
   578                 case opc_drem:
   579                     emit(out, "@1 %= @2;", smapper.getD(1), smapper.popD());
   580                     break;
   581                 case opc_iand:
   582                     emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI());
   583                     break;
   584                 case opc_land:
   585                     emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL());
   586                     break;
   587                 case opc_ior:
   588                     emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI());
   589                     break;
   590                 case opc_lor:
   591                     emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL());
   592                     break;
   593                 case opc_ixor:
   594                     emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI());
   595                     break;
   596                 case opc_lxor:
   597                     emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL());
   598                     break;
   599                 case opc_ineg:
   600                     emit(out, "@1 = -@1;", smapper.getI(0));
   601                     break;
   602                 case opc_lneg:
   603                     emit(out, "@1 = -@1;", smapper.getL(0));
   604                     break;
   605                 case opc_fneg:
   606                     emit(out, "@1 = -@1;", smapper.getF(0));
   607                     break;
   608                 case opc_dneg:
   609                     emit(out, "@1 = -@1;", smapper.getD(0));
   610                     break;
   611                 case opc_ishl:
   612                     emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI());
   613                     break;
   614                 case opc_lshl:
   615                     emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI());
   616                     break;
   617                 case opc_ishr:
   618                     emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI());
   619                     break;
   620                 case opc_lshr:
   621                     emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI());
   622                     break;
   623                 case opc_iushr:
   624                     emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI());
   625                     break;
   626                 case opc_lushr:
   627                     emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI());
   628                     break;
   629                 case opc_iinc: {
   630                     ++i;
   631                     final int varIndx = wide ? readUShort(byteCodes, i++)
   632                                              : readUByte(byteCodes, i);
   633                     ++i;
   634                     final int incrBy = wide ? readIntArg(byteCodes, i++)
   635                                             : byteCodes[i];
   636                     wide = false;
   637                     if (incrBy == 1) {
   638                         emit(out, "@1++;", lmapper.getI(varIndx));
   639                     } else {
   640                         emit(out, "@1 += @2;",
   641                              lmapper.getI(varIndx),
   642                              Integer.toString(incrBy));
   643                     }
   644                     break;
   645                 }
   646                 case opc_return:
   647                     emit(out, "return;");
   648                     break;
   649                 case opc_ireturn:
   650                     emit(out, "return @1;", smapper.popI());
   651                     break;
   652                 case opc_lreturn:
   653                     emit(out, "return @1;", smapper.popL());
   654                     break;
   655                 case opc_freturn:
   656                     emit(out, "return @1;", smapper.popF());
   657                     break;
   658                 case opc_dreturn:
   659                     emit(out, "return @1;", smapper.popD());
   660                     break;
   661                 case opc_areturn:
   662                     emit(out, "return @1;", smapper.popA());
   663                     break;
   664                 case opc_i2l:
   665                     emit(out, "var @2 = @1;", smapper.popI(), smapper.pushL());
   666                     break;
   667                 case opc_i2f:
   668                     emit(out, "var @2 = @1;", smapper.popI(), smapper.pushF());
   669                     break;
   670                 case opc_i2d:
   671                     emit(out, "var @2 = @1;", smapper.popI(), smapper.pushD());
   672                     break;
   673                 case opc_l2i:
   674                     emit(out, "var @2 = @1;", smapper.popL(), smapper.pushI());
   675                     break;
   676                     // max int check?
   677                 case opc_l2f:
   678                     emit(out, "var @2 = @1;", smapper.popL(), smapper.pushF());
   679                     break;
   680                 case opc_l2d:
   681                     emit(out, "var @2 = @1;", smapper.popL(), smapper.pushD());
   682                     break;
   683                 case opc_f2d:
   684                     emit(out, "var @2 = @1;", smapper.popF(), smapper.pushD());
   685                     break;
   686                 case opc_d2f:
   687                     emit(out, "var @2 = @1;", smapper.popD(), smapper.pushF());
   688                     break;
   689                 case opc_f2i:
   690                     emit(out, "var @2 = Math.floor(@1);",
   691                          smapper.popF(), smapper.pushI());
   692                     break;
   693                 case opc_f2l:
   694                     emit(out, "var @2 = Math.floor(@1);",
   695                          smapper.popF(), smapper.pushL());
   696                     break;
   697                 case opc_d2i:
   698                     emit(out, "var @2 = Math.floor(@1);",
   699                          smapper.popD(), smapper.pushI());
   700                     break;
   701                 case opc_d2l:
   702                     emit(out, "var @2 = Math.floor(@1);",
   703                          smapper.popD(), smapper.pushL());
   704                     break;
   705                 case opc_i2b:
   706                     emit(out, "var @1 = @1.toInt8();", smapper.getI(0));
   707                     break;
   708                 case opc_i2c:
   709                     out.append("{ /* number conversion */ }");
   710                     break;
   711                 case opc_i2s:
   712                     emit(out, "var @1 = @1.toInt16();", smapper.getI(0));
   713                     break;
   714                 case opc_aconst_null:
   715                     emit(out, "var @1 = null;", smapper.pushA());
   716                     break;
   717                 case opc_iconst_m1:
   718                     emit(out, "var @1 = -1;", smapper.pushI());
   719                     break;
   720                 case opc_iconst_0:
   721                     emit(out, "var @1 = 0;", smapper.pushI());
   722                     break;
   723                 case opc_dconst_0:
   724                     emit(out, "var @1 = 0;", smapper.pushD());
   725                     break;
   726                 case opc_lconst_0:
   727                     emit(out, "var @1 = 0;", smapper.pushL());
   728                     break;
   729                 case opc_fconst_0:
   730                     emit(out, "var @1 = 0;", smapper.pushF());
   731                     break;
   732                 case opc_iconst_1:
   733                     emit(out, "var @1 = 1;", smapper.pushI());
   734                     break;
   735                 case opc_lconst_1:
   736                     emit(out, "var @1 = 1;", smapper.pushL());
   737                     break;
   738                 case opc_fconst_1:
   739                     emit(out, "var @1 = 1;", smapper.pushF());
   740                     break;
   741                 case opc_dconst_1:
   742                     emit(out, "var @1 = 1;", smapper.pushD());
   743                     break;
   744                 case opc_iconst_2:
   745                     emit(out, "var @1 = 2;", smapper.pushI());
   746                     break;
   747                 case opc_fconst_2:
   748                     emit(out, "var @1 = 2;", smapper.pushF());
   749                     break;
   750                 case opc_iconst_3:
   751                     emit(out, "var @1 = 3;", smapper.pushI());
   752                     break;
   753                 case opc_iconst_4:
   754                     emit(out, "var @1 = 4;", smapper.pushI());
   755                     break;
   756                 case opc_iconst_5:
   757                     emit(out, "var @1 = 5;", smapper.pushI());
   758                     break;
   759                 case opc_ldc: {
   760                     int indx = readUByte(byteCodes, ++i);
   761                     String v = encodeConstant(indx);
   762                     int type = VarType.fromConstantType(jc.getTag(indx));
   763                     emit(out, "var @1 = @2;", smapper.pushT(type), v);
   764                     break;
   765                 }
   766                 case opc_ldc_w:
   767                 case opc_ldc2_w: {
   768                     int indx = readIntArg(byteCodes, i);
   769                     i += 2;
   770                     String v = encodeConstant(indx);
   771                     int type = VarType.fromConstantType(jc.getTag(indx));
   772                     emit(out, "var @1 = @2;", smapper.pushT(type), v);
   773                     break;
   774                 }
   775                 case opc_lcmp:
   776                     emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   777                          smapper.popL(), smapper.popL(), smapper.pushI());
   778                     break;
   779                 case opc_fcmpl:
   780                 case opc_fcmpg:
   781                     emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   782                          smapper.popF(), smapper.popF(), smapper.pushI());
   783                     break;
   784                 case opc_dcmpl:
   785                 case opc_dcmpg:
   786                     emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   787                          smapper.popD(), smapper.popD(), smapper.pushI());
   788                     break;
   789                 case opc_if_acmpeq:
   790                     i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
   791                                    "===");
   792                     break;
   793                 case opc_if_acmpne:
   794                     i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
   795                                    "!=");
   796                     break;
   797                 case opc_if_icmpeq:
   798                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   799                                    "==");
   800                     break;
   801                 case opc_ifeq: {
   802                     int indx = i + readIntArg(byteCodes, i);
   803                     emit(out, "if (@1 == 0) { gt = @2; continue; }",
   804                          smapper.popI(), Integer.toString(indx));
   805                     i += 2;
   806                     break;
   807                 }
   808                 case opc_ifne: {
   809                     int indx = i + readIntArg(byteCodes, i);
   810                     emit(out, "if (@1 != 0) { gt = @2; continue; }",
   811                          smapper.popI(), Integer.toString(indx));
   812                     i += 2;
   813                     break;
   814                 }
   815                 case opc_iflt: {
   816                     int indx = i + readIntArg(byteCodes, i);
   817                     emit(out, "if (@1 < 0) { gt = @2; continue; }",
   818                          smapper.popI(), Integer.toString(indx));
   819                     i += 2;
   820                     break;
   821                 }
   822                 case opc_ifle: {
   823                     int indx = i + readIntArg(byteCodes, i);
   824                     emit(out, "if (@1 <= 0) { gt = @2; continue; }",
   825                          smapper.popI(), Integer.toString(indx));
   826                     i += 2;
   827                     break;
   828                 }
   829                 case opc_ifgt: {
   830                     int indx = i + readIntArg(byteCodes, i);
   831                     emit(out, "if (@1 > 0) { gt = @2; continue; }",
   832                          smapper.popI(), Integer.toString(indx));
   833                     i += 2;
   834                     break;
   835                 }
   836                 case opc_ifge: {
   837                     int indx = i + readIntArg(byteCodes, i);
   838                     emit(out, "if (@1 >= 0) { gt = @2; continue; }",
   839                          smapper.popI(), Integer.toString(indx));
   840                     i += 2;
   841                     break;
   842                 }
   843                 case opc_ifnonnull: {
   844                     int indx = i + readIntArg(byteCodes, i);
   845                     emit(out, "if (@1 !== null) { gt = @2; continue; }",
   846                          smapper.popA(), Integer.toString(indx));
   847                     i += 2;
   848                     break;
   849                 }
   850                 case opc_ifnull: {
   851                     int indx = i + readIntArg(byteCodes, i);
   852                     emit(out, "if (@1 === null) { gt = @2; continue; }",
   853                          smapper.popA(), Integer.toString(indx));
   854                     i += 2;
   855                     break;
   856                 }
   857                 case opc_if_icmpne:
   858                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   859                                    "!=");
   860                     break;
   861                 case opc_if_icmplt:
   862                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   863                                    "<");
   864                     break;
   865                 case opc_if_icmple:
   866                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   867                                    "<=");
   868                     break;
   869                 case opc_if_icmpgt:
   870                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   871                                    ">");
   872                     break;
   873                 case opc_if_icmpge:
   874                     i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   875                                    ">=");
   876                     break;
   877                 case opc_goto: {
   878                     int indx = i + readIntArg(byteCodes, i);
   879                     emit(out, "gt = @1; continue;", Integer.toString(indx));
   880                     i += 2;
   881                     break;
   882                 }
   883                 case opc_lookupswitch: {
   884                     int table = i / 4 * 4 + 4;
   885                     int dflt = i + readInt4(byteCodes, table);
   886                     table += 4;
   887                     int n = readInt4(byteCodes, table);
   888                     table += 4;
   889                     out.append("switch (").append(smapper.popI()).append(") {\n");
   890                     while (n-- > 0) {
   891                         int cnstnt = readInt4(byteCodes, table);
   892                         table += 4;
   893                         int offset = i + readInt4(byteCodes, table);
   894                         table += 4;
   895                         out.append("  case " + cnstnt).append(": gt = " + offset).append("; continue;\n");
   896                     }
   897                     out.append("  default: gt = " + dflt).append("; continue;\n}");
   898                     i = table - 1;
   899                     break;
   900                 }
   901                 case opc_tableswitch: {
   902                     int table = i / 4 * 4 + 4;
   903                     int dflt = i + readInt4(byteCodes, table);
   904                     table += 4;
   905                     int low = readInt4(byteCodes, table);
   906                     table += 4;
   907                     int high = readInt4(byteCodes, table);
   908                     table += 4;
   909                     out.append("switch (").append(smapper.popI()).append(") {\n");
   910                     while (low <= high) {
   911                         int offset = i + readInt4(byteCodes, table);
   912                         table += 4;
   913                         out.append("  case " + low).append(": gt = " + offset).append("; continue;\n");
   914                         low++;
   915                     }
   916                     out.append("  default: gt = " + dflt).append("; continue;\n}");
   917                     i = table - 1;
   918                     break;
   919                 }
   920                 case opc_invokeinterface: {
   921                     i = invokeVirtualMethod(byteCodes, i, smapper) + 2;
   922                     break;
   923                 }
   924                 case opc_invokevirtual:
   925                     i = invokeVirtualMethod(byteCodes, i, smapper);
   926                     break;
   927                 case opc_invokespecial:
   928                     i = invokeStaticMethod(byteCodes, i, smapper, false);
   929                     break;
   930                 case opc_invokestatic:
   931                     i = invokeStaticMethod(byteCodes, i, smapper, true);
   932                     break;
   933                 case opc_new: {
   934                     int indx = readIntArg(byteCodes, i);
   935                     String ci = jc.getClassName(indx);
   936                     emit(out, "var @1 = new @2;",
   937                          smapper.pushA(), accessClass(ci.replace('/', '_')));
   938                     addReference(ci);
   939                     i += 2;
   940                     break;
   941                 }
   942                 case opc_newarray:
   943                     int atype = readUByte(byteCodes, ++i);
   944                     String jvmType;
   945                     switch (atype) {
   946                         case 4: jvmType = "[Z"; break;
   947                         case 5: jvmType = "[C"; break;
   948                         case 6: jvmType = "[F"; break;
   949                         case 7: jvmType = "[D"; break;
   950                         case 8: jvmType = "[B"; break;
   951                         case 9: jvmType = "[S"; break;
   952                         case 10: jvmType = "[I"; break;
   953                         case 11: jvmType = "[J"; break;
   954                         default: throw new IllegalStateException("Array type: " + atype);
   955                     }
   956                     emit(out, "var @2 = Array.prototype.newArray__Ljava_lang_Object_2ZLjava_lang_String_2I(true, '@3', @1);",
   957                          smapper.popI(), smapper.pushA(), jvmType);
   958                     break;
   959                 case opc_anewarray: {
   960                     int type = readIntArg(byteCodes, i);
   961                     i += 2;
   962                     String typeName = jc.getClassName(type);
   963                     if (typeName.startsWith("[")) {
   964                         typeName = "[" + typeName;
   965                     } else {
   966                         typeName = "[L" + typeName + ";";
   967                     }
   968                     emit(out, "var @2 = Array.prototype.newArray__Ljava_lang_Object_2ZLjava_lang_String_2I(false, '@3', @1);",
   969                          smapper.popI(), smapper.pushA(), typeName);
   970                     break;
   971                 }
   972                 case opc_multianewarray: {
   973                     int type = readIntArg(byteCodes, i);
   974                     i += 2;
   975                     String typeName = jc.getClassName(type);
   976                     int dim = readUByte(byteCodes, ++i);
   977                     StringBuilder dims = new StringBuilder();
   978                     dims.append('[');
   979                     for (int d = 0; d < dim; d++) {
   980                         if (d != 0) {
   981                             dims.append(",");
   982                         }
   983                         dims.append(smapper.popI());
   984                     }
   985                     dims.append(']');
   986                     emit(out, "var @2 = Array.prototype.multiNewArray__Ljava_lang_Object_2Ljava_lang_String_2_3II('@3', @1, 0);",
   987                          dims.toString(), smapper.pushA(), typeName);
   988                     break;
   989                 }
   990                 case opc_arraylength:
   991                     emit(out, "var @2 = @1.length;",
   992                          smapper.popA(), smapper.pushI());
   993                     break;
   994                 case opc_lastore:
   995                     emit(out, "@3.at(@2, @1);",
   996                          smapper.popL(), smapper.popI(), smapper.popA());
   997                     break;
   998                 case opc_fastore:
   999                     emit(out, "@3.at(@2, @1);",
  1000                          smapper.popF(), smapper.popI(), smapper.popA());
  1001                     break;
  1002                 case opc_dastore:
  1003                     emit(out, "@3.at(@2, @1);",
  1004                          smapper.popD(), smapper.popI(), smapper.popA());
  1005                     break;
  1006                 case opc_aastore:
  1007                     emit(out, "@3.at(@2, @1);",
  1008                          smapper.popA(), smapper.popI(), smapper.popA());
  1009                     break;
  1010                 case opc_iastore:
  1011                 case opc_bastore:
  1012                 case opc_castore:
  1013                 case opc_sastore:
  1014                     emit(out, "@3.at(@2, @1);",
  1015                          smapper.popI(), smapper.popI(), smapper.popA());
  1016                     break;
  1017                 case opc_laload:
  1018                     emit(out, "var @3 = @2.at(@1);",
  1019                          smapper.popI(), smapper.popA(), smapper.pushL());
  1020                     break;
  1021                 case opc_faload:
  1022                     emit(out, "var @3 = @2.at(@1);",
  1023                          smapper.popI(), smapper.popA(), smapper.pushF());
  1024                     break;
  1025                 case opc_daload:
  1026                     emit(out, "var @3 = @2.at(@1);",
  1027                          smapper.popI(), smapper.popA(), smapper.pushD());
  1028                     break;
  1029                 case opc_aaload:
  1030                     emit(out, "var @3 = @2.at(@1);",
  1031                          smapper.popI(), smapper.popA(), smapper.pushA());
  1032                     break;
  1033                 case opc_iaload:
  1034                 case opc_baload:
  1035                 case opc_caload:
  1036                 case opc_saload:
  1037                     emit(out, "var @3 = @2.at(@1);",
  1038                          smapper.popI(), smapper.popA(), smapper.pushI());
  1039                     break;
  1040                 case opc_pop:
  1041                 case opc_pop2:
  1042                     smapper.pop(1);
  1043                     debug("/* pop */");
  1044                     break;
  1045                 case opc_dup: {
  1046                     final Variable v = smapper.get(0);
  1047                     emit(out, "var @1 = @2;", smapper.pushT(v.getType()), v);
  1048                     break;
  1049                 }
  1050                 case opc_dup2: {
  1051                     final Variable vi1 = smapper.get(0);
  1052 
  1053                     if (vi1.isCategory2()) {
  1054                         emit(out, "var @1 = @2;",
  1055                              smapper.pushT(vi1.getType()), vi1);
  1056                     } else {
  1057                         final Variable vi2 = smapper.get(1);
  1058                         emit(out, "var @1 = @2, @3 = @4;",
  1059                              smapper.pushT(vi2.getType()), vi2,
  1060                              smapper.pushT(vi1.getType()), vi1);
  1061                     }
  1062                     break;
  1063                 }
  1064                 case opc_dup_x1: {
  1065                     final Variable vi1 = smapper.pop();
  1066                     final Variable vi2 = smapper.pop();
  1067                     final Variable vo3 = smapper.pushT(vi1.getType());
  1068                     final Variable vo2 = smapper.pushT(vi2.getType());
  1069                     final Variable vo1 = smapper.pushT(vi1.getType());
  1070 
  1071                     emit(out, "var @1 = @2, @3 = @4, @5 = @6;",
  1072                          vo1, vi1, vo2, vi2, vo3, vo1);
  1073                     break;
  1074                 }
  1075                 case opc_dup2_x1: {
  1076                     final Variable vi1 = smapper.pop();
  1077                     final Variable vi2 = smapper.pop();
  1078 
  1079                     if (vi1.isCategory2()) {
  1080                         final Variable vo3 = smapper.pushT(vi1.getType());
  1081                         final Variable vo2 = smapper.pushT(vi2.getType());
  1082                         final Variable vo1 = smapper.pushT(vi1.getType());
  1083 
  1084                         emit(out, "var @1 = @2, @3 = @4, @5 = @6;",
  1085                              vo1, vi1, vo2, vi2, vo3, vo1);
  1086                     } else {
  1087                         final Variable vi3 = smapper.pop();
  1088                         final Variable vo5 = smapper.pushT(vi2.getType());
  1089                         final Variable vo4 = smapper.pushT(vi1.getType());
  1090                         final Variable vo3 = smapper.pushT(vi3.getType());
  1091                         final Variable vo2 = smapper.pushT(vi2.getType());
  1092                         final Variable vo1 = smapper.pushT(vi1.getType());
  1093 
  1094                         emit(out, "var @1 = @2, @3 = @4, @5 = @6,",
  1095                              vo1, vi1, vo2, vi2, vo3, vi3);
  1096                         emit(out, " @1 = @2, @3 = @4;",
  1097                              vo4, vo1, vo5, vo2);
  1098                     }
  1099                     break;
  1100                 }
  1101                 case opc_dup_x2: {
  1102                     final Variable vi1 = smapper.pop();
  1103                     final Variable vi2 = smapper.pop();
  1104 
  1105                     if (vi2.isCategory2()) {
  1106                         final Variable vo3 = smapper.pushT(vi1.getType());
  1107                         final Variable vo2 = smapper.pushT(vi2.getType());
  1108                         final Variable vo1 = smapper.pushT(vi1.getType());
  1109 
  1110                         emit(out, "var @1 = @2, @3 = @4, @5 = @6;",
  1111                              vo1, vi1, vo2, vi2, vo3, vo1);
  1112                     } else {
  1113                         final Variable vi3 = smapper.pop();
  1114                         final Variable vo4 = smapper.pushT(vi1.getType());
  1115                         final Variable vo3 = smapper.pushT(vi3.getType());
  1116                         final Variable vo2 = smapper.pushT(vi2.getType());
  1117                         final Variable vo1 = smapper.pushT(vi1.getType());
  1118 
  1119                         emit(out, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8;",
  1120                              vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1);
  1121                     }
  1122                     break;
  1123                 }
  1124                 case opc_dup2_x2: {
  1125                     final Variable vi1 = smapper.pop();
  1126                     final Variable vi2 = smapper.pop();
  1127 
  1128                     if (vi1.isCategory2()) {
  1129                         if (vi2.isCategory2()) {
  1130                             final Variable vo3 = smapper.pushT(vi1.getType());
  1131                             final Variable vo2 = smapper.pushT(vi2.getType());
  1132                             final Variable vo1 = smapper.pushT(vi1.getType());
  1133 
  1134                             emit(out, "var @1 = @2, @3 = @4, @5 = @6;",
  1135                                  vo1, vi1, vo2, vi2, vo3, vo1);
  1136                         } else {
  1137                             final Variable vi3 = smapper.pop();
  1138                             final Variable vo4 = smapper.pushT(vi1.getType());
  1139                             final Variable vo3 = smapper.pushT(vi3.getType());
  1140                             final Variable vo2 = smapper.pushT(vi2.getType());
  1141                             final Variable vo1 = smapper.pushT(vi1.getType());
  1142 
  1143                             emit(out, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8;",
  1144                                  vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1);
  1145                         }
  1146                     } else {
  1147                         final Variable vi3 = smapper.pop();
  1148 
  1149                         if (vi3.isCategory2()) {
  1150                             final Variable vo5 = smapper.pushT(vi2.getType());
  1151                             final Variable vo4 = smapper.pushT(vi1.getType());
  1152                             final Variable vo3 = smapper.pushT(vi3.getType());
  1153                             final Variable vo2 = smapper.pushT(vi2.getType());
  1154                             final Variable vo1 = smapper.pushT(vi1.getType());
  1155 
  1156                             emit(out, "var @1 = @2, @3 = @4, @5 = @6,",
  1157                                  vo1, vi1, vo2, vi2, vo3, vi3);
  1158                             emit(out, " @1 = @2, @3 = @4;",
  1159                                  vo4, vo1, vo5, vo2);
  1160                         } else {
  1161                             final Variable vi4 = smapper.pop();
  1162                             final Variable vo6 = smapper.pushT(vi2.getType());
  1163                             final Variable vo5 = smapper.pushT(vi1.getType());
  1164                             final Variable vo4 = smapper.pushT(vi4.getType());
  1165                             final Variable vo3 = smapper.pushT(vi3.getType());
  1166                             final Variable vo2 = smapper.pushT(vi2.getType());
  1167                             final Variable vo1 = smapper.pushT(vi1.getType());
  1168                             
  1169                             emit(out, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8,",
  1170                                  vo1, vi1, vo2, vi2, vo3, vi3, vo4, vi4);
  1171                             emit(out, " @1 = @2, @3 = @4;",
  1172                                  vo5, vo1, vo6, vo2);
  1173                         }
  1174                     }
  1175                     break;
  1176                 }
  1177                 case opc_swap: {
  1178                     final Variable vi1 = smapper.get(0);
  1179                     final Variable vi2 = smapper.get(1);
  1180 
  1181                     if (vi1.getType() == vi2.getType()) {
  1182                         final Variable tmp = smapper.pushT(vi1.getType());
  1183 
  1184                         emit(out, "var @1 = @2, @2 = @3, @3 = @1;",
  1185                              tmp, vi1, vi2);
  1186                         smapper.pop(1);
  1187                     } else {
  1188                         smapper.pop(2);
  1189                         smapper.pushT(vi1.getType());
  1190                         smapper.pushT(vi2.getType());
  1191                     }
  1192                     break;
  1193                 }
  1194                 case opc_bipush:
  1195                     emit(out, "var @1 = @2;",
  1196                          smapper.pushI(), Integer.toString(byteCodes[++i]));
  1197                     break;
  1198                 case opc_sipush:
  1199                     emit(out, "var @1 = @2;",
  1200                          smapper.pushI(),
  1201                          Integer.toString(readIntArg(byteCodes, i)));
  1202                     i += 2;
  1203                     break;
  1204                 case opc_getfield: {
  1205                     int indx = readIntArg(byteCodes, i);
  1206                     String[] fi = jc.getFieldInfoName(indx);
  1207                     final int type = VarType.fromFieldType(fi[2].charAt(0));
  1208                     final String mangleClass = mangleSig(fi[0]);
  1209                     final String mangleClassAccess = accessClass(mangleClass);
  1210                     emit(out, "var @2 = @4(false)._@3.call(@1);",
  1211                          smapper.popA(),
  1212                          smapper.pushT(type), fi[1], mangleClassAccess
  1213                     );
  1214                     i += 2;
  1215                     break;
  1216                 }
  1217                 case opc_putfield: {
  1218                     int indx = readIntArg(byteCodes, i);
  1219                     String[] fi = jc.getFieldInfoName(indx);
  1220                     final int type = VarType.fromFieldType(fi[2].charAt(0));
  1221                     final String mangleClass = mangleSig(fi[0]);
  1222                     final String mangleClassAccess = accessClass(mangleClass);
  1223                     emit(out, "@4(false)._@3.call(@2, @1);",
  1224                          smapper.popT(type),
  1225                          smapper.popA(), fi[1], 
  1226                          mangleClassAccess
  1227                     );
  1228                     i += 2;
  1229                     break;
  1230                 }
  1231                 case opc_getstatic: {
  1232                     int indx = readIntArg(byteCodes, i);
  1233                     String[] fi = jc.getFieldInfoName(indx);
  1234                     final int type = VarType.fromFieldType(fi[2].charAt(0));
  1235                     emit(out, "var @1 = @2(false).constructor.@3;",
  1236                          smapper.pushT(type),
  1237                          accessClass(fi[0].replace('/', '_')), fi[1]);
  1238                     i += 2;
  1239                     addReference(fi[0]);
  1240                     break;
  1241                 }
  1242                 case opc_putstatic: {
  1243                     int indx = readIntArg(byteCodes, i);
  1244                     String[] fi = jc.getFieldInfoName(indx);
  1245                     final int type = VarType.fromFieldType(fi[2].charAt(0));
  1246                     emit(out, "@1(false).constructor.@2 = @3;",
  1247                          accessClass(fi[0].replace('/', '_')), fi[1],
  1248                          smapper.popT(type));
  1249                     i += 2;
  1250                     addReference(fi[0]);
  1251                     break;
  1252                 }
  1253                 case opc_checkcast: {
  1254                     int indx = readIntArg(byteCodes, i);
  1255                     final String type = jc.getClassName(indx);
  1256                     if (!type.startsWith("[")) {
  1257                         emit(out,
  1258                              "if (@1 !== null && !@1.$instOf_@2) throw {};",
  1259                              smapper.getA(0), type.replace('/', '_'));
  1260                     } else {
  1261                         emit(out, "vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@2').cast__Ljava_lang_Object_2Ljava_lang_Object_2(@1);",
  1262                              smapper.getA(0), type
  1263                         );
  1264                     }
  1265                     i += 2;
  1266                     break;
  1267                 }
  1268                 case opc_instanceof: {
  1269                     int indx = readIntArg(byteCodes, i);
  1270                     final String type = jc.getClassName(indx);
  1271                     if (!type.startsWith("[")) {
  1272                         emit(out, "var @2 = @1.$instOf_@3 ? 1 : 0;",
  1273                              smapper.popA(), smapper.pushI(),
  1274                              type.replace('/', '_'));
  1275                     } else {
  1276                         emit(out, "var @2 = vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@3').isInstance__ZLjava_lang_Object_2(@1);",
  1277                             smapper.popA(), smapper.pushI(),
  1278                             type
  1279                         );
  1280                     }
  1281                     i += 2;
  1282                     break;
  1283                 }
  1284                 case opc_athrow: {
  1285                     final Variable v = smapper.popA();
  1286                     smapper.clear();
  1287 
  1288                     emit(out, "{ var @1 = @2; throw @2; }",
  1289                          smapper.pushA(), v);
  1290                     break;
  1291                 }
  1292 
  1293                 case opc_monitorenter: {
  1294                     out.append("/* monitor enter */");
  1295                     smapper.popA();
  1296                     break;
  1297                 }
  1298 
  1299                 case opc_monitorexit: {
  1300                     out.append("/* monitor exit */");
  1301                     smapper.popA();
  1302                     break;
  1303                 }
  1304 
  1305                 case opc_wide:
  1306                     wide = true;
  1307                     break;
  1308 
  1309                 default: {
  1310                     wide = false;
  1311                     emit(out, "throw 'unknown bytecode @1';",
  1312                          Integer.toString(c));
  1313                 }
  1314             }
  1315             if (debug(" //")) {
  1316                 for (int j = prev; j <= i; j++) {
  1317                     out.append(" ");
  1318                     final int cc = readUByte(byteCodes, j);
  1319                     out.append(Integer.toString(cc));
  1320                 }
  1321             }
  1322             out.append("\n");            
  1323         }
  1324         if (previousTrap != null) {
  1325             generateCatch(previousTrap);
  1326         }
  1327         out.append("  }\n");
  1328         out.append("};");
  1329     }
  1330 
  1331     private int generateIf(byte[] byteCodes, int i,
  1332                            final Variable v2, final Variable v1,
  1333                            final String test) throws IOException {
  1334         int indx = i + readIntArg(byteCodes, i);
  1335         out.append("if (").append(v1)
  1336            .append(' ').append(test).append(' ')
  1337            .append(v2).append(") { gt = " + indx)
  1338            .append("; continue; }");
  1339         return i + 2;
  1340     }
  1341 
  1342     private int readIntArg(byte[] byteCodes, int offsetInstruction) {
  1343         final int indxHi = byteCodes[offsetInstruction + 1] << 8;
  1344         final int indxLo = byteCodes[offsetInstruction + 2];
  1345         return (indxHi & 0xffffff00) | (indxLo & 0xff);
  1346     }
  1347     private int readInt4(byte[] byteCodes, int offsetInstruction) {
  1348         final int d = byteCodes[offsetInstruction + 0] << 24;
  1349         final int c = byteCodes[offsetInstruction + 1] << 16;
  1350         final int b = byteCodes[offsetInstruction + 2] << 8;
  1351         final int a = byteCodes[offsetInstruction + 3];
  1352         return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
  1353     }
  1354     private int readUByte(byte[] byteCodes, int offsetInstruction) {
  1355         return byteCodes[offsetInstruction] & 0xff;
  1356     }
  1357 
  1358     private int readUShort(byte[] byteCodes, int offsetInstruction) {
  1359         return ((byteCodes[offsetInstruction] & 0xff) << 8)
  1360                     | (byteCodes[offsetInstruction + 1] & 0xff);
  1361     }
  1362 
  1363     private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
  1364         int i = 0;
  1365         Boolean count = null;
  1366         boolean array = false;
  1367         sig.append("__");
  1368         int firstPos = sig.length();
  1369         while (i < descriptor.length()) {
  1370             char ch = descriptor.charAt(i++);
  1371             switch (ch) {
  1372                 case '(':
  1373                     count = true;
  1374                     continue;
  1375                 case ')':
  1376                     count = false;
  1377                     continue;
  1378                 case 'B': 
  1379                 case 'C': 
  1380                 case 'D': 
  1381                 case 'F': 
  1382                 case 'I': 
  1383                 case 'J': 
  1384                 case 'S': 
  1385                 case 'Z': 
  1386                     if (count) {
  1387                         if (array) {
  1388                             sig.append("_3");
  1389                         }
  1390                         sig.append(ch);
  1391                         if (ch == 'J' || ch == 'D') {
  1392                             cnt.append('1');
  1393                         } else {
  1394                             cnt.append('0');
  1395                         }
  1396                     } else {
  1397                         sig.insert(firstPos, ch);
  1398                         if (array) {
  1399                             returnType[0] = '[';
  1400                             sig.insert(firstPos, "_3");
  1401                         } else {
  1402                             returnType[0] = ch;
  1403                         }
  1404                     }
  1405                     array = false;
  1406                     continue;
  1407                 case 'V': 
  1408                     assert !count;
  1409                     returnType[0] = 'V';
  1410                     sig.insert(firstPos, 'V');
  1411                     continue;
  1412                 case 'L':
  1413                     int next = descriptor.indexOf(';', i);
  1414                     String realSig = mangleSig(descriptor, i - 1, next + 1);
  1415                     if (count) {
  1416                         if (array) {
  1417                             sig.append("_3");
  1418                         }
  1419                         sig.append(realSig);
  1420                         cnt.append('0');
  1421                     } else {
  1422                         sig.insert(firstPos, realSig);
  1423                         if (array) {
  1424                             sig.insert(firstPos, "_3");
  1425                         }
  1426                         returnType[0] = 'L';
  1427                     }
  1428                     i = next + 1;
  1429                     array = false;
  1430                     continue;
  1431                 case '[':
  1432                     array = true;
  1433                     continue;
  1434                 default:
  1435                     throw new IllegalStateException("Invalid char: " + ch);
  1436             }
  1437         }
  1438     }
  1439     
  1440     static String mangleSig(String sig) {
  1441         return mangleSig(sig, 0, sig.length());
  1442     }
  1443     
  1444     private static String mangleSig(String txt, int first, int last) {
  1445         StringBuilder sb = new StringBuilder();
  1446         for (int i = first; i < last; i++) {
  1447             final char ch = txt.charAt(i);
  1448             switch (ch) {
  1449                 case '/': sb.append('_'); break;
  1450                 case '_': sb.append("_1"); break;
  1451                 case ';': sb.append("_2"); break;
  1452                 case '[': sb.append("_3"); break;
  1453                 default: sb.append(ch); break;
  1454             }
  1455         }
  1456         return sb.toString();
  1457     }
  1458 
  1459     private static String findMethodName(MethodData m, StringBuilder cnt) {
  1460         StringBuilder name = new StringBuilder();
  1461         if ("<init>".equals(m.getName())) { // NOI18N
  1462             name.append("cons"); // NOI18N
  1463         } else if ("<clinit>".equals(m.getName())) { // NOI18N
  1464             name.append("class"); // NOI18N
  1465         } else {
  1466             name.append(m.getName());
  1467         } 
  1468         
  1469         countArgs(m.getInternalSig(), new char[1], name, cnt);
  1470         return name.toString();
  1471     }
  1472 
  1473     static String findMethodName(String[] mi, StringBuilder cnt, char[] returnType) {
  1474         StringBuilder name = new StringBuilder();
  1475         String descr = mi[2];//mi.getDescriptor();
  1476         String nm= mi[1];
  1477         if ("<init>".equals(nm)) { // NOI18N
  1478             name.append("cons"); // NOI18N
  1479         } else {
  1480             name.append(nm);
  1481         }
  1482         countArgs(descr, returnType, name, cnt);
  1483         return name.toString();
  1484     }
  1485 
  1486     private int invokeStaticMethod(byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic)
  1487     throws IOException {
  1488         int methodIndex = readIntArg(byteCodes, i);
  1489         String[] mi = jc.getFieldInfoName(methodIndex);
  1490         char[] returnType = { 'V' };
  1491         StringBuilder cnt = new StringBuilder();
  1492         String mn = findMethodName(mi, cnt, returnType);
  1493 
  1494         final int numArguments = isStatic ? cnt.length() : cnt.length() + 1;
  1495         final Variable[] vars = new Variable[numArguments];
  1496 
  1497         for (int j = numArguments - 1; j >= 0; --j) {
  1498             vars[j] = mapper.pop();
  1499         }
  1500 
  1501         if (returnType[0] != 'V') {
  1502             out.append("var ")
  1503                .append(mapper.pushT(VarType.fromFieldType(returnType[0])))
  1504                .append(" = ");
  1505         }
  1506 
  1507         final String in = mi[0];
  1508         out.append(accessClass(in.replace('/', '_')));
  1509         out.append("(false).");
  1510         if (mn.startsWith("cons_")) {
  1511             out.append("constructor.");
  1512         }
  1513         out.append(mn);
  1514         if (isStatic) {
  1515             out.append('(');
  1516         } else {
  1517             out.append(".call(");
  1518         }
  1519         if (numArguments > 0) {
  1520             out.append(vars[0]);
  1521             for (int j = 1; j < numArguments; ++j) {
  1522                 out.append(", ");
  1523                 out.append(vars[j]);
  1524             }
  1525         }
  1526         out.append(");");
  1527         i += 2;
  1528         addReference(in);
  1529         return i;
  1530     }
  1531     private int invokeVirtualMethod(byte[] byteCodes, int i, final StackMapper mapper)
  1532     throws IOException {
  1533         int methodIndex = readIntArg(byteCodes, i);
  1534         String[] mi = jc.getFieldInfoName(methodIndex);
  1535         char[] returnType = { 'V' };
  1536         StringBuilder cnt = new StringBuilder();
  1537         String mn = findMethodName(mi, cnt, returnType);
  1538 
  1539         final int numArguments = cnt.length() + 1;
  1540         final Variable[] vars = new Variable[numArguments];
  1541 
  1542         for (int j = numArguments - 1; j >= 0; --j) {
  1543             vars[j] = mapper.pop();
  1544         }
  1545 
  1546         if (returnType[0] != 'V') {
  1547             out.append("var ")
  1548                .append(mapper.pushT(VarType.fromFieldType(returnType[0])))
  1549                .append(" = ");
  1550         }
  1551 
  1552         out.append(vars[0]).append('.');
  1553         out.append(mn);
  1554         out.append('(');
  1555         String sep = "";
  1556         for (int j = 1; j < numArguments; ++j) {
  1557             out.append(sep);
  1558             out.append(vars[j]);
  1559             sep = ", ";
  1560         }
  1561         out.append(");");
  1562         i += 2;
  1563         return i;
  1564     }
  1565 
  1566     private void addReference(String cn) throws IOException {
  1567         if (requireReference(cn)) {
  1568             debug(" /* needs " + cn + " */");
  1569         }
  1570     }
  1571 
  1572     private void outType(String d, StringBuilder out) {
  1573         int arr = 0;
  1574         while (d.charAt(0) == '[') {
  1575             out.append('A');
  1576             d = d.substring(1);
  1577         }
  1578         if (d.charAt(0) == 'L') {
  1579             assert d.charAt(d.length() - 1) == ';';
  1580             out.append(d.replace('/', '_').substring(0, d.length() - 1));
  1581         } else {
  1582             out.append(d);
  1583         }
  1584     }
  1585 
  1586     private String encodeConstant(int entryIndex) throws IOException {
  1587         String[] classRef = { null };
  1588         String s = jc.stringValue(entryIndex, classRef);
  1589         if (classRef[0] != null) {
  1590             if (classRef[0].startsWith("[")) {
  1591                 s = accessClass("java_lang_Class") + "(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('" + classRef[0] + "');";
  1592             } else {
  1593                 addReference(classRef[0]);
  1594                 s = accessClass(s.replace('/', '_')) + "(false).constructor.$class";
  1595             }
  1596         }
  1597         return s;
  1598     }
  1599 
  1600     private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException {
  1601         byte[] arr = m.findAnnotationData(true);
  1602         if (arr == null) {
  1603             return null;
  1604         }
  1605         final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;";
  1606         class P extends AnnotationParser {
  1607             public P() {
  1608                 super(false);
  1609             }
  1610             
  1611             int cnt;
  1612             String[] args = new String[30];
  1613             String body;
  1614             
  1615             @Override
  1616             protected void visitAttr(String type, String attr, String at, String value) {
  1617                 if (type.equals(jvmType)) {
  1618                     if ("body".equals(attr)) {
  1619                         body = value;
  1620                     } else if ("args".equals(attr)) {
  1621                         args[cnt++] = value;
  1622                     } else {
  1623                         throw new IllegalArgumentException(attr);
  1624                     }
  1625                 }
  1626             }
  1627         }
  1628         P p = new P();
  1629         p.parse(arr, jc);
  1630         if (p.body == null) {
  1631             return null;
  1632         }
  1633         StringBuilder cnt = new StringBuilder();
  1634         final String mn = findMethodName(m, cnt);
  1635         out.append(prefix).append(mn);
  1636         out.append(" = function(");
  1637         String space = "";
  1638         int index = 0;
  1639         for (int i = 0; i < cnt.length(); i++) {
  1640             out.append(space);
  1641             space = outputArg(out, p.args, index);
  1642             index++;
  1643         }
  1644         out.append(") {").append("\n");
  1645         out.append(p.body);
  1646         out.append("\n}\n");
  1647         return mn;
  1648     }
  1649     private static String className(ClassData jc) {
  1650         //return jc.getName().getInternalName().replace('/', '_');
  1651         return jc.getClassName().replace('/', '_');
  1652     }
  1653     
  1654     private static String[] findAnnotation(
  1655         byte[] arr, ClassData cd, final String className, 
  1656         final String... attrNames
  1657     ) throws IOException {
  1658         if (arr == null) {
  1659             return null;
  1660         }
  1661         final String[] values = new String[attrNames.length];
  1662         final boolean[] found = { false };
  1663         final String jvmType = "L" + className.replace('.', '/') + ";";
  1664         AnnotationParser ap = new AnnotationParser(false) {
  1665             @Override
  1666             protected void visitAttr(String type, String attr, String at, String value) {
  1667                 if (type.equals(jvmType)) {
  1668                     found[0] = true;
  1669                     for (int i = 0; i < attrNames.length; i++) {
  1670                         if (attrNames[i].equals(attr)) {
  1671                             values[i] = value;
  1672                         }
  1673                     }
  1674                 }
  1675             }
  1676             
  1677         };
  1678         ap.parse(arr, cd);
  1679         return found[0] ? values : null;
  1680     }
  1681 
  1682     private CharSequence initField(FieldData v) {
  1683         final String is = v.getInternalSig();
  1684         if (is.length() == 1) {
  1685             switch (is.charAt(0)) {
  1686                 case 'S':
  1687                 case 'J':
  1688                 case 'B':
  1689                 case 'Z':
  1690                 case 'C':
  1691                 case 'I': return " = 0;";
  1692                 case 'F': 
  1693                 case 'D': return " = 0.0;";
  1694                 default:
  1695                     throw new IllegalStateException(is);
  1696             }
  1697         }
  1698         return " = null;";
  1699     }
  1700 
  1701     private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException {
  1702         AnnotationParser ap = new AnnotationParser(true) {
  1703             int anno;
  1704             int cnt;
  1705             
  1706             @Override
  1707             protected void visitAnnotationStart(String type) throws IOException {
  1708                 if (anno++ > 0) {
  1709                     out.append(",");
  1710                 }
  1711                 out.append('"').append(type).append("\" : {\n");
  1712                 cnt = 0;
  1713             }
  1714 
  1715             @Override
  1716             protected void visitAnnotationEnd(String type) throws IOException {
  1717                 out.append("\n}\n");
  1718             }
  1719             
  1720             @Override
  1721             protected void visitAttr(String type, String attr, String attrType, String value) 
  1722             throws IOException {
  1723                 if (attr == null) {
  1724                     return;
  1725                 }
  1726                 if (cnt++ > 0) {
  1727                     out.append(",\n");
  1728                 }
  1729                 out.append(attr).append("__").append(attrType).append(" : ").append(value);
  1730             }
  1731         };
  1732         ap.parse(data, cd);
  1733     }
  1734 
  1735     private static String outputArg(Appendable out, String[] args, int indx) throws IOException {
  1736         final String name = args[indx];
  1737         if (name == null) {
  1738             return "";
  1739         }
  1740         if (name.contains(",")) {
  1741             throw new IOException("Wrong parameter with ',': " + name);
  1742         }
  1743         out.append(name);
  1744         return ",";
  1745     }
  1746 
  1747     private static void emit(final Appendable out,
  1748                              final String format,
  1749                              final CharSequence... params) throws IOException {
  1750         final int length = format.length();
  1751 
  1752         int processed = 0;
  1753         int paramOffset = format.indexOf('@');
  1754         while ((paramOffset != -1) && (paramOffset < (length - 1))) {
  1755             final char paramChar = format.charAt(paramOffset + 1);
  1756             if ((paramChar >= '1') && (paramChar <= '9')) {
  1757                 final int paramIndex = paramChar - '0' - 1;
  1758 
  1759                 out.append(format, processed, paramOffset);
  1760                 out.append(params[paramIndex]);
  1761 
  1762                 ++paramOffset;
  1763                 processed = paramOffset + 1;
  1764             }
  1765 
  1766             paramOffset = format.indexOf('@', paramOffset + 1);
  1767         }
  1768 
  1769         out.append(format, processed, length);
  1770     }
  1771 
  1772     private void generateCatch(TrapData[] traps) throws IOException {
  1773         out.append("} catch (e) {\n");
  1774         int finallyPC = -1;
  1775         for (TrapData e : traps) {
  1776             if (e == null) {
  1777                 break;
  1778             }
  1779             if (e.catch_cpx != 0) { //not finally
  1780                 final String classInternalName = jc.getClassName(e.catch_cpx);
  1781                 addReference(classInternalName);
  1782                 if ("java/lang/Throwable".equals(classInternalName)) {
  1783                     out.append("if (e.$instOf_java_lang_Throwable) {");
  1784                     out.append("  var stA0 = e;");
  1785                     out.append("} else {");
  1786                     out.append("  var stA0 = vm.java_lang_Throwable(true);");
  1787                     out.append("  vm.java_lang_Throwable.cons__VLjava_lang_String_2.call(stA0, e.toString());");
  1788                     out.append("}");
  1789                     out.append("gt=" + e.handler_pc + "; continue;");
  1790                 } else {
  1791                     out.append("if (e.$instOf_" + classInternalName.replace('/', '_') + ") {");
  1792                     out.append("gt=" + e.handler_pc + "; var stA0 = e; continue;");
  1793                     out.append("}\n");
  1794                 }
  1795             } else {
  1796                 finallyPC = e.handler_pc;
  1797             }
  1798         }
  1799         if (finallyPC == -1) {
  1800             out.append("throw e;");
  1801         } else {
  1802             out.append("gt=" + finallyPC + "; var stA0 = e; continue;");
  1803         }
  1804         out.append("\n}");
  1805     }
  1806 }