Merging Lubomir's work on registers into default branch
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 14 Dec 2012 11:32:45 +0100
changeset 3185876fdbbca11
parent 316 8da329789435
parent 317 15cbc8cb2163
child 320 92224eba98cb
Merging Lubomir's work on registers into default branch
     1.1 --- a/javap/src/main/java/org/apidesign/javap/MethodData.java	Thu Dec 13 23:20:47 2012 +0100
     1.2 +++ b/javap/src/main/java/org/apidesign/javap/MethodData.java	Fri Dec 14 11:32:45 2012 +0100
     1.3 @@ -317,6 +317,10 @@
     1.4          return stackMapTable;
     1.5      }
     1.6  
     1.7 +    public StackMapIterator createStackMapIterator() {
     1.8 +        return new StackMapIterator(this);
     1.9 +    }
    1.10 +
    1.11      /**
    1.12       * Return true if method is static
    1.13       */
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/javap/src/main/java/org/apidesign/javap/StackMapIterator.java	Fri Dec 14 11:32:45 2012 +0100
     2.3 @@ -0,0 +1,179 @@
     2.4 +/*
     2.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.7 + *
     2.8 + * This code is free software; you can redistribute it and/or modify it
     2.9 + * under the terms of the GNU General Public License version 2 only, as
    2.10 + * published by the Free Software Foundation.  Oracle designates this
    2.11 + * particular file as subject to the "Classpath" exception as provided
    2.12 + * by Oracle in the LICENSE file that accompanied this code.
    2.13 + *
    2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    2.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    2.17 + * version 2 for more details (a copy is included in the LICENSE file that
    2.18 + * accompanied this code).
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License version
    2.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    2.23 + *
    2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    2.25 + * or visit www.oracle.com if you need additional information or have any
    2.26 + * questions.
    2.27 + */
    2.28 +
    2.29 +package org.apidesign.javap;
    2.30 +
    2.31 +import static org.apidesign.javap.RuntimeConstants.ITEM_Integer;
    2.32 +import static org.apidesign.javap.RuntimeConstants.ITEM_Float;
    2.33 +import static org.apidesign.javap.RuntimeConstants.ITEM_Double;
    2.34 +import static org.apidesign.javap.RuntimeConstants.ITEM_Long;
    2.35 +import static org.apidesign.javap.RuntimeConstants.ITEM_Object;
    2.36 +
    2.37 +public final class StackMapIterator {
    2.38 +    private final StackMapTableData[] stackMapTable;
    2.39 +    private final TypeArray argTypes;
    2.40 +    private final TypeArray localTypes;
    2.41 +    private final TypeArray stackTypes;
    2.42 +
    2.43 +    private int nextFrameIndex;
    2.44 +    private int lastFrameByteCodeOffset;
    2.45 +
    2.46 +    private int byteCodeOffset;
    2.47 +
    2.48 +    StackMapIterator(final MethodData methodData) {
    2.49 +        this(methodData.getStackMapTable(),
    2.50 +             methodData.getInternalSig(),
    2.51 +             methodData.isStatic());
    2.52 +    }
    2.53 +
    2.54 +    StackMapIterator(final StackMapTableData[] stackMapTable,
    2.55 +                     final String methodSignature,
    2.56 +                     final boolean isStaticMethod) {
    2.57 +        this.stackMapTable = (stackMapTable != null)
    2.58 +                                 ? stackMapTable
    2.59 +                                 : new StackMapTableData[0];
    2.60 +
    2.61 +        argTypes = getArgTypes(methodSignature, isStaticMethod);
    2.62 +        localTypes = new TypeArray();
    2.63 +        stackTypes = new TypeArray();
    2.64 +
    2.65 +        localTypes.addAll(argTypes);
    2.66 +
    2.67 +        lastFrameByteCodeOffset = -1;
    2.68 +        advanceBy(0);
    2.69 +    }
    2.70 +
    2.71 +    public String getFrameAsString() {
    2.72 +        return (nextFrameIndex == 0)
    2.73 +                   ? StackMapTableData.toString("INITIAL", 0, null, null)
    2.74 +                   : stackMapTable[nextFrameIndex - 1].toString();
    2.75 +    }
    2.76 +
    2.77 +    public int getFrameIndex() {
    2.78 +        return nextFrameIndex;
    2.79 +    }
    2.80 +
    2.81 +    public TypeArray getFrameStack() {
    2.82 +        return stackTypes;
    2.83 +    }
    2.84 +
    2.85 +    public TypeArray getFrameLocals() {
    2.86 +        return localTypes;
    2.87 +    }
    2.88 +
    2.89 +    public TypeArray getArguments() {
    2.90 +        return argTypes;
    2.91 +    }
    2.92 +
    2.93 +    public void advanceBy(final int numByteCodes) {
    2.94 +        if (numByteCodes < 0) {
    2.95 +            throw new IllegalStateException("Forward only iterator");
    2.96 +        }
    2.97 +
    2.98 +        byteCodeOffset += numByteCodes;
    2.99 +        while ((nextFrameIndex < stackMapTable.length)
   2.100 +                    && ((byteCodeOffset - lastFrameByteCodeOffset)
   2.101 +                            >= (stackMapTable[nextFrameIndex].offsetDelta
   2.102 +                                    + 1))) {
   2.103 +            final StackMapTableData nextFrame = stackMapTable[nextFrameIndex];
   2.104 +
   2.105 +            lastFrameByteCodeOffset += nextFrame.offsetDelta + 1;
   2.106 +            nextFrame.applyTo(localTypes, stackTypes);
   2.107 +
   2.108 +            ++nextFrameIndex;
   2.109 +        }
   2.110 +    }
   2.111 +
   2.112 +    public void advanceTo(final int nextByteCodeOffset) {
   2.113 +        advanceBy(nextByteCodeOffset - byteCodeOffset);
   2.114 +    }
   2.115 +
   2.116 +    private static TypeArray getArgTypes(final String methodSignature,
   2.117 +                                         final boolean isStaticMethod) {
   2.118 +        final TypeArray argTypes = new TypeArray();
   2.119 +
   2.120 +        if (!isStaticMethod) {
   2.121 +            argTypes.add(ITEM_Object);
   2.122 +        }
   2.123 +
   2.124 +        if (methodSignature.charAt(0) != '(') {
   2.125 +            throw new IllegalArgumentException("Invalid method signature");
   2.126 +        }
   2.127 +
   2.128 +        final int length = methodSignature.length();
   2.129 +        int skipType = 0;
   2.130 +        int argType;
   2.131 +        for (int i = 1; i < length; ++i) {
   2.132 +            switch (methodSignature.charAt(i)) {
   2.133 +                case 'B':
   2.134 +                case 'C':
   2.135 +                case 'S':
   2.136 +                case 'Z':
   2.137 +                case 'I':
   2.138 +                    argType = ITEM_Integer;
   2.139 +                    break;
   2.140 +                case 'J':
   2.141 +                    argType = ITEM_Long;
   2.142 +                    break;
   2.143 +                case 'F':
   2.144 +                    argType = ITEM_Float;
   2.145 +                    break;
   2.146 +                case 'D':
   2.147 +                    argType = ITEM_Double;
   2.148 +                    break;
   2.149 +                case 'L': {
   2.150 +                    i = methodSignature.indexOf(';', i + 1);
   2.151 +                    if (i == -1) {
   2.152 +                        throw new IllegalArgumentException(
   2.153 +                                      "Invalid method signature");
   2.154 +                    }
   2.155 +                    argType = ITEM_Object;
   2.156 +                    break;
   2.157 +                }
   2.158 +                case ')':
   2.159 +                    // not interested in the return value type
   2.160 +                    return argTypes;
   2.161 +                case '[':
   2.162 +                    if (skipType == 0) {
   2.163 +                        argTypes.add(ITEM_Object);
   2.164 +                    }
   2.165 +                    ++skipType;
   2.166 +                    continue;
   2.167 +
   2.168 +                default:
   2.169 +                    throw new IllegalArgumentException(
   2.170 +                                  "Invalid method signature");
   2.171 +            }
   2.172 +
   2.173 +            if (skipType == 0) {
   2.174 +                argTypes.add(argType);
   2.175 +            } else {
   2.176 +                --skipType;
   2.177 +            }
   2.178 +        }
   2.179 +
   2.180 +        return argTypes;
   2.181 +    }
   2.182 +}
     3.1 --- a/javap/src/main/java/org/apidesign/javap/StackMapTableData.java	Thu Dec 13 23:20:47 2012 +0100
     3.2 +++ b/javap/src/main/java/org/apidesign/javap/StackMapTableData.java	Fri Dec 14 11:32:45 2012 +0100
     3.3 @@ -32,7 +32,7 @@
     3.4  
     3.5  /* represents one entry of StackMapTable attribute
     3.6   */
     3.7 -class StackMapTableData {
     3.8 +abstract class StackMapTableData {
     3.9      final int frameType;
    3.10      int offsetDelta;
    3.11  
    3.12 @@ -40,11 +40,59 @@
    3.13          this.frameType = frameType;
    3.14      }
    3.15  
    3.16 +    abstract void applyTo(TypeArray localTypes, TypeArray stackTypes);
    3.17 +
    3.18 +    protected static String toString(
    3.19 +            final String frameType,
    3.20 +            final int offset,
    3.21 +            final int[] localTypes,
    3.22 +            final int[] stackTypes) {
    3.23 +        final StringBuilder sb = new StringBuilder(frameType);
    3.24 +
    3.25 +        sb.append("(off: +").append(offset);
    3.26 +        if (localTypes != null) {
    3.27 +            sb.append(", locals: ");
    3.28 +            appendTypes(sb, localTypes);
    3.29 +        }
    3.30 +        if (stackTypes != null) {
    3.31 +            sb.append(", stack: ");
    3.32 +            appendTypes(sb, stackTypes);
    3.33 +        }
    3.34 +        sb.append(')');
    3.35 +
    3.36 +        return sb.toString();
    3.37 +    }
    3.38 +
    3.39 +    private static void appendTypes(final StringBuilder sb, final int[] types) {
    3.40 +        sb.append('[');
    3.41 +        if (types.length > 0) {
    3.42 +            sb.append(TypeArray.typeString(types[0]));
    3.43 +            for (int i = 1; i < types.length; ++i) {
    3.44 +                sb.append(", ");
    3.45 +                sb.append(TypeArray.typeString(types[i]));
    3.46 +            }
    3.47 +        }
    3.48 +        sb.append(']');
    3.49 +    }
    3.50 +
    3.51      static class SameFrame extends StackMapTableData {
    3.52          SameFrame(int frameType, int offsetDelta) {
    3.53              super(frameType);
    3.54              this.offsetDelta = offsetDelta;
    3.55          }
    3.56 +
    3.57 +        @Override
    3.58 +        void applyTo(TypeArray localTypes, TypeArray stackTypes) {
    3.59 +            stackTypes.clear();
    3.60 +        }
    3.61 +
    3.62 +        @Override
    3.63 +        public String toString() {
    3.64 +            return toString("SAME" + ((frameType == SAME_FRAME_EXTENDED)
    3.65 +                                          ? "_FRAME_EXTENDED" : ""),
    3.66 +                            offsetDelta,
    3.67 +                            null, null);
    3.68 +        }
    3.69      }
    3.70  
    3.71      static class SameLocals1StackItem extends StackMapTableData {
    3.72 @@ -54,6 +102,21 @@
    3.73              this.offsetDelta = offsetDelta;
    3.74              this.stack = stack;
    3.75          }
    3.76 +
    3.77 +        @Override
    3.78 +        void applyTo(TypeArray localTypes, TypeArray stackTypes) {
    3.79 +            stackTypes.setAll(stack);
    3.80 +        }
    3.81 +
    3.82 +        @Override
    3.83 +        public String toString() {
    3.84 +            return toString(
    3.85 +                       "SAME_LOCALS_1_STACK_ITEM"
    3.86 +                           + ((frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED)
    3.87 +                                  ? "_EXTENDED" : ""),
    3.88 +                       offsetDelta,
    3.89 +                       null, stack);
    3.90 +        }
    3.91      }
    3.92  
    3.93      static class ChopFrame extends StackMapTableData {
    3.94 @@ -61,6 +124,18 @@
    3.95              super(frameType);
    3.96              this.offsetDelta = offsetDelta;
    3.97          }
    3.98 +
    3.99 +        @Override
   3.100 +        void applyTo(TypeArray localTypes, TypeArray stackTypes) {
   3.101 +            localTypes.setSize(localTypes.getSize()
   3.102 +                                   - (SAME_FRAME_EXTENDED - frameType));
   3.103 +            stackTypes.clear();
   3.104 +        }
   3.105 +
   3.106 +        @Override
   3.107 +        public String toString() {
   3.108 +            return toString("CHOP", offsetDelta, null, null);
   3.109 +        }
   3.110      }
   3.111  
   3.112      static class AppendFrame extends StackMapTableData {
   3.113 @@ -70,6 +145,17 @@
   3.114              this.offsetDelta = offsetDelta;
   3.115              this.locals = locals;
   3.116          }
   3.117 +
   3.118 +        @Override
   3.119 +        void applyTo(TypeArray localTypes, TypeArray stackTypes) {
   3.120 +            localTypes.addAll(locals);
   3.121 +            stackTypes.clear();
   3.122 +        }
   3.123 +
   3.124 +        @Override
   3.125 +        public String toString() {
   3.126 +            return toString("APPEND", offsetDelta, locals, null);
   3.127 +        }
   3.128      }
   3.129  
   3.130      static class FullFrame extends StackMapTableData {
   3.131 @@ -81,6 +167,17 @@
   3.132              this.locals = locals;
   3.133              this.stack = stack;
   3.134          }
   3.135 +
   3.136 +        @Override
   3.137 +        void applyTo(TypeArray localTypes, TypeArray stackTypes) {
   3.138 +            localTypes.setAll(locals);
   3.139 +            stackTypes.setAll(stack);
   3.140 +        }
   3.141 +
   3.142 +        @Override
   3.143 +        public String toString() {
   3.144 +            return toString("FULL", offsetDelta, locals, stack);
   3.145 +        }
   3.146      }
   3.147  
   3.148      static StackMapTableData getInstance(DataInputStream in, MethodData method)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/javap/src/main/java/org/apidesign/javap/TypeArray.java	Fri Dec 14 11:32:45 2012 +0100
     4.3 @@ -0,0 +1,186 @@
     4.4 +/*
     4.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.7 + *
     4.8 + * This code is free software; you can redistribute it and/or modify it
     4.9 + * under the terms of the GNU General Public License version 2 only, as
    4.10 + * published by the Free Software Foundation.  Oracle designates this
    4.11 + * particular file as subject to the "Classpath" exception as provided
    4.12 + * by Oracle in the LICENSE file that accompanied this code.
    4.13 + *
    4.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    4.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    4.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    4.17 + * version 2 for more details (a copy is included in the LICENSE file that
    4.18 + * accompanied this code).
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License version
    4.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    4.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    4.23 + *
    4.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    4.25 + * or visit www.oracle.com if you need additional information or have any
    4.26 + * questions.
    4.27 + */
    4.28 +
    4.29 +package org.apidesign.javap;
    4.30 +
    4.31 +import static org.apidesign.javap.RuntimeConstants.ITEM_Bogus;
    4.32 +import static org.apidesign.javap.RuntimeConstants.ITEM_Integer;
    4.33 +import static org.apidesign.javap.RuntimeConstants.ITEM_Float;
    4.34 +import static org.apidesign.javap.RuntimeConstants.ITEM_Double;
    4.35 +import static org.apidesign.javap.RuntimeConstants.ITEM_Long;
    4.36 +import static org.apidesign.javap.RuntimeConstants.ITEM_Null;
    4.37 +import static org.apidesign.javap.RuntimeConstants.ITEM_InitObject;
    4.38 +import static org.apidesign.javap.RuntimeConstants.ITEM_Object;
    4.39 +import static org.apidesign.javap.RuntimeConstants.ITEM_NewObject;
    4.40 +
    4.41 +public final class TypeArray {
    4.42 +    private static final int CAPACITY_INCREMENT = 16;
    4.43 +
    4.44 +    private int[] types;
    4.45 +    private int size;
    4.46 +
    4.47 +    public TypeArray() {
    4.48 +    }
    4.49 +    
    4.50 +    public TypeArray(final TypeArray initialTypes) {
    4.51 +        setAll(initialTypes);
    4.52 +    }
    4.53 +
    4.54 +    public void add(final int newType) {
    4.55 +        ensureCapacity(size + 1);
    4.56 +        types[size++] = newType;
    4.57 +    }
    4.58 +
    4.59 +    public void addAll(final TypeArray newTypes) {
    4.60 +        addAll(newTypes.types, 0, newTypes.size);
    4.61 +    }
    4.62 +
    4.63 +    public void addAll(final int[] newTypes) {
    4.64 +        addAll(newTypes, 0, newTypes.length);
    4.65 +    }
    4.66 +
    4.67 +    public void addAll(final int[] newTypes,
    4.68 +                       final int offset,
    4.69 +                       final int count) {
    4.70 +        if (count > 0) {
    4.71 +            ensureCapacity(size + count);
    4.72 +            arraycopy(newTypes, offset, types, size, count);
    4.73 +            size += count;
    4.74 +        }
    4.75 +    }
    4.76 +
    4.77 +    public void set(final int index, final int newType) {
    4.78 +        types[index] = newType;
    4.79 +    }
    4.80 +
    4.81 +    public void setAll(final TypeArray newTypes) {
    4.82 +        setAll(newTypes.types, 0, newTypes.size);
    4.83 +    }
    4.84 +
    4.85 +    public void setAll(final int[] newTypes) {
    4.86 +        setAll(newTypes, 0, newTypes.length);
    4.87 +    }
    4.88 +
    4.89 +    public void setAll(final int[] newTypes,
    4.90 +                       final int offset,
    4.91 +                       final int count) {
    4.92 +        if (count > 0) {
    4.93 +            ensureCapacity(count);
    4.94 +            arraycopy(newTypes, offset, types, 0, count);
    4.95 +            size = count;
    4.96 +        } else {
    4.97 +            clear();
    4.98 +        }
    4.99 +    }
   4.100 +
   4.101 +    public void setSize(final int newSize) {
   4.102 +        if (size != newSize) {
   4.103 +            ensureCapacity(newSize);
   4.104 +
   4.105 +            for (int i = size; i < newSize; ++i) {
   4.106 +                types[i] = 0;
   4.107 +            }
   4.108 +            size = newSize;
   4.109 +        }
   4.110 +    }
   4.111 +
   4.112 +    public void clear() {
   4.113 +        size = 0;
   4.114 +    }
   4.115 +
   4.116 +    public int getSize() {
   4.117 +        return size;
   4.118 +    }
   4.119 +
   4.120 +    public int get(final int index) {
   4.121 +        return types[index];
   4.122 +    }
   4.123 +
   4.124 +    public static String typeString(final int type) {
   4.125 +        switch (type & 0xff) {
   4.126 +            case ITEM_Bogus:
   4.127 +                return "_top_";
   4.128 +            case ITEM_Integer:
   4.129 +                return "_int_";
   4.130 +            case ITEM_Float:
   4.131 +                return "_float_";
   4.132 +            case ITEM_Double:
   4.133 +                return "_double_";
   4.134 +            case ITEM_Long:
   4.135 +                return "_long_";
   4.136 +            case ITEM_Null:
   4.137 +                return "_null_";
   4.138 +            case ITEM_InitObject: // UninitializedThis
   4.139 +                return "_init_";
   4.140 +            case ITEM_Object:
   4.141 +                return "_object_";
   4.142 +            case ITEM_NewObject: // Uninitialized
   4.143 +                return "_new_";
   4.144 +            default:
   4.145 +                throw new IllegalArgumentException("Unknown type");
   4.146 +        }
   4.147 +    }
   4.148 +
   4.149 +    @Override
   4.150 +    public String toString() {
   4.151 +        final StringBuilder sb = new StringBuilder("[");
   4.152 +        if (size > 0) {
   4.153 +            sb.append(typeString(types[0]));
   4.154 +            for (int i = 1; i < size; ++i) {
   4.155 +                sb.append(", ");
   4.156 +                sb.append(typeString(types[i]));
   4.157 +            }
   4.158 +        }
   4.159 +
   4.160 +        return sb.append(']').toString();
   4.161 +    }
   4.162 +
   4.163 +    private void ensureCapacity(final int minCapacity) {
   4.164 +        if ((minCapacity == 0)
   4.165 +                || (types != null) && (minCapacity <= types.length)) {
   4.166 +            return;
   4.167 +        }
   4.168 +
   4.169 +        final int newCapacity =
   4.170 +                ((minCapacity + CAPACITY_INCREMENT - 1) / CAPACITY_INCREMENT)
   4.171 +                    * CAPACITY_INCREMENT;
   4.172 +        final int[] newTypes = new int[newCapacity];
   4.173 +
   4.174 +        if (size > 0) {
   4.175 +            arraycopy(types, 0, newTypes, 0, size);
   4.176 +        }
   4.177 +
   4.178 +        types = newTypes;
   4.179 +    }
   4.180 +
   4.181 +    // no System.arraycopy
   4.182 +    private void arraycopy(final int[] src, final int srcPos,
   4.183 +                           final int[] dest, final int destPos,
   4.184 +                           final int length) {
   4.185 +        for (int i = 0; i < length; ++i) {
   4.186 +            dest[destPos + i] = src[srcPos + i];
   4.187 +        }
   4.188 +    }
   4.189 +}
     5.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Dec 13 23:20:47 2012 +0100
     5.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Fri Dec 14 11:32:45 2012 +0100
     5.3 @@ -24,6 +24,7 @@
     5.4  import org.apidesign.javap.ClassData;
     5.5  import org.apidesign.javap.FieldData;
     5.6  import org.apidesign.javap.MethodData;
     5.7 +import org.apidesign.javap.StackMapIterator;
     5.8  import static org.apidesign.javap.RuntimeConstants.*;
     5.9  
    5.10  /** Translator of the code inside class files to JavaScript.
    5.11 @@ -195,257 +196,438 @@
    5.12          if (jsb != null) {
    5.13              return jsb;
    5.14          }
    5.15 -        StringBuilder argsCnt = new StringBuilder();
    5.16 -        final String mn = findMethodName(m, argsCnt);
    5.17 -        out.append(prefix).append(mn).append(" = function");
    5.18 +        final String mn = findMethodName(m, new StringBuilder());
    5.19          if (mn.equals("class__V")) {
    5.20              toInitilize.add(accessClass(className(jc)) + "(false)." + mn);
    5.21          }
    5.22 -        out.append('(');
    5.23 -        String space = "";
    5.24 -        for (int index = 0, i = 0; i < argsCnt.length(); i++) {
    5.25 -            out.append(space);
    5.26 -            out.append("arg").append(String.valueOf(index));
    5.27 -            space = ",";
    5.28 -            final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor());
    5.29 -            if (argsCnt.charAt(i) == '1') {
    5.30 -                index += 2;
    5.31 -            } else {
    5.32 -                index++;
    5.33 -            }
    5.34 -        }
    5.35 -        out.append(") {").append("\n");
    5.36 -        final byte[] code = m.getCode();
    5.37 -        if (code != null) {
    5.38 -            int len = m.getMaxLocals();
    5.39 -            for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) {
    5.40 -                out.append("  var ");
    5.41 -                out.append("arg").append(String.valueOf(i)).append(";\n");
    5.42 -            }
    5.43 -            out.append("  var s = new Array();\n");
    5.44 -            produceCode(code);
    5.45 -        } else {
    5.46 -            out.append("  throw 'no code found for ").append(m.getInternalSig()).append("';\n");
    5.47 -        }
    5.48 -        out.append("};");
    5.49 +        generateMethod(prefix, mn, m);
    5.50          return mn;
    5.51      }
    5.52 -    
    5.53 +
    5.54      private String generateInstanceMethod(String prefix, MethodData m) throws IOException {
    5.55          String jsb = javaScriptBody(prefix, m, false);
    5.56          if (jsb != null) {
    5.57              return jsb;
    5.58          }
    5.59 -        StringBuilder argsCnt = new StringBuilder();
    5.60 -        final String mn = findMethodName(m, argsCnt);
    5.61 -        out.append(prefix).append(mn).append(" = function");
    5.62 -        out.append("(arg0");
    5.63 -        String space = ",";
    5.64 -        for (int index = 1, i = 0; i < argsCnt.length(); i++) {
    5.65 -            out.append(space);
    5.66 -            out.append("arg").append(String.valueOf(index));
    5.67 -            if (argsCnt.charAt(i) == '1') {
    5.68 -                index += 2;
    5.69 -            } else {
    5.70 -                index++;
    5.71 -            }
    5.72 -        }
    5.73 -        out.append(") {").append("\n");
    5.74 -        final byte[] code = m.getCode();
    5.75 -        if (code != null) {
    5.76 -            int len = m.getMaxLocals();
    5.77 -            for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) {
    5.78 -                out.append("  var ");
    5.79 -                out.append("arg").append(String.valueOf(i + 1)).append(";\n");
    5.80 -            }
    5.81 -            out.append(";\n  var s = new Array();\n");
    5.82 -            produceCode(code);
    5.83 -        } else {
    5.84 -            out.append("  throw 'no code found for ").append(m.getInternalSig()).append("';\n");
    5.85 -        }
    5.86 -        out.append("};");
    5.87 +        final String mn = findMethodName(m, new StringBuilder());
    5.88 +        generateMethod(prefix, mn, m);
    5.89          return mn;
    5.90      }
    5.91  
    5.92 -    private void produceCode(byte[] byteCodes) throws IOException {
    5.93 +    private void generateMethod(String prefix, String name, MethodData m)
    5.94 +            throws IOException {
    5.95 +        final StackMapIterator stackMapIterator = m.createStackMapIterator();
    5.96 +        final LocalsMapper lmapper =
    5.97 +                new LocalsMapper(stackMapIterator.getArguments());
    5.98 +
    5.99 +        out.append(prefix).append(name).append(" = function(");
   5.100 +        lmapper.outputArguments(out);
   5.101 +        out.append(") {").append("\n");
   5.102 +
   5.103 +        final byte[] byteCodes = m.getCode();
   5.104 +        if (byteCodes == null) {
   5.105 +            out.append("  throw 'no code found for ")
   5.106 +               .append(m.getInternalSig()).append("';\n");
   5.107 +            out.append("};");
   5.108 +            return;
   5.109 +        }
   5.110 +
   5.111 +        final StackMapper smapper = new StackMapper();
   5.112 +
   5.113 +        final int maxLocals = m.getMaxLocals();
   5.114 +        if (maxLocals > 0) {
   5.115 +            // TODO: generate only used local variables
   5.116 +            for (int j = 0; j <= VarType.LAST; ++j) {
   5.117 +                out.append("\n  var ").append(Variable.getLocalVariable(j, 0));
   5.118 +                for (int i = 1; i < maxLocals; ++i) {
   5.119 +                    out.append(", ");
   5.120 +                    out.append(Variable.getLocalVariable(j, i));
   5.121 +                }
   5.122 +                out.append(';');
   5.123 +            }
   5.124 +        }
   5.125 +
   5.126 +        // maxStack includes two stack positions for every pushed long / double
   5.127 +        // so this might generate more stack variables than we need
   5.128 +        final int maxStack = m.getMaxStack();
   5.129 +        if (maxStack > 0) {
   5.130 +            // TODO: generate only used stack variables
   5.131 +            for (int j = 0; j <= VarType.LAST; ++j) {
   5.132 +                out.append("\n  var ").append(Variable.getStackVariable(j, 0));
   5.133 +                for (int i = 1; i < maxStack; ++i) {
   5.134 +                    out.append(", ");
   5.135 +                    out.append(Variable.getStackVariable(j, i));
   5.136 +                }
   5.137 +                out.append(';');
   5.138 +            }
   5.139 +        }
   5.140 +
   5.141 +        int lastStackFrame = -1;
   5.142 +
   5.143          out.append("\n  var gt = 0;\n  for(;;) switch(gt) {\n");
   5.144          for (int i = 0; i < byteCodes.length; i++) {
   5.145              int prev = i;
   5.146 -            out.append("    case " + i).append(": ");
   5.147 +            stackMapIterator.advanceTo(i);
   5.148 +            if (lastStackFrame != stackMapIterator.getFrameIndex()) {
   5.149 +                lastStackFrame = stackMapIterator.getFrameIndex();
   5.150 +                lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals());
   5.151 +                smapper.syncWithFrameStack(stackMapIterator.getFrameStack());
   5.152 +                out.append("    case " + i).append(": ");
   5.153 +            } else {
   5.154 +                out.append("    /* " + i).append(" */ ");
   5.155 +            }
   5.156              final int c = readByte(byteCodes, i);
   5.157              switch (c) {
   5.158                  case opc_aload_0:
   5.159 +                    emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(0));
   5.160 +                    break;
   5.161                  case opc_iload_0:
   5.162 +                    emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(0));
   5.163 +                    break;
   5.164                  case opc_lload_0:
   5.165 +                    emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(0));
   5.166 +                    break;
   5.167                  case opc_fload_0:
   5.168 +                    emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(0));
   5.169 +                    break;
   5.170                  case opc_dload_0:
   5.171 -                    out.append("s.push(arg0);");
   5.172 +                    emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(0));
   5.173                      break;
   5.174                  case opc_aload_1:
   5.175 +                    emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(1));
   5.176 +                    break;
   5.177                  case opc_iload_1:
   5.178 +                    emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(1));
   5.179 +                    break;
   5.180                  case opc_lload_1:
   5.181 +                    emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(1));
   5.182 +                    break;
   5.183                  case opc_fload_1:
   5.184 +                    emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(1));
   5.185 +                    break;
   5.186                  case opc_dload_1:
   5.187 -                    out.append("s.push(arg1);");
   5.188 +                    emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(1));
   5.189                      break;
   5.190                  case opc_aload_2:
   5.191 +                    emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(2));
   5.192 +                    break;
   5.193                  case opc_iload_2:
   5.194 +                    emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(2));
   5.195 +                    break;
   5.196                  case opc_lload_2:
   5.197 +                    emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(2));
   5.198 +                    break;
   5.199                  case opc_fload_2:
   5.200 +                    emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(2));
   5.201 +                    break;
   5.202                  case opc_dload_2:
   5.203 -                    out.append("s.push(arg2);");
   5.204 +                    emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(2));
   5.205                      break;
   5.206                  case opc_aload_3:
   5.207 +                    emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(3));
   5.208 +                    break;
   5.209                  case opc_iload_3:
   5.210 +                    emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(3));
   5.211 +                    break;
   5.212                  case opc_lload_3:
   5.213 +                    emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(3));
   5.214 +                    break;
   5.215                  case opc_fload_3:
   5.216 +                    emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(3));
   5.217 +                    break;
   5.218                  case opc_dload_3:
   5.219 -                    out.append("s.push(arg3);");
   5.220 +                    emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(3));
   5.221                      break;
   5.222 -                case opc_iload:
   5.223 -                case opc_lload:
   5.224 -                case opc_fload:
   5.225 -                case opc_dload:
   5.226 +                case opc_iload: {
   5.227 +                    final int indx = readByte(byteCodes, ++i);
   5.228 +                    emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(indx));
   5.229 +                    break;
   5.230 +                }
   5.231 +                case opc_lload: {
   5.232 +                    final int indx = readByte(byteCodes, ++i);
   5.233 +                    emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(indx));
   5.234 +                    break;
   5.235 +                }
   5.236 +                case opc_fload: {
   5.237 +                    final int indx = readByte(byteCodes, ++i);
   5.238 +                    emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(indx));
   5.239 +                    break;
   5.240 +                }
   5.241 +                case opc_dload: {
   5.242 +                    final int indx = readByte(byteCodes, ++i);
   5.243 +                    emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(indx));
   5.244 +                    break;
   5.245 +                }
   5.246                  case opc_aload: {
   5.247                      final int indx = readByte(byteCodes, ++i);
   5.248 -                    out.append("s.push(arg").append(indx + ");");
   5.249 +                    emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(indx));
   5.250                      break;
   5.251                  }
   5.252 -                case opc_istore:
   5.253 -                case opc_lstore:
   5.254 -                case opc_fstore:
   5.255 -                case opc_dstore:
   5.256 +                case opc_istore: {
   5.257 +                    final int indx = readByte(byteCodes, ++i);
   5.258 +                    emit(out, "@1 = @2;", lmapper.setI(indx), smapper.popI());
   5.259 +                    break;
   5.260 +                }
   5.261 +                case opc_lstore: {
   5.262 +                    final int indx = readByte(byteCodes, ++i);
   5.263 +                    emit(out, "@1 = @2;", lmapper.setL(indx), smapper.popL());
   5.264 +                    break;
   5.265 +                }
   5.266 +                case opc_fstore: {
   5.267 +                    final int indx = readByte(byteCodes, ++i);
   5.268 +                    emit(out, "@1 = @2;", lmapper.setF(indx), smapper.popF());
   5.269 +                    break;
   5.270 +                }
   5.271 +                case opc_dstore: {
   5.272 +                    final int indx = readByte(byteCodes, ++i);
   5.273 +                    emit(out, "@1 = @2;", lmapper.setD(indx), smapper.popD());
   5.274 +                    break;
   5.275 +                }
   5.276                  case opc_astore: {
   5.277                      final int indx = readByte(byteCodes, ++i);
   5.278 -                    out.append("arg" + indx).append(" = s.pop();");
   5.279 +                    emit(out, "@1 = @2;", lmapper.setA(indx), smapper.popA());
   5.280                      break;
   5.281                  }
   5.282                  case opc_astore_0:
   5.283 +                    emit(out, "@1 = @2;", lmapper.setA(0), smapper.popA());
   5.284 +                    break;
   5.285                  case opc_istore_0:
   5.286 +                    emit(out, "@1 = @2;", lmapper.setI(0), smapper.popI());
   5.287 +                    break;
   5.288                  case opc_lstore_0:
   5.289 +                    emit(out, "@1 = @2;", lmapper.setL(0), smapper.popL());
   5.290 +                    break;
   5.291                  case opc_fstore_0:
   5.292 +                    emit(out, "@1 = @2;", lmapper.setF(0), smapper.popF());
   5.293 +                    break;
   5.294                  case opc_dstore_0:
   5.295 -                    out.append("arg0 = s.pop();");
   5.296 +                    emit(out, "@1 = @2;", lmapper.setD(0), smapper.popD());
   5.297                      break;
   5.298                  case opc_astore_1:
   5.299 +                    emit(out, "@1 = @2;", lmapper.setA(1), smapper.popA());
   5.300 +                    break;
   5.301                  case opc_istore_1:
   5.302 +                    emit(out, "@1 = @2;", lmapper.setI(1), smapper.popI());
   5.303 +                    break;
   5.304                  case opc_lstore_1:
   5.305 +                    emit(out, "@1 = @2;", lmapper.setL(1), smapper.popL());
   5.306 +                    break;
   5.307                  case opc_fstore_1:
   5.308 +                    emit(out, "@1 = @2;", lmapper.setF(1), smapper.popF());
   5.309 +                    break;
   5.310                  case opc_dstore_1:
   5.311 -                    out.append("arg1 = s.pop();");
   5.312 +                    emit(out, "@1 = @2;", lmapper.setD(1), smapper.popD());
   5.313                      break;
   5.314                  case opc_astore_2:
   5.315 +                    emit(out, "@1 = @2;", lmapper.setA(2), smapper.popA());
   5.316 +                    break;
   5.317                  case opc_istore_2:
   5.318 +                    emit(out, "@1 = @2;", lmapper.setI(2), smapper.popI());
   5.319 +                    break;
   5.320                  case opc_lstore_2:
   5.321 +                    emit(out, "@1 = @2;", lmapper.setL(2), smapper.popL());
   5.322 +                    break;
   5.323                  case opc_fstore_2:
   5.324 +                    emit(out, "@1 = @2;", lmapper.setF(2), smapper.popF());
   5.325 +                    break;
   5.326                  case opc_dstore_2:
   5.327 -                    out.append("arg2 = s.pop();");
   5.328 +                    emit(out, "@1 = @2;", lmapper.setD(2), smapper.popD());
   5.329                      break;
   5.330                  case opc_astore_3:
   5.331 +                    emit(out, "@1 = @2;", lmapper.setA(3), smapper.popA());
   5.332 +                    break;
   5.333                  case opc_istore_3:
   5.334 +                    emit(out, "@1 = @2;", lmapper.setI(3), smapper.popI());
   5.335 +                    break;
   5.336                  case opc_lstore_3:
   5.337 +                    emit(out, "@1 = @2;", lmapper.setL(3), smapper.popL());
   5.338 +                    break;
   5.339                  case opc_fstore_3:
   5.340 +                    emit(out, "@1 = @2;", lmapper.setF(3), smapper.popF());
   5.341 +                    break;
   5.342                  case opc_dstore_3:
   5.343 -                    out.append("arg3 = s.pop();");
   5.344 +                    emit(out, "@1 = @2;", lmapper.setD(3), smapper.popD());
   5.345                      break;
   5.346                  case opc_iadd:
   5.347 +                    emit(out, "@1 += @2;", smapper.getI(1), smapper.popI());
   5.348 +                    break;
   5.349                  case opc_ladd:
   5.350 +                    emit(out, "@1 += @2;", smapper.getL(1), smapper.popL());
   5.351 +                    break;
   5.352                  case opc_fadd:
   5.353 +                    emit(out, "@1 += @2;", smapper.getF(1), smapper.popF());
   5.354 +                    break;
   5.355                  case opc_dadd:
   5.356 -                    out.append("s.push(s.pop() + s.pop());");
   5.357 +                    emit(out, "@1 += @2;", smapper.getD(1), smapper.popD());
   5.358                      break;
   5.359                  case opc_isub:
   5.360 +                    emit(out, "@1 -= @2;", smapper.getI(1), smapper.popI());
   5.361 +                    break;
   5.362                  case opc_lsub:
   5.363 +                    emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL());
   5.364 +                    break;
   5.365                  case opc_fsub:
   5.366 +                    emit(out, "@1 -= @2;", smapper.getF(1), smapper.popF());
   5.367 +                    break;
   5.368                  case opc_dsub:
   5.369 -                    out.append("{ var tmp = s.pop(); s.push(s.pop() - tmp); }");
   5.370 +                    emit(out, "@1 -= @2;", smapper.getD(1), smapper.popD());
   5.371                      break;
   5.372                  case opc_imul:
   5.373 +                    emit(out, "@1 *= @2;", smapper.getI(1), smapper.popI());
   5.374 +                    break;
   5.375                  case opc_lmul:
   5.376 +                    emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL());
   5.377 +                    break;
   5.378                  case opc_fmul:
   5.379 +                    emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF());
   5.380 +                    break;
   5.381                  case opc_dmul:
   5.382 -                    out.append("s.push(s.pop() * s.pop());");
   5.383 +                    emit(out, "@1 *= @2;", smapper.getD(1), smapper.popD());
   5.384                      break;
   5.385                  case opc_idiv:
   5.386 +                    emit(out, "@1 = Math.floor(@1 / @2);",
   5.387 +                         smapper.getI(1), smapper.popI());
   5.388 +                    break;
   5.389                  case opc_ldiv:
   5.390 -                    out.append("{ var tmp = s.pop(); s.push(Math.floor(s.pop() / tmp)); }");
   5.391 +                    emit(out, "@1 = Math.floor(@1 / @2);",
   5.392 +                         smapper.getL(1), smapper.popL());
   5.393                      break;
   5.394                  case opc_fdiv:
   5.395 +                    emit(out, "@1 /= @2;", smapper.getF(1), smapper.popF());
   5.396 +                    break;
   5.397                  case opc_ddiv:
   5.398 -                    out.append("{ var tmp = s.pop(); s.push(s.pop() / tmp); }");
   5.399 +                    emit(out, "@1 /= @2;", smapper.getD(1), smapper.popD());
   5.400                      break;
   5.401                  case opc_irem:
   5.402 +                    emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI());
   5.403 +                    break;
   5.404                  case opc_lrem:
   5.405 +                    emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL());
   5.406 +                    break;
   5.407                  case opc_frem:
   5.408 +                    emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF());
   5.409 +                    break;
   5.410                  case opc_drem:
   5.411 -                    out.append("{ var d = s.pop(); s.push(s.pop() % d); }");
   5.412 +                    emit(out, "@1 %= @2;", smapper.getD(1), smapper.popD());
   5.413                      break;
   5.414                  case opc_iand:
   5.415 +                    emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI());
   5.416 +                    break;
   5.417                  case opc_land:
   5.418 -                    out.append("s.push(s.pop() & s.pop());");
   5.419 +                    emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL());
   5.420                      break;
   5.421                  case opc_ior:
   5.422 +                    emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI());
   5.423 +                    break;
   5.424                  case opc_lor:
   5.425 -                    out.append("s.push(s.pop() | s.pop());");
   5.426 +                    emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL());
   5.427                      break;
   5.428                  case opc_ixor:
   5.429 +                    emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI());
   5.430 +                    break;
   5.431                  case opc_lxor:
   5.432 -                    out.append("s.push(s.pop() ^ s.pop());");
   5.433 +                    emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL());
   5.434                      break;
   5.435                  case opc_ineg:
   5.436 +                    emit(out, "@1 = -@1;", smapper.getI(0));
   5.437 +                    break;
   5.438                  case opc_lneg:
   5.439 +                    emit(out, "@1 = -@1;", smapper.getL(0));
   5.440 +                    break;
   5.441                  case opc_fneg:
   5.442 +                    emit(out, "@1 = -@1;", smapper.getF(0));
   5.443 +                    break;
   5.444                  case opc_dneg:
   5.445 -                    out.append("s.push(- s.pop());");
   5.446 +                    emit(out, "@1 = -@1;", smapper.getD(0));
   5.447                      break;
   5.448                  case opc_ishl:
   5.449 +                    emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI());
   5.450 +                    break;
   5.451                  case opc_lshl:
   5.452 -                    out.append("{ var v = s.pop(); s.push(s.pop() << v); }");
   5.453 +                    emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI());
   5.454                      break;
   5.455                  case opc_ishr:
   5.456 +                    emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI());
   5.457 +                    break;
   5.458                  case opc_lshr:
   5.459 -                    out.append("{ var v = s.pop(); s.push(s.pop() >> v); }");
   5.460 +                    emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI());
   5.461                      break;
   5.462                  case opc_iushr:
   5.463 +                    emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI());
   5.464 +                    break;
   5.465                  case opc_lushr:
   5.466 -                    out.append("{ var v = s.pop(); s.push(s.pop() >>> v); }");
   5.467 +                    emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI());
   5.468                      break;
   5.469                  case opc_iinc: {
   5.470                      final int varIndx = readByte(byteCodes, ++i);
   5.471                      final int incrBy = byteCodes[++i];
   5.472                      if (incrBy == 1) {
   5.473 -                        out.append("arg" + varIndx).append("++;");
   5.474 +                        emit(out, "@1++;", lmapper.getI(varIndx));
   5.475                      } else {
   5.476 -                        out.append("arg" + varIndx).append(" += " + incrBy).append(";");
   5.477 +                        emit(out, "@1 += @2;",
   5.478 +                             lmapper.getI(varIndx),
   5.479 +                             Integer.toString(incrBy));
   5.480                      }
   5.481                      break;
   5.482                  }
   5.483                  case opc_return:
   5.484 -                    out.append("return;");
   5.485 +                    emit(out, "return;");
   5.486                      break;
   5.487                  case opc_ireturn:
   5.488 +                    emit(out, "return @1;", smapper.popI());
   5.489 +                    break;
   5.490                  case opc_lreturn:
   5.491 +                    emit(out, "return @1;", smapper.popL());
   5.492 +                    break;
   5.493                  case opc_freturn:
   5.494 +                    emit(out, "return @1;", smapper.popF());
   5.495 +                    break;
   5.496                  case opc_dreturn:
   5.497 +                    emit(out, "return @1;", smapper.popD());
   5.498 +                    break;
   5.499                  case opc_areturn:
   5.500 -                    out.append("return s.pop();");
   5.501 +                    emit(out, "return @1;", smapper.popA());
   5.502                      break;
   5.503                  case opc_i2l:
   5.504 +                    emit(out, "@2 = @1;", smapper.popI(), smapper.pushL());
   5.505 +                    break;
   5.506                  case opc_i2f:
   5.507 +                    emit(out, "@2 = @1;", smapper.popI(), smapper.pushF());
   5.508 +                    break;
   5.509                  case opc_i2d:
   5.510 +                    emit(out, "@2 = @1;", smapper.popI(), smapper.pushD());
   5.511 +                    break;
   5.512                  case opc_l2i:
   5.513 +                    emit(out, "@2 = @1;", smapper.popL(), smapper.pushI());
   5.514 +                    break;
   5.515                      // max int check?
   5.516                  case opc_l2f:
   5.517 +                    emit(out, "@2 = @1;", smapper.popL(), smapper.pushF());
   5.518 +                    break;
   5.519                  case opc_l2d:
   5.520 +                    emit(out, "@2 = @1;", smapper.popL(), smapper.pushD());
   5.521 +                    break;
   5.522                  case opc_f2d:
   5.523 +                    emit(out, "@2 = @1;", smapper.popF(), smapper.pushD());
   5.524 +                    break;
   5.525                  case opc_d2f:
   5.526 -                    out.append("/* number conversion */");
   5.527 +                    emit(out, "@2 = @1;", smapper.popD(), smapper.pushF());
   5.528                      break;
   5.529                  case opc_f2i:
   5.530 +                    emit(out, "@2 = Math.floor(@1);",
   5.531 +                         smapper.popF(), smapper.pushI());
   5.532 +                    break;
   5.533                  case opc_f2l:
   5.534 +                    emit(out, "@2 = Math.floor(@1);",
   5.535 +                         smapper.popF(), smapper.pushL());
   5.536 +                    break;
   5.537                  case opc_d2i:
   5.538 +                    emit(out, "@2 = Math.floor(@1);",
   5.539 +                         smapper.popD(), smapper.pushI());
   5.540 +                    break;
   5.541                  case opc_d2l:
   5.542 -                    out.append("s.push(Math.floor(s.pop()));");
   5.543 +                    emit(out, "@2 = Math.floor(@1);",
   5.544 +                         smapper.popD(), smapper.pushL());
   5.545                      break;
   5.546                  case opc_i2b:
   5.547                  case opc_i2c:
   5.548 @@ -453,40 +635,55 @@
   5.549                      out.append("/* number conversion */");
   5.550                      break;
   5.551                  case opc_aconst_null:
   5.552 -                    out.append("s.push(null);");
   5.553 +                    emit(out, "@1 = null;", smapper.pushA());
   5.554                      break;
   5.555                  case opc_iconst_m1:
   5.556 -                    out.append("s.push(-1);");
   5.557 +                    emit(out, "@1 = -1;", smapper.pushI());
   5.558                      break;
   5.559                  case opc_iconst_0:
   5.560 +                    emit(out, "@1 = 0;", smapper.pushI());
   5.561 +                    break;
   5.562                  case opc_dconst_0:
   5.563 +                    emit(out, "@1 = 0;", smapper.pushD());
   5.564 +                    break;
   5.565                  case opc_lconst_0:
   5.566 +                    emit(out, "@1 = 0;", smapper.pushL());
   5.567 +                    break;
   5.568                  case opc_fconst_0:
   5.569 -                    out.append("s.push(0);");
   5.570 +                    emit(out, "@1 = 0;", smapper.pushF());
   5.571                      break;
   5.572                  case opc_iconst_1:
   5.573 +                    emit(out, "@1 = 1;", smapper.pushI());
   5.574 +                    break;
   5.575                  case opc_lconst_1:
   5.576 +                    emit(out, "@1 = 1;", smapper.pushL());
   5.577 +                    break;
   5.578                  case opc_fconst_1:
   5.579 +                    emit(out, "@1 = 1;", smapper.pushF());
   5.580 +                    break;
   5.581                  case opc_dconst_1:
   5.582 -                    out.append("s.push(1);");
   5.583 +                    emit(out, "@1 = 1;", smapper.pushD());
   5.584                      break;
   5.585                  case opc_iconst_2:
   5.586 +                    emit(out, "@1 = 2;", smapper.pushI());
   5.587 +                    break;
   5.588                  case opc_fconst_2:
   5.589 -                    out.append("s.push(2);");
   5.590 +                    emit(out, "@1 = 2;", smapper.pushF());
   5.591                      break;
   5.592                  case opc_iconst_3:
   5.593 -                    out.append("s.push(3);");
   5.594 +                    emit(out, "@1 = 3;", smapper.pushI());
   5.595                      break;
   5.596                  case opc_iconst_4:
   5.597 -                    out.append("s.push(4);");
   5.598 +                    emit(out, "@1 = 4;", smapper.pushI());
   5.599                      break;
   5.600                  case opc_iconst_5:
   5.601 -                    out.append("s.push(5);");
   5.602 +                    emit(out, "@1 = 5;", smapper.pushI());
   5.603                      break;
   5.604                  case opc_ldc: {
   5.605                      int indx = readByte(byteCodes, ++i);
   5.606                      String v = encodeConstant(indx);
   5.607 -                    out.append("s.push(").append(v).append(");");
   5.608 +                    int type = VarType.fromConstantType(jc.getTag(indx));
   5.609 +                    emit(out, "@1 = @2;", smapper.pushT(type), v);
   5.610                      break;
   5.611                  }
   5.612                  case opc_ldc_w:
   5.613 @@ -494,101 +691,115 @@
   5.614                      int indx = readIntArg(byteCodes, i);
   5.615                      i += 2;
   5.616                      String v = encodeConstant(indx);
   5.617 -                    out.append("s.push(").append(v).append(");");
   5.618 +                    int type = VarType.fromConstantType(jc.getTag(indx));
   5.619 +                    emit(out, "@1 = @2;", smapper.pushT(type), v);
   5.620                      break;
   5.621                  }
   5.622                  case opc_lcmp:
   5.623 +                    emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   5.624 +                         smapper.popL(), smapper.popL(), smapper.pushI());
   5.625 +                    break;
   5.626                  case opc_fcmpl:
   5.627                  case opc_fcmpg:
   5.628 +                    emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   5.629 +                         smapper.popF(), smapper.popF(), smapper.pushI());
   5.630 +                    break;
   5.631                  case opc_dcmpl:
   5.632 -                case opc_dcmpg: {
   5.633 -                    out.append("{ var delta = s.pop() - s.pop(); s.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }");
   5.634 +                case opc_dcmpg:
   5.635 +                    emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   5.636 +                         smapper.popD(), smapper.popD(), smapper.pushI());
   5.637                      break;
   5.638 -                }
   5.639                  case opc_if_acmpeq:
   5.640 -                    i = generateIf(byteCodes, i, "===");
   5.641 +                    i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
   5.642 +                                   "===");
   5.643                      break;
   5.644                  case opc_if_acmpne:
   5.645 -                    i = generateIf(byteCodes, i, "!=");
   5.646 +                    i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
   5.647 +                                   "!=");
   5.648                      break;
   5.649 -                case opc_if_icmpeq: {
   5.650 -                    i = generateIf(byteCodes, i, "==");
   5.651 +                case opc_if_icmpeq:
   5.652 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.653 +                                   "==");
   5.654                      break;
   5.655 -                }
   5.656                  case opc_ifeq: {
   5.657                      int indx = i + readIntArg(byteCodes, i);
   5.658 -                    out.append("if (s.pop() == 0) { gt = " + indx);
   5.659 -                    out.append("; continue; }");
   5.660 +                    emit(out, "if (@1 == 0) { gt = @2; continue; }",
   5.661 +                         smapper.popI(), Integer.toString(indx));
   5.662                      i += 2;
   5.663                      break;
   5.664                  }
   5.665                  case opc_ifne: {
   5.666                      int indx = i + readIntArg(byteCodes, i);
   5.667 -                    out.append("if (s.pop() != 0) { gt = " + indx);
   5.668 -                    out.append("; continue; }");
   5.669 +                    emit(out, "if (@1 != 0) { gt = @2; continue; }",
   5.670 +                         smapper.popI(), Integer.toString(indx));
   5.671                      i += 2;
   5.672                      break;
   5.673                  }
   5.674                  case opc_iflt: {
   5.675                      int indx = i + readIntArg(byteCodes, i);
   5.676 -                    out.append("if (s.pop() < 0) { gt = " + indx);
   5.677 -                    out.append("; continue; }");
   5.678 +                    emit(out, "if (@1 < 0) { gt = @2; continue; }",
   5.679 +                         smapper.popI(), Integer.toString(indx));
   5.680                      i += 2;
   5.681                      break;
   5.682                  }
   5.683                  case opc_ifle: {
   5.684                      int indx = i + readIntArg(byteCodes, i);
   5.685 -                    out.append("if (s.pop() <= 0) { gt = " + indx);
   5.686 -                    out.append("; continue; }");
   5.687 +                    emit(out, "if (@1 <= 0) { gt = @2; continue; }",
   5.688 +                         smapper.popI(), Integer.toString(indx));
   5.689                      i += 2;
   5.690                      break;
   5.691                  }
   5.692                  case opc_ifgt: {
   5.693                      int indx = i + readIntArg(byteCodes, i);
   5.694 -                    out.append("if (s.pop() > 0) { gt = " + indx);
   5.695 -                    out.append("; continue; }");
   5.696 +                    emit(out, "if (@1 > 0) { gt = @2; continue; }",
   5.697 +                         smapper.popI(), Integer.toString(indx));
   5.698                      i += 2;
   5.699                      break;
   5.700                  }
   5.701                  case opc_ifge: {
   5.702                      int indx = i + readIntArg(byteCodes, i);
   5.703 -                    out.append("if (s.pop() >= 0) { gt = " + indx);
   5.704 -                    out.append("; continue; }");
   5.705 +                    emit(out, "if (@1 >= 0) { gt = @2; continue; }",
   5.706 +                         smapper.popI(), Integer.toString(indx));
   5.707                      i += 2;
   5.708                      break;
   5.709                  }
   5.710                  case opc_ifnonnull: {
   5.711                      int indx = i + readIntArg(byteCodes, i);
   5.712 -                    out.append("if (s.pop() !== null) { gt = " + indx);
   5.713 -                    out.append("; continue; }");
   5.714 +                    emit(out, "if (@1 !== null) { gt = @2; continue; }",
   5.715 +                         smapper.popA(), Integer.toString(indx));
   5.716                      i += 2;
   5.717                      break;
   5.718                  }
   5.719                  case opc_ifnull: {
   5.720                      int indx = i + readIntArg(byteCodes, i);
   5.721 -                    out.append("if (s.pop() === null) { gt = " + indx);
   5.722 -                    out.append("; continue; }");
   5.723 +                    emit(out, "if (@1 === null) { gt = @2; continue; }",
   5.724 +                         smapper.popA(), Integer.toString(indx));
   5.725                      i += 2;
   5.726                      break;
   5.727                  }
   5.728                  case opc_if_icmpne:
   5.729 -                    i = generateIf(byteCodes, i, "!=");
   5.730 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.731 +                                   "!=");
   5.732                      break;
   5.733                  case opc_if_icmplt:
   5.734 -                    i = generateIf(byteCodes, i, ">");
   5.735 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.736 +                                   "<");
   5.737                      break;
   5.738                  case opc_if_icmple:
   5.739 -                    i = generateIf(byteCodes, i, ">=");
   5.740 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.741 +                                   "<=");
   5.742                      break;
   5.743                  case opc_if_icmpgt:
   5.744 -                    i = generateIf(byteCodes, i, "<");
   5.745 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.746 +                                   ">");
   5.747                      break;
   5.748                  case opc_if_icmpge:
   5.749 -                    i = generateIf(byteCodes, i, "<=");
   5.750 +                    i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
   5.751 +                                   ">=");
   5.752                      break;
   5.753                  case opc_goto: {
   5.754                      int indx = i + readIntArg(byteCodes, i);
   5.755 -                    out.append("gt = " + indx).append("; continue;");
   5.756 +                    emit(out, "gt = @1; continue;", Integer.toString(indx));
   5.757                      i += 2;
   5.758                      break;
   5.759                  }
   5.760 @@ -598,7 +809,7 @@
   5.761                      table += 4;
   5.762                      int n = readInt4(byteCodes, table);
   5.763                      table += 4;
   5.764 -                    out.append("switch (s.pop()) {\n");
   5.765 +                    out.append("switch (").append(smapper.popI()).append(") {\n");
   5.766                      while (n-- > 0) {
   5.767                          int cnstnt = readInt4(byteCodes, table);
   5.768                          table += 4;
   5.769 @@ -618,7 +829,7 @@
   5.770                      table += 4;
   5.771                      int high = readInt4(byteCodes, table);
   5.772                      table += 4;
   5.773 -                    out.append("switch (s.pop()) {\n");
   5.774 +                    out.append("switch (").append(smapper.popI()).append(") {\n");
   5.775                      while (low <= high) {
   5.776                          int offset = i + readInt4(byteCodes, table);
   5.777                          table += 4;
   5.778 @@ -630,44 +841,45 @@
   5.779                      break;
   5.780                  }
   5.781                  case opc_invokeinterface: {
   5.782 -                    i = invokeVirtualMethod(byteCodes, i) + 2;
   5.783 +                    i = invokeVirtualMethod(byteCodes, i, smapper) + 2;
   5.784                      break;
   5.785                  }
   5.786                  case opc_invokevirtual:
   5.787 -                    i = invokeVirtualMethod(byteCodes, i);
   5.788 +                    i = invokeVirtualMethod(byteCodes, i, smapper);
   5.789                      break;
   5.790                  case opc_invokespecial:
   5.791 -                    i = invokeStaticMethod(byteCodes, i, false);
   5.792 +                    i = invokeStaticMethod(byteCodes, i, smapper, false);
   5.793                      break;
   5.794                  case opc_invokestatic:
   5.795 -                    i = invokeStaticMethod(byteCodes, i, true);
   5.796 +                    i = invokeStaticMethod(byteCodes, i, smapper, true);
   5.797                      break;
   5.798                  case opc_new: {
   5.799                      int indx = readIntArg(byteCodes, i);
   5.800                      String ci = jc.getClassName(indx);
   5.801 -                    out.append("s.push(new ");
   5.802 -                    out.append(accessClass(ci.replace('/','_')));
   5.803 -                    out.append("());");
   5.804 +                    emit(out, "@1 = new @2;",
   5.805 +                         smapper.pushA(), accessClass(ci.replace('/', '_')));
   5.806                      addReference(ci);
   5.807                      i += 2;
   5.808                      break;
   5.809                  }
   5.810 -                case opc_newarray: {
   5.811 -                    int type = byteCodes[i++];
   5.812 -                    out.append("s.push(new Array(s.pop()).fillNulls());");
   5.813 +                case opc_newarray:
   5.814 +                    ++i; // skip type of array
   5.815 +                    emit(out, "@2 = new Array(@1).fillNulls();",
   5.816 +                         smapper.popI(), smapper.pushA());
   5.817                      break;
   5.818 -                }
   5.819 -                case opc_anewarray: {
   5.820 +                case opc_anewarray:
   5.821                      i += 2; // skip type of array
   5.822 -                    out.append("s.push(new Array(s.pop()).fillNulls());");
   5.823 +                    emit(out, "@2 = new Array(@1).fillNulls();",
   5.824 +                         smapper.popI(), smapper.pushA());
   5.825                      break;
   5.826 -                }
   5.827                  case opc_multianewarray: {
   5.828                      i += 2;
   5.829                      int dim = readByte(byteCodes, ++i);
   5.830 -                    out.append("{ var a0 = new Array(s.pop()).fillNulls();");
   5.831 +                    out.append("{ var a0 = new Array(").append(smapper.popI())
   5.832 +                       .append(").fillNulls();");
   5.833                      for (int d = 1; d < dim; d++) {
   5.834 -                        out.append("\n  var l" + d).append(" = s.pop();");
   5.835 +                        out.append("\n  var l" + d).append(" = ")
   5.836 +                           .append(smapper.popI()).append(';');
   5.837                          out.append("\n  for (var i" + d).append (" = 0; i" + d).
   5.838                              append(" < a" + (d - 1)).
   5.839                              append(".length; i" + d).append("++) {");
   5.840 @@ -679,77 +891,142 @@
   5.841                      for (int d = 1; d < dim; d++) {
   5.842                          out.append("\n  }");
   5.843                      }
   5.844 -                    out.append("\ns.push(a0); }");
   5.845 +                    out.append("\n").append(smapper.pushA()).append(" = a0; }");
   5.846                      break;
   5.847                  }
   5.848                  case opc_arraylength:
   5.849 -                    out.append("s.push(s.pop().length);");
   5.850 +                    emit(out, "@2 = @1.length;", smapper.popA(), smapper.pushI());
   5.851 +                    break;
   5.852 +                case opc_lastore:
   5.853 +                    emit(out, "@3[@2] = @1;",
   5.854 +                         smapper.popL(), smapper.popI(), smapper.popA());
   5.855 +                    break;
   5.856 +                case opc_fastore:
   5.857 +                    emit(out, "@3[@2] = @1;",
   5.858 +                         smapper.popF(), smapper.popI(), smapper.popA());
   5.859 +                    break;
   5.860 +                case opc_dastore:
   5.861 +                    emit(out, "@3[@2] = @1;",
   5.862 +                         smapper.popD(), smapper.popI(), smapper.popA());
   5.863 +                    break;
   5.864 +                case opc_aastore:
   5.865 +                    emit(out, "@3[@2] = @1;",
   5.866 +                         smapper.popA(), smapper.popI(), smapper.popA());
   5.867                      break;
   5.868                  case opc_iastore:
   5.869 -                case opc_lastore:
   5.870 -                case opc_fastore:
   5.871 -                case opc_dastore:
   5.872 -                case opc_aastore:
   5.873                  case opc_bastore:
   5.874                  case opc_castore:
   5.875 -                case opc_sastore: {
   5.876 -                    out.append("{ var value = s.pop(); var indx = s.pop(); s.pop()[indx] = value; }");
   5.877 +                case opc_sastore:
   5.878 +                    emit(out, "@3[@2] = @1;",
   5.879 +                         smapper.popI(), smapper.popI(), smapper.popA());
   5.880 +                    break;
   5.881 +                case opc_laload:
   5.882 +                    emit(out, "@3 = @2[@1];",
   5.883 +                         smapper.popI(), smapper.popA(), smapper.pushL());
   5.884 +                    break;
   5.885 +                case opc_faload:
   5.886 +                    emit(out, "@3 = @2[@1];",
   5.887 +                         smapper.popI(), smapper.popA(), smapper.pushF());
   5.888 +                    break;
   5.889 +                case opc_daload:
   5.890 +                    emit(out, "@3 = @2[@1];",
   5.891 +                         smapper.popI(), smapper.popA(), smapper.pushD());
   5.892 +                    break;
   5.893 +                case opc_aaload:
   5.894 +                    emit(out, "@3 = @2[@1];",
   5.895 +                         smapper.popI(), smapper.popA(), smapper.pushA());
   5.896 +                    break;
   5.897 +                case opc_iaload:
   5.898 +                case opc_baload:
   5.899 +                case opc_caload:
   5.900 +                case opc_saload:
   5.901 +                    emit(out, "@3 = @2[@1];",
   5.902 +                         smapper.popI(), smapper.popA(), smapper.pushI());
   5.903 +                    break;
   5.904 +                case opc_pop:
   5.905 +                case opc_pop2:
   5.906 +                    smapper.pop(1);
   5.907 +                    out.append("/* pop */");
   5.908 +                    break;
   5.909 +                case opc_dup: {
   5.910 +                    final Variable v = smapper.get(0);
   5.911 +                    emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
   5.912                      break;
   5.913                  }
   5.914 -                case opc_iaload:
   5.915 -                case opc_laload:
   5.916 -                case opc_faload:
   5.917 -                case opc_daload:
   5.918 -                case opc_aaload:
   5.919 -                case opc_baload:
   5.920 -                case opc_caload:
   5.921 -                case opc_saload: {
   5.922 -                    out.append("{ var indx = s.pop(); s.push(s.pop()[indx]); }");
   5.923 +                case opc_dup2: {
   5.924 +                    if (smapper.get(0).isCategory2()) {
   5.925 +                        final Variable v = smapper.get(0);
   5.926 +                        emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
   5.927 +                    } else {
   5.928 +                        final Variable v1 = smapper.get(0);
   5.929 +                        final Variable v2 = smapper.get(1);
   5.930 +                        emit(out, "{ @1 = @2; @3 = @4; }",
   5.931 +                             smapper.pushT(v2.getType()), v2,
   5.932 +                             smapper.pushT(v1.getType()), v1);
   5.933 +                    }
   5.934                      break;
   5.935                  }
   5.936 -                case opc_pop2:
   5.937 -                    out.append("s.pop();");
   5.938 -                case opc_pop:
   5.939 -                    out.append("s.pop();");
   5.940 +                case opc_dup_x1: {
   5.941 +                    final Variable vi1 = smapper.pop();
   5.942 +                    final Variable vi2 = smapper.pop();
   5.943 +                    final Variable vo3 = smapper.pushT(vi1.getType());
   5.944 +                    final Variable vo2 = smapper.pushT(vi2.getType());
   5.945 +                    final Variable vo1 = smapper.pushT(vi1.getType());
   5.946 +
   5.947 +                    emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }",
   5.948 +                         vo1, vi1, vo2, vi2, vo3, vo1);
   5.949                      break;
   5.950 -                case opc_dup:
   5.951 -                    out.append("s.push(s[s.length - 1]);");
   5.952 +                }
   5.953 +                case opc_dup_x2: {
   5.954 +                    if (smapper.get(1).isCategory2()) {
   5.955 +                        final Variable vi1 = smapper.pop();
   5.956 +                        final Variable vi2 = smapper.pop();
   5.957 +                        final Variable vo3 = smapper.pushT(vi1.getType());
   5.958 +                        final Variable vo2 = smapper.pushT(vi2.getType());
   5.959 +                        final Variable vo1 = smapper.pushT(vi1.getType());
   5.960 +
   5.961 +                        emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }",
   5.962 +                             vo1, vi1, vo2, vi2, vo3, vo1);
   5.963 +                    } else {
   5.964 +                        final Variable vi1 = smapper.pop();
   5.965 +                        final Variable vi2 = smapper.pop();
   5.966 +                        final Variable vi3 = smapper.pop();
   5.967 +                        final Variable vo4 = smapper.pushT(vi1.getType());
   5.968 +                        final Variable vo3 = smapper.pushT(vi3.getType());
   5.969 +                        final Variable vo2 = smapper.pushT(vi2.getType());
   5.970 +                        final Variable vo1 = smapper.pushT(vi1.getType());
   5.971 +
   5.972 +                        emit(out, "{ @1 = @2; @3 = @4; @5 = @6; @7 = @8; }",
   5.973 +                             vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1);
   5.974 +                    }
   5.975                      break;
   5.976 -                case opc_dup_x1:
   5.977 -                    out.append("{ var v1 = s.pop(); var v2 = s.pop(); s.push(v1); s.push(v2); s.push(v1); }");
   5.978 -                    break;
   5.979 -                case opc_dup_x2:
   5.980 -                    out.append("{ var v1 = s.pop(); var v2 = s.pop(); var v3 = s.pop(); s.push(v1); s.push(v3); s.push(v2); s.push(v1); }");
   5.981 -                    break;
   5.982 +                }
   5.983                  case opc_bipush:
   5.984 -                    out.append("s.push(" + byteCodes[++i] + ");");
   5.985 +                    emit(out, "@1 = @2;",
   5.986 +                         smapper.pushI(), Integer.toString(byteCodes[++i]));
   5.987                      break;
   5.988                  case opc_sipush:
   5.989 -                    out.append("s.push(" + readIntArg(byteCodes, i) + ");");
   5.990 +                    emit(out, "@1 = @2;",
   5.991 +                         smapper.pushI(),
   5.992 +                         Integer.toString(readIntArg(byteCodes, i)));
   5.993                      i += 2;
   5.994                      break;
   5.995                  case opc_getfield: {
   5.996                      int indx = readIntArg(byteCodes, i);
   5.997                      String[] fi = jc.getFieldInfoName(indx);
   5.998 -                    out.append("s.push(s.pop().fld_").
   5.999 -                        append(fi[1]).append(");");
  5.1000 +                    final int type = VarType.fromFieldType(fi[2].charAt(0));
  5.1001 +                    emit(out, "@2 = @1.fld_@3;",
  5.1002 +                         smapper.popA(), smapper.pushT(type), fi[1]);
  5.1003                      i += 2;
  5.1004                      break;
  5.1005                  }
  5.1006                  case opc_getstatic: {
  5.1007                      int indx = readIntArg(byteCodes, i);
  5.1008                      String[] fi = jc.getFieldInfoName(indx);
  5.1009 -                    out.append("s.push(").append(accessClass(fi[0].replace('/', '_')));
  5.1010 -                    out.append('.').append(fi[1]).append(");");
  5.1011 -                    i += 2;
  5.1012 -                    addReference(fi[0]);
  5.1013 -                    break;
  5.1014 -                }
  5.1015 -                case opc_putstatic: {
  5.1016 -                    int indx = readIntArg(byteCodes, i);
  5.1017 -                    String[] fi = jc.getFieldInfoName(indx);
  5.1018 -                    out.append(accessClass(fi[0].replace('/', '_')));
  5.1019 -                    out.append('.').append(fi[1]).append(" = s.pop();");
  5.1020 +                    final int type = VarType.fromFieldType(fi[2].charAt(0));
  5.1021 +                    emit(out, "@1 = @2.@3;",
  5.1022 +                         smapper.pushT(type),
  5.1023 +                         accessClass(fi[0].replace('/', '_')), fi[1]);
  5.1024                      i += 2;
  5.1025                      addReference(fi[0]);
  5.1026                      break;
  5.1027 @@ -757,19 +1034,32 @@
  5.1028                  case opc_putfield: {
  5.1029                      int indx = readIntArg(byteCodes, i);
  5.1030                      String[] fi = jc.getFieldInfoName(indx);
  5.1031 -                    out.append("{ var v = s.pop(); s.pop().fld_")
  5.1032 -                       .append(fi[1]).append(" = v; }");
  5.1033 +                    final int type = VarType.fromFieldType(fi[2].charAt(0));
  5.1034 +                    emit(out, "@2.fld_@3 = @1;",
  5.1035 +                         smapper.popT(type), smapper.popA(), fi[1]);
  5.1036                      i += 2;
  5.1037                      break;
  5.1038                  }
  5.1039 +                case opc_putstatic: {
  5.1040 +                    int indx = readIntArg(byteCodes, i);
  5.1041 +                    String[] fi = jc.getFieldInfoName(indx);
  5.1042 +                    final int type = VarType.fromFieldType(fi[2].charAt(0));
  5.1043 +                    emit(out, "@1.@2 = @3;",
  5.1044 +                         accessClass(fi[0].replace('/', '_')), fi[1],
  5.1045 +                         smapper.popT(type));
  5.1046 +                    i += 2;
  5.1047 +                    addReference(fi[0]);
  5.1048 +                    break;
  5.1049 +                }
  5.1050                  case opc_checkcast: {
  5.1051                      int indx = readIntArg(byteCodes, i);
  5.1052                      final String type = jc.getClassName(indx);
  5.1053                      if (!type.startsWith("[")) {
  5.1054                          // no way to check arrays right now
  5.1055 -                        out.append("if(s[s.length - 1] !== null && !s[s.length - 1].$instOf_")
  5.1056 -                           .append(type.replace('/', '_'))
  5.1057 -                           .append(") throw {};"); // XXX proper exception
  5.1058 +                        // XXX proper exception
  5.1059 +                        emit(out,
  5.1060 +                             "if (@1 !== null && !@1.$instOf_@2) throw {};",
  5.1061 +                             smapper.getA(0), type.replace('/', '_'));
  5.1062                      }
  5.1063                      i += 2;
  5.1064                      break;
  5.1065 @@ -777,20 +1067,37 @@
  5.1066                  case opc_instanceof: {
  5.1067                      int indx = readIntArg(byteCodes, i);
  5.1068                      final String type = jc.getClassName(indx);
  5.1069 -                    out.append("s.push(s.pop().$instOf_")
  5.1070 -                       .append(type.replace('/', '_'))
  5.1071 -                       .append(" ? 1 : 0);");
  5.1072 +                    emit(out, "@2 = @1.$instOf_@3 ? 1 : 0;",
  5.1073 +                         smapper.popA(), smapper.pushI(),
  5.1074 +                         type.replace('/', '_'));
  5.1075                      i += 2;
  5.1076                      break;
  5.1077                  }
  5.1078                  case opc_athrow: {
  5.1079 -                    out.append("{ var t = s.pop(); s = new Array(1); s[0] = t; throw t; }");
  5.1080 +                    final Variable v = smapper.popA();
  5.1081 +                    smapper.clear();
  5.1082 +
  5.1083 +                    emit(out, "{ @1 = @2; throw @2; }",
  5.1084 +                         smapper.pushA(), v);
  5.1085                      break;
  5.1086                  }
  5.1087 +
  5.1088 +                case opc_monitorenter: {
  5.1089 +                    out.append("/* monitor enter */");
  5.1090 +                    smapper.popA();
  5.1091 +                    break;
  5.1092 +                }
  5.1093 +
  5.1094 +                case opc_monitorexit: {
  5.1095 +                    out.append("/* monitor exit */");
  5.1096 +                    smapper.popA();
  5.1097 +                    break;
  5.1098 +                }
  5.1099 +
  5.1100                  default: {
  5.1101 -                    out.append("throw 'unknown bytecode " + c + "';");
  5.1102 +                    emit(out, "throw 'unknown bytecode @1';",
  5.1103 +                         Integer.toString(c));
  5.1104                  }
  5.1105 -                    
  5.1106              }
  5.1107              out.append(" //");
  5.1108              for (int j = prev; j <= i; j++) {
  5.1109 @@ -801,12 +1108,17 @@
  5.1110              out.append("\n");
  5.1111          }
  5.1112          out.append("  }\n");
  5.1113 +        out.append("};");
  5.1114      }
  5.1115  
  5.1116 -    private int generateIf(byte[] byteCodes, int i, final String test) throws IOException {
  5.1117 +    private int generateIf(byte[] byteCodes, int i,
  5.1118 +                           final Variable v2, final Variable v1,
  5.1119 +                           final String test) throws IOException {
  5.1120          int indx = i + readIntArg(byteCodes, i);
  5.1121 -        out.append("if (s.pop() ").append(test).append(" s.pop()) { gt = " + indx);
  5.1122 -        out.append("; continue; }");
  5.1123 +        out.append("if (").append(v1)
  5.1124 +           .append(' ').append(test).append(' ')
  5.1125 +           .append(v2).append(") { gt = " + indx)
  5.1126 +           .append("; continue; }");
  5.1127          return i + 2;
  5.1128      }
  5.1129  
  5.1130 @@ -823,10 +1135,10 @@
  5.1131          return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
  5.1132      }
  5.1133      private int readByte(byte[] byteCodes, int offsetInstruction) {
  5.1134 -        return (byteCodes[offsetInstruction] + 256) % 256;
  5.1135 +        return byteCodes[offsetInstruction] & 0xff;
  5.1136      }
  5.1137      
  5.1138 -    private static void countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig, StringBuilder cnt) {
  5.1139 +    private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
  5.1140          int i = 0;
  5.1141          Boolean count = null;
  5.1142          boolean array = false;
  5.1143 @@ -860,17 +1172,19 @@
  5.1144                              cnt.append('0');
  5.1145                          }
  5.1146                      } else {
  5.1147 -                        hasReturnType[0] = true;
  5.1148                          sig.insert(firstPos, ch);
  5.1149                          if (array) {
  5.1150 +                            returnType[0] = '[';
  5.1151                              sig.insert(firstPos, "_3");
  5.1152 +                        } else {
  5.1153 +                            returnType[0] = ch;
  5.1154                          }
  5.1155                      }
  5.1156                      array = false;
  5.1157                      continue;
  5.1158                  case 'V': 
  5.1159                      assert !count;
  5.1160 -                    hasReturnType[0] = false;
  5.1161 +                    returnType[0] = 'V';
  5.1162                      sig.insert(firstPos, 'V');
  5.1163                      continue;
  5.1164                  case 'L':
  5.1165 @@ -887,7 +1201,7 @@
  5.1166                          if (array) {
  5.1167                              sig.insert(firstPos, "_3");
  5.1168                          }
  5.1169 -                        hasReturnType[0] = true;
  5.1170 +                        returnType[0] = 'L';
  5.1171                      }
  5.1172                      i = next + 1;
  5.1173                      continue;
  5.1174 @@ -925,12 +1239,11 @@
  5.1175              name.append(m.getName());
  5.1176          } 
  5.1177          
  5.1178 -        boolean hasReturn[] = { false };
  5.1179 -        countArgs(m.getInternalSig(), hasReturn, name, cnt);
  5.1180 +        countArgs(m.getInternalSig(), new char[1], name, cnt);
  5.1181          return name.toString();
  5.1182      }
  5.1183  
  5.1184 -    static String findMethodName(String[] mi, StringBuilder cnt, boolean[] hasReturn) {
  5.1185 +    static String findMethodName(String[] mi, StringBuilder cnt, char[] returnType) {
  5.1186          StringBuilder name = new StringBuilder();
  5.1187          String descr = mi[2];//mi.getDescriptor();
  5.1188          String nm= mi[1];
  5.1189 @@ -939,81 +1252,80 @@
  5.1190          } else {
  5.1191              name.append(nm);
  5.1192          }
  5.1193 -        countArgs(descr, hasReturn, name, cnt);
  5.1194 +        countArgs(descr, returnType, name, cnt);
  5.1195          return name.toString();
  5.1196      }
  5.1197  
  5.1198 -    private int invokeStaticMethod(byte[] byteCodes, int i, boolean isStatic)
  5.1199 +    private int invokeStaticMethod(byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic)
  5.1200      throws IOException {
  5.1201          int methodIndex = readIntArg(byteCodes, i);
  5.1202          String[] mi = jc.getFieldInfoName(methodIndex);
  5.1203 -        boolean[] hasReturn = { false };
  5.1204 +        char[] returnType = { 'V' };
  5.1205          StringBuilder cnt = new StringBuilder();
  5.1206 -        String mn = findMethodName(mi, cnt, hasReturn);
  5.1207 -        out.append("{ ");
  5.1208 -        for (int j = cnt.length() - 1; j >= 0; j--) {
  5.1209 -            out.append("var v" + j).append(" = s.pop(); ");
  5.1210 +        String mn = findMethodName(mi, cnt, returnType);
  5.1211 +
  5.1212 +        final int numArguments = isStatic ? cnt.length() : cnt.length() + 1;
  5.1213 +        final Variable[] vars = new Variable[numArguments];
  5.1214 +
  5.1215 +        for (int j = numArguments - 1; j >= 0; --j) {
  5.1216 +            vars[j] = mapper.pop();
  5.1217          }
  5.1218 -        
  5.1219 -        if (hasReturn[0]) {
  5.1220 -            out.append("s.push(");
  5.1221 +
  5.1222 +        if (returnType[0] != 'V') {
  5.1223 +            out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
  5.1224 +               .append(" = ");
  5.1225          }
  5.1226 +
  5.1227          final String in = mi[0];
  5.1228          out.append(accessClass(in.replace('/', '_')));
  5.1229          out.append("(false).");
  5.1230          out.append(mn);
  5.1231          out.append('(');
  5.1232 -        String sep = "";
  5.1233 -        if (!isStatic) {
  5.1234 -            out.append("s.pop()");
  5.1235 -            sep = ", ";
  5.1236 +        if (numArguments > 0) {
  5.1237 +            out.append(vars[0]);
  5.1238 +            for (int j = 1; j < numArguments; ++j) {
  5.1239 +                out.append(", ");
  5.1240 +                out.append(vars[j]);
  5.1241 +            }
  5.1242          }
  5.1243 -        for (int j = 0; j < cnt.length(); j++) {
  5.1244 -            out.append(sep);
  5.1245 -            out.append("v" + j);
  5.1246 -            sep = ", ";
  5.1247 -        }
  5.1248 -        out.append(")");
  5.1249 -        if (hasReturn[0]) {
  5.1250 -            out.append(")");
  5.1251 -        }
  5.1252 -        out.append("; }");
  5.1253 +        out.append(");");
  5.1254          i += 2;
  5.1255          addReference(in);
  5.1256          return i;
  5.1257      }
  5.1258 -    private int invokeVirtualMethod(byte[] byteCodes, int i)
  5.1259 +    private int invokeVirtualMethod(byte[] byteCodes, int i, final StackMapper mapper)
  5.1260      throws IOException {
  5.1261          int methodIndex = readIntArg(byteCodes, i);
  5.1262          String[] mi = jc.getFieldInfoName(methodIndex);
  5.1263 -        boolean[] hasReturn = { false };
  5.1264 +        char[] returnType = { 'V' };
  5.1265          StringBuilder cnt = new StringBuilder();
  5.1266 -        String mn = findMethodName(mi, cnt, hasReturn);
  5.1267 -        out.append("{ ");
  5.1268 -        for (int j = cnt.length() - 1; j >= 0; j--) {
  5.1269 -            out.append("var v" + j).append(" = s.pop(); ");
  5.1270 +        String mn = findMethodName(mi, cnt, returnType);
  5.1271 +
  5.1272 +        final int numArguments = cnt.length() + 1;
  5.1273 +        final Variable[] vars = new Variable[numArguments];
  5.1274 +
  5.1275 +        for (int j = numArguments - 1; j >= 0; --j) {
  5.1276 +            vars[j] = mapper.pop();
  5.1277          }
  5.1278 -        out.append("var self = s.pop(); ");
  5.1279 -        if (hasReturn[0]) {
  5.1280 -            out.append("s.push(");
  5.1281 +
  5.1282 +        if (returnType[0] != 'V') {
  5.1283 +            out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
  5.1284 +               .append(" = ");
  5.1285          }
  5.1286 -        out.append("self.");
  5.1287 +
  5.1288 +        out.append(vars[0]).append('.');
  5.1289          out.append(mn);
  5.1290          out.append('(');
  5.1291 -        out.append("self");
  5.1292 -        for (int j = 0; j < cnt.length(); j++) {
  5.1293 +        out.append(vars[0]);
  5.1294 +        for (int j = 1; j < numArguments; ++j) {
  5.1295              out.append(", ");
  5.1296 -            out.append("v" + j);
  5.1297 +            out.append(vars[j]);
  5.1298          }
  5.1299 -        out.append(")");
  5.1300 -        if (hasReturn[0]) {
  5.1301 -            out.append(")");
  5.1302 -        }
  5.1303 -        out.append("; }");
  5.1304 +        out.append(");");
  5.1305          i += 2;
  5.1306          return i;
  5.1307      }
  5.1308 -    
  5.1309 +
  5.1310      private void addReference(String cn) throws IOException {
  5.1311          if (requireReference(cn)) {
  5.1312              out.append(" /* needs ").append(cn).append(" */");
  5.1313 @@ -1197,4 +1509,29 @@
  5.1314          out.append(name);
  5.1315          return ",";
  5.1316      }
  5.1317 +
  5.1318 +    private static void emit(final Appendable out,
  5.1319 +                             final String format,
  5.1320 +                             final CharSequence... params) throws IOException {
  5.1321 +        final int length = format.length();
  5.1322 +
  5.1323 +        int processed = 0;
  5.1324 +        int paramOffset = format.indexOf('@');
  5.1325 +        while ((paramOffset != -1) && (paramOffset < (length - 1))) {
  5.1326 +            final char paramChar = format.charAt(paramOffset + 1);
  5.1327 +            if ((paramChar >= '1') && (paramChar <= '9')) {
  5.1328 +                final int paramIndex = paramChar - '0' - 1;
  5.1329 +
  5.1330 +                out.append(format, processed, paramOffset);
  5.1331 +                out.append(params[paramIndex]);
  5.1332 +
  5.1333 +                ++paramOffset;
  5.1334 +                processed = paramOffset + 1;
  5.1335 +            }
  5.1336 +
  5.1337 +            paramOffset = format.indexOf('@', paramOffset + 1);
  5.1338 +        }
  5.1339 +
  5.1340 +        out.append(format, processed, length);
  5.1341 +    }
  5.1342  }
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java	Fri Dec 14 11:32:45 2012 +0100
     6.3 @@ -0,0 +1,141 @@
     6.4 +/**
     6.5 + * Back 2 Browser Bytecode Translator
     6.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     6.7 + *
     6.8 + * This program is free software: you can redistribute it and/or modify
     6.9 + * it under the terms of the GNU General Public License as published by
    6.10 + * the Free Software Foundation, version 2 of the License.
    6.11 + *
    6.12 + * This program is distributed in the hope that it will be useful,
    6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.15 + * GNU General Public License for more details.
    6.16 + *
    6.17 + * You should have received a copy of the GNU General Public License
    6.18 + * along with this program. Look for COPYING file in the top folder.
    6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    6.20 + */
    6.21 +package org.apidesign.vm4brwsr;
    6.22 +
    6.23 +import java.io.IOException;
    6.24 +import org.apidesign.javap.RuntimeConstants;
    6.25 +import org.apidesign.javap.TypeArray;
    6.26 +
    6.27 +final class LocalsMapper {
    6.28 +    private final TypeArray argTypeRecords;
    6.29 +    private final TypeArray localTypeRecords;
    6.30 +
    6.31 +    public LocalsMapper(final TypeArray stackMapArgs) {
    6.32 +        final TypeArray initTypeRecords = new TypeArray();
    6.33 +        updateRecords(initTypeRecords, stackMapArgs);
    6.34 +
    6.35 +        argTypeRecords = initTypeRecords;
    6.36 +        localTypeRecords = new TypeArray(initTypeRecords);
    6.37 +    }
    6.38 +
    6.39 +    public void outputArguments(final Appendable out) throws IOException {
    6.40 +        final int argRecordCount = argTypeRecords.getSize();
    6.41 +        if (argRecordCount > 0) {
    6.42 +            Variable variable = getVariable(argTypeRecords, 0);
    6.43 +            out.append(variable);
    6.44 +
    6.45 +            int i = variable.isCategory2() ? 2 : 1;
    6.46 +            while (i < argRecordCount) {
    6.47 +                variable = getVariable(argTypeRecords, i);
    6.48 +                out.append(", ");
    6.49 +                out.append(variable);
    6.50 +                i += variable.isCategory2() ? 2 : 1;
    6.51 +            }
    6.52 +        }
    6.53 +    }
    6.54 +
    6.55 +    public void syncWithFrameLocals(final TypeArray frameLocals) {
    6.56 +        updateRecords(localTypeRecords, frameLocals);
    6.57 +    }
    6.58 +
    6.59 +    public Variable setI(final int index) {
    6.60 +        return setT(index, VarType.INTEGER);
    6.61 +    }
    6.62 +
    6.63 +    public Variable setL(final int index) {
    6.64 +        return setT(index, VarType.LONG);
    6.65 +    }
    6.66 +
    6.67 +    public Variable setF(final int index) {
    6.68 +        return setT(index, VarType.FLOAT);
    6.69 +    }
    6.70 +
    6.71 +    public Variable setD(final int index) {
    6.72 +        return setT(index, VarType.DOUBLE);
    6.73 +    }
    6.74 +
    6.75 +    public Variable setA(final int index) {
    6.76 +        return setT(index, VarType.REFERENCE);
    6.77 +    }
    6.78 +
    6.79 +    public Variable setT(final int index, final int type) {
    6.80 +        updateRecord(localTypeRecords, index, type);
    6.81 +        return Variable.getLocalVariable(type, index);
    6.82 +    }
    6.83 +
    6.84 +    public Variable getI(final int index) {
    6.85 +        return getT(index, VarType.INTEGER);
    6.86 +    }
    6.87 +
    6.88 +    public Variable getL(final int index) {
    6.89 +        return getT(index, VarType.LONG);
    6.90 +    }
    6.91 +
    6.92 +    public Variable getF(final int index) {
    6.93 +        return getT(index, VarType.FLOAT);
    6.94 +    }
    6.95 +
    6.96 +    public Variable getD(final int index) {
    6.97 +        return getT(index, VarType.DOUBLE);
    6.98 +    }
    6.99 +
   6.100 +    public Variable getA(final int index) {
   6.101 +        return getT(index, VarType.REFERENCE);
   6.102 +    }
   6.103 +
   6.104 +    public Variable getT(final int index, final int type) {
   6.105 +        final int oldRecordValue = localTypeRecords.get(index);
   6.106 +        if ((oldRecordValue & 0xff) != type) {
   6.107 +            throw new IllegalStateException("Type mismatch");
   6.108 +        }
   6.109 +
   6.110 +        return Variable.getLocalVariable(type, index);
   6.111 +    }
   6.112 +
   6.113 +    private static void updateRecords(final TypeArray typeRecords,
   6.114 +                                      final TypeArray stackMapTypes) {
   6.115 +        final int srcSize = stackMapTypes.getSize();
   6.116 +        for (int i = 0, dstIndex = 0; i < srcSize; ++i) {
   6.117 +            final int smType = stackMapTypes.get(i);
   6.118 +            if (smType == RuntimeConstants.ITEM_Bogus) {
   6.119 +                ++dstIndex;
   6.120 +                continue;
   6.121 +            }
   6.122 +            final int varType = VarType.fromStackMapType(smType);
   6.123 +            updateRecord(typeRecords, dstIndex, varType);
   6.124 +            dstIndex += VarType.isCategory2(varType) ? 2 : 1;
   6.125 +        }
   6.126 +    }
   6.127 +
   6.128 +    private static void updateRecord(final TypeArray typeRecords,
   6.129 +                                     final int index, final int type) {
   6.130 +        if (typeRecords.getSize() < (index + 1)) {
   6.131 +            typeRecords.setSize(index + 1);
   6.132 +        }
   6.133 +
   6.134 +        final int oldRecordValue = typeRecords.get(index);
   6.135 +        final int usedTypesMask =
   6.136 +                (oldRecordValue >> 8) | (1 << type);
   6.137 +        typeRecords.set(index, (usedTypesMask << 8) | type);
   6.138 +    }
   6.139 +
   6.140 +    private static Variable getVariable(final TypeArray typeRecords,
   6.141 +                                        final int index) {
   6.142 +        return Variable.getLocalVariable(typeRecords.get(index) & 0xff, index);
   6.143 +    }
   6.144 +}
     7.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/Main.java	Thu Dec 13 23:20:47 2012 +0100
     7.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Main.java	Fri Dec 14 11:32:45 2012 +0100
     7.3 @@ -40,7 +40,11 @@
     7.4          Writer w = new BufferedWriter(new FileWriter(args[0]));
     7.5          StringArray classes = StringArray.asList(args);
     7.6          classes.delete(0);
     7.7 -        Bck2Brwsr.generate(w, Main.class.getClassLoader(), classes.toArray());
     7.8 -        w.close();
     7.9 +        try {
    7.10 +            Bck2Brwsr.generate(w, Main.class.getClassLoader(),
    7.11 +                               classes.toArray());
    7.12 +        } finally {
    7.13 +            w.close();
    7.14 +        }
    7.15      }
    7.16  }
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java	Fri Dec 14 11:32:45 2012 +0100
     8.3 @@ -0,0 +1,194 @@
     8.4 +/**
     8.5 + * Back 2 Browser Bytecode Translator
     8.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     8.7 + *
     8.8 + * This program is free software: you can redistribute it and/or modify
     8.9 + * it under the terms of the GNU General Public License as published by
    8.10 + * the Free Software Foundation, version 2 of the License.
    8.11 + *
    8.12 + * This program is distributed in the hope that it will be useful,
    8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.15 + * GNU General Public License for more details.
    8.16 + *
    8.17 + * You should have received a copy of the GNU General Public License
    8.18 + * along with this program. Look for COPYING file in the top folder.
    8.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    8.20 + */
    8.21 +package org.apidesign.vm4brwsr;
    8.22 +
    8.23 +import org.apidesign.javap.TypeArray;
    8.24 +
    8.25 +final class StackMapper {
    8.26 +    private final TypeArray stackTypeIndexPairs;
    8.27 +    private int[] typeCounters;
    8.28 +    private int[] typeMaxCounters;
    8.29 +
    8.30 +    public StackMapper() {
    8.31 +        stackTypeIndexPairs = new TypeArray();
    8.32 +        typeCounters = new int[VarType.LAST + 1];
    8.33 +        typeMaxCounters = new int[VarType.LAST + 1];
    8.34 +    }
    8.35 +
    8.36 +    public void clear() {
    8.37 +        for (int type = 0; type <= VarType.LAST; ++type) {
    8.38 +            typeCounters[type] = 0;
    8.39 +        }
    8.40 +        stackTypeIndexPairs.clear();
    8.41 +    }
    8.42 +
    8.43 +    public void syncWithFrameStack(final TypeArray frameStack) {
    8.44 +        clear();
    8.45 +
    8.46 +        final int size = frameStack.getSize();
    8.47 +        for (int i = 0; i < size; ++i) {
    8.48 +            pushTypeImpl(VarType.fromStackMapType(frameStack.get(i)));
    8.49 +        }
    8.50 +    }
    8.51 +
    8.52 +    public Variable pushI() {
    8.53 +        return pushT(VarType.INTEGER);
    8.54 +    }
    8.55 +
    8.56 +    public Variable pushL() {
    8.57 +        return pushT(VarType.LONG);
    8.58 +    }
    8.59 +
    8.60 +    public Variable pushF() {
    8.61 +        return pushT(VarType.FLOAT);
    8.62 +    }
    8.63 +
    8.64 +    public Variable pushD() {
    8.65 +        return pushT(VarType.DOUBLE);
    8.66 +    }
    8.67 +
    8.68 +    public Variable pushA() {
    8.69 +        return pushT(VarType.REFERENCE);
    8.70 +    }
    8.71 +
    8.72 +    public Variable pushT(final int type) {
    8.73 +        return getVariable(pushTypeImpl(type));
    8.74 +    }
    8.75 +
    8.76 +    public Variable popI() {
    8.77 +        return popT(VarType.INTEGER);
    8.78 +    }
    8.79 +
    8.80 +    public Variable popL() {
    8.81 +        return popT(VarType.LONG);
    8.82 +    }
    8.83 +
    8.84 +    public Variable popF() {
    8.85 +        return popT(VarType.FLOAT);
    8.86 +    }
    8.87 +
    8.88 +    public Variable popD() {
    8.89 +        return popT(VarType.DOUBLE);
    8.90 +    }
    8.91 +
    8.92 +    public Variable popA() {
    8.93 +        return popT(VarType.REFERENCE);
    8.94 +    }
    8.95 +
    8.96 +    public Variable popT(final int type) {
    8.97 +        final Variable variable = getT(0, type);
    8.98 +        popImpl(1);
    8.99 +        return variable;
   8.100 +    }
   8.101 +
   8.102 +    public Variable pop() {
   8.103 +        final Variable variable = get(0);
   8.104 +        popImpl(1);
   8.105 +        return variable;
   8.106 +    }
   8.107 +
   8.108 +    public void pop(final int count) {
   8.109 +        final int stackSize = stackTypeIndexPairs.getSize();
   8.110 +        if (count > stackSize) {
   8.111 +            throw new IllegalStateException("Stack underflow");
   8.112 +        }
   8.113 +        popImpl(count);
   8.114 +    }
   8.115 +
   8.116 +    public Variable getI(final int indexFromTop) {
   8.117 +        return getT(indexFromTop, VarType.INTEGER);
   8.118 +    }
   8.119 +
   8.120 +    public Variable getL(final int indexFromTop) {
   8.121 +        return getT(indexFromTop, VarType.LONG);
   8.122 +    }
   8.123 +
   8.124 +    public Variable getF(final int indexFromTop) {
   8.125 +        return getT(indexFromTop, VarType.FLOAT);
   8.126 +    }
   8.127 +
   8.128 +    public Variable getD(final int indexFromTop) {
   8.129 +        return getT(indexFromTop, VarType.DOUBLE);
   8.130 +    }
   8.131 +
   8.132 +    public Variable getA(final int indexFromTop) {
   8.133 +        return getT(indexFromTop, VarType.REFERENCE);
   8.134 +    }
   8.135 +
   8.136 +    public Variable getT(final int indexFromTop, final int type) {
   8.137 +        final int stackSize = stackTypeIndexPairs.getSize();
   8.138 +        if (indexFromTop >= stackSize) {
   8.139 +            throw new IllegalStateException("Stack underflow");
   8.140 +        }
   8.141 +        final int stackValue =
   8.142 +                stackTypeIndexPairs.get(stackSize - indexFromTop - 1);
   8.143 +        if ((stackValue & 0xff) != type) {
   8.144 +            throw new IllegalStateException("Type mismatch");
   8.145 +        }
   8.146 +
   8.147 +        return getVariable(stackValue);
   8.148 +    }
   8.149 +
   8.150 +    public Variable get(final int indexFromTop) {
   8.151 +        final int stackSize = stackTypeIndexPairs.getSize();
   8.152 +        if (indexFromTop >= stackSize) {
   8.153 +            throw new IllegalStateException("Stack underflow");
   8.154 +        }
   8.155 +        final int stackValue =
   8.156 +                stackTypeIndexPairs.get(stackSize - indexFromTop - 1);
   8.157 +
   8.158 +        return getVariable(stackValue);
   8.159 +    }
   8.160 +
   8.161 +    private int pushTypeImpl(final int type) {
   8.162 +        final int count = typeCounters[type];
   8.163 +        final int value = (count << 8) | (type & 0xff);
   8.164 +        incCounter(type);
   8.165 +        stackTypeIndexPairs.add(value);
   8.166 +
   8.167 +        return value;
   8.168 +    }
   8.169 +
   8.170 +    private void popImpl(final int count) {
   8.171 +        final int stackSize = stackTypeIndexPairs.getSize();
   8.172 +        for (int i = stackSize - count; i < stackSize; ++i) {
   8.173 +            final int value = stackTypeIndexPairs.get(i);
   8.174 +            decCounter(value & 0xff);
   8.175 +        }
   8.176 +
   8.177 +        stackTypeIndexPairs.setSize(stackSize - count);
   8.178 +    }
   8.179 +
   8.180 +    private void incCounter(final int type) {
   8.181 +        final int newValue = ++typeCounters[type];
   8.182 +        if (typeMaxCounters[type] < newValue) {
   8.183 +            typeMaxCounters[type] = newValue;
   8.184 +        }
   8.185 +    }
   8.186 +
   8.187 +    private void decCounter(final int type) {
   8.188 +        --typeCounters[type];
   8.189 +    }
   8.190 +
   8.191 +    public Variable getVariable(final int typeAndIndex) {
   8.192 +        final int type = typeAndIndex & 0xff;
   8.193 +        final int index = typeAndIndex >> 8;
   8.194 +
   8.195 +        return Variable.getStackVariable(type, index);
   8.196 +    }
   8.197 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VarType.java	Fri Dec 14 11:32:45 2012 +0100
     9.3 @@ -0,0 +1,110 @@
     9.4 +/**
     9.5 + * Back 2 Browser Bytecode Translator
     9.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     9.7 + *
     9.8 + * This program is free software: you can redistribute it and/or modify
     9.9 + * it under the terms of the GNU General Public License as published by
    9.10 + * the Free Software Foundation, version 2 of the License.
    9.11 + *
    9.12 + * This program is distributed in the hope that it will be useful,
    9.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9.15 + * GNU General Public License for more details.
    9.16 + *
    9.17 + * You should have received a copy of the GNU General Public License
    9.18 + * along with this program. Look for COPYING file in the top folder.
    9.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    9.20 + */
    9.21 +package org.apidesign.vm4brwsr;
    9.22 +
    9.23 +import org.apidesign.javap.RuntimeConstants;
    9.24 +
    9.25 +final class VarType {
    9.26 +    public static final int INTEGER = 0;
    9.27 +    public static final int LONG = 1;
    9.28 +    public static final int FLOAT = 2;
    9.29 +    public static final int DOUBLE = 3;
    9.30 +    public static final int REFERENCE = 4;
    9.31 +
    9.32 +    public static final int LAST = REFERENCE;
    9.33 +
    9.34 +    private VarType() {
    9.35 +    }
    9.36 +
    9.37 +    public static boolean isCategory2(final int varType) {
    9.38 +        return (varType == LONG) || (varType == DOUBLE);
    9.39 +    }
    9.40 +
    9.41 +    public static int fromStackMapType(final int smType) {
    9.42 +        switch (smType & 0xff) {
    9.43 +            case RuntimeConstants.ITEM_Integer:
    9.44 +                return VarType.INTEGER;
    9.45 +            case RuntimeConstants.ITEM_Float:
    9.46 +                return VarType.FLOAT;
    9.47 +            case RuntimeConstants.ITEM_Double:
    9.48 +                return VarType.DOUBLE;
    9.49 +            case RuntimeConstants.ITEM_Long:
    9.50 +                return VarType.LONG;
    9.51 +            case RuntimeConstants.ITEM_Object:
    9.52 +                return VarType.REFERENCE;
    9.53 +
    9.54 +            case RuntimeConstants.ITEM_Bogus:
    9.55 +            case RuntimeConstants.ITEM_Null:
    9.56 +            case RuntimeConstants.ITEM_InitObject:
    9.57 +            case RuntimeConstants.ITEM_NewObject:
    9.58 +                /* unclear how to handle for now */
    9.59 +            default:
    9.60 +                throw new IllegalStateException("Unhandled stack map type");
    9.61 +        }
    9.62 +    }
    9.63 +
    9.64 +    public static int fromConstantType(final byte constantTag) {
    9.65 +        switch (constantTag) {
    9.66 +            case RuntimeConstants.CONSTANT_INTEGER:
    9.67 +                return VarType.INTEGER;
    9.68 +            case RuntimeConstants.CONSTANT_FLOAT:
    9.69 +                return VarType.FLOAT;
    9.70 +            case RuntimeConstants.CONSTANT_LONG:
    9.71 +                return VarType.LONG;
    9.72 +            case RuntimeConstants.CONSTANT_DOUBLE:
    9.73 +                return VarType.DOUBLE;
    9.74 +
    9.75 +            case RuntimeConstants.CONSTANT_CLASS:
    9.76 +            case RuntimeConstants.CONSTANT_UTF8:
    9.77 +            case RuntimeConstants.CONSTANT_UNICODE:
    9.78 +            case RuntimeConstants.CONSTANT_STRING:
    9.79 +                return VarType.REFERENCE;
    9.80 +
    9.81 +            case RuntimeConstants.CONSTANT_FIELD:
    9.82 +            case RuntimeConstants.CONSTANT_METHOD:
    9.83 +            case RuntimeConstants.CONSTANT_INTERFACEMETHOD:
    9.84 +            case RuntimeConstants.CONSTANT_NAMEANDTYPE:
    9.85 +                /* unclear how to handle for now */
    9.86 +            default:
    9.87 +                throw new IllegalStateException("Unhandled constant tag");
    9.88 +        }
    9.89 +    }
    9.90 +
    9.91 +    public static int fromFieldType(final char fieldType) {
    9.92 +        switch (fieldType) {
    9.93 +            case 'B':
    9.94 +            case 'C':
    9.95 +            case 'S':
    9.96 +            case 'Z':
    9.97 +            case 'I':
    9.98 +                return VarType.INTEGER;
    9.99 +            case 'J':
   9.100 +                return VarType.LONG;
   9.101 +            case 'F':
   9.102 +                return VarType.FLOAT;
   9.103 +            case 'D':
   9.104 +                return VarType.DOUBLE;
   9.105 +            case 'L':
   9.106 +            case '[':
   9.107 +                return VarType.REFERENCE;
   9.108 +
   9.109 +            default:
   9.110 +                throw new IllegalStateException("Unhandled field type");
   9.111 +        }
   9.112 +    }
   9.113 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Variable.java	Fri Dec 14 11:32:45 2012 +0100
    10.3 @@ -0,0 +1,100 @@
    10.4 +/**
    10.5 + * Back 2 Browser Bytecode Translator
    10.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    10.7 + *
    10.8 + * This program is free software: you can redistribute it and/or modify
    10.9 + * it under the terms of the GNU General Public License as published by
   10.10 + * the Free Software Foundation, version 2 of the License.
   10.11 + *
   10.12 + * This program is distributed in the hope that it will be useful,
   10.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.15 + * GNU General Public License for more details.
   10.16 + *
   10.17 + * You should have received a copy of the GNU General Public License
   10.18 + * along with this program. Look for COPYING file in the top folder.
   10.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
   10.20 + */
   10.21 +package org.apidesign.vm4brwsr;
   10.22 +
   10.23 +final class Variable implements CharSequence {
   10.24 +    private static final String STACK_VAR_PREFIX = "st";
   10.25 +    private static final String LOCAL_VAR_PREFIX = "lc";
   10.26 +
   10.27 +    private final String name;
   10.28 +    private final int type;
   10.29 +    private final int index;
   10.30 +
   10.31 +    private static final char[] TYPE_IDS = { 'I', 'L', 'F', 'D', 'A' };
   10.32 +
   10.33 +    private Variable(final String prefix, final int type, final int index) {
   10.34 +        this.name = prefix + TYPE_IDS[type] + index;
   10.35 +        this.type = type;
   10.36 +        this.index = index;
   10.37 +    }
   10.38 +
   10.39 +    public static Variable getStackVariable(
   10.40 +            final int type, final int index) {
   10.41 +        // TODO: precreate frequently used variables
   10.42 +        return new Variable(STACK_VAR_PREFIX, type, index);
   10.43 +    }
   10.44 +
   10.45 +    public static Variable getLocalVariable(
   10.46 +            final int type, final int index) {
   10.47 +        // TODO: precreate frequently used variables
   10.48 +        return new Variable(LOCAL_VAR_PREFIX, type, index);
   10.49 +    }
   10.50 +
   10.51 +    public String getName() {
   10.52 +        return name;
   10.53 +    }
   10.54 +
   10.55 +    public int getType() {
   10.56 +        return type;
   10.57 +    }
   10.58 +
   10.59 +    public int getIndex() {
   10.60 +        return index;
   10.61 +    }
   10.62 +
   10.63 +    public boolean isCategory2() {
   10.64 +        return VarType.isCategory2(type);
   10.65 +    }
   10.66 +
   10.67 +    @Override
   10.68 +    public int length() {
   10.69 +        return name.length();
   10.70 +    }
   10.71 +
   10.72 +    @Override
   10.73 +    public char charAt(final int index) {
   10.74 +        return name.charAt(index);
   10.75 +    }
   10.76 +
   10.77 +    @Override
   10.78 +    public CharSequence subSequence(final int start, final int end) {
   10.79 +        return name.subSequence(start, end);
   10.80 +    }
   10.81 +
   10.82 +    @Override
   10.83 +    public int hashCode() {
   10.84 +        return name.hashCode();
   10.85 +    }
   10.86 +
   10.87 +    @Override
   10.88 +    public boolean equals(final Object other) {
   10.89 +        if (this == other) {
   10.90 +            return true;
   10.91 +        }
   10.92 +        if (!(other instanceof Variable)) {
   10.93 +            return false;
   10.94 +        }
   10.95 +
   10.96 +        return name.equals(((Variable) other).name);
   10.97 +    }
   10.98 +
   10.99 +    @Override
  10.100 +    public String toString() {
  10.101 +        return name;
  10.102 +    }
  10.103 +}
    11.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java	Thu Dec 13 23:20:47 2012 +0100
    11.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java	Fri Dec 14 11:32:45 2012 +0100
    11.3 @@ -32,24 +32,24 @@
    11.4      @Test
    11.5      public void findMethodNameManglesObjectsCorrectly() {
    11.6          StringBuilder cnt = new StringBuilder();
    11.7 -        boolean[] hasReturn = { false };
    11.8 +        char[] returnType = { 'V' };
    11.9          String ret = ByteCodeToJavaScript.findMethodName(new String[] { 
   11.10              "StringTest", "replace", "(Ljava/lang/String;CC)Ljava/lang/String;"
   11.11 -        }, cnt, hasReturn);
   11.12 +        }, cnt, returnType);
   11.13          assertEquals(cnt.toString(), "000", "No doubles or longs");
   11.14 -        assertTrue(hasReturn[0], "Returns string");
   11.15 +        assertTrue(returnType[0] != 'V', "Returns string");
   11.16          assertEquals(ret, "replace__Ljava_lang_String_2Ljava_lang_String_2CC");
   11.17      }
   11.18  
   11.19      @Test
   11.20      public void manglingArrays() {
   11.21          StringBuilder cnt = new StringBuilder();
   11.22 -        boolean[] hasReturn = { false };
   11.23 +        char[] returnType = { 'V' };
   11.24          String ret = ByteCodeToJavaScript.findMethodName(new String[] { 
   11.25              "VMinVM", "toJavaScript", "([B)Ljava/lang/String;"
   11.26 -        }, cnt, hasReturn);
   11.27 +        }, cnt, returnType);
   11.28          assertEquals(cnt.toString(), "0", "No doubles or longs");
   11.29 -        assertTrue(hasReturn[0], "Returns string");
   11.30 +        assertTrue(returnType[0] != 'V', "Returns string");
   11.31          assertEquals(ret, "toJavaScript__Ljava_lang_String_2_3B");
   11.32      }
   11.33  }