# HG changeset patch # User Jaroslav Tulach # Date 1355481165 -3600 # Node ID 5876fdbbca117de5a1edef667ad2ad8974ebbdec # Parent 8da329789435be467c110affcbe01165ea1a9c33# Parent 15cbc8cb2163b8fea5e7cb82fa6870b71f2de6e2 Merging Lubomir's work on registers into default branch diff -r 8da329789435 -r 5876fdbbca11 javap/src/main/java/org/apidesign/javap/MethodData.java --- a/javap/src/main/java/org/apidesign/javap/MethodData.java Thu Dec 13 23:20:47 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/MethodData.java Fri Dec 14 11:32:45 2012 +0100 @@ -317,6 +317,10 @@ return stackMapTable; } + public StackMapIterator createStackMapIterator() { + return new StackMapIterator(this); + } + /** * Return true if method is static */ diff -r 8da329789435 -r 5876fdbbca11 javap/src/main/java/org/apidesign/javap/StackMapIterator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javap/src/main/java/org/apidesign/javap/StackMapIterator.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.apidesign.javap; + +import static org.apidesign.javap.RuntimeConstants.ITEM_Integer; +import static org.apidesign.javap.RuntimeConstants.ITEM_Float; +import static org.apidesign.javap.RuntimeConstants.ITEM_Double; +import static org.apidesign.javap.RuntimeConstants.ITEM_Long; +import static org.apidesign.javap.RuntimeConstants.ITEM_Object; + +public final class StackMapIterator { + private final StackMapTableData[] stackMapTable; + private final TypeArray argTypes; + private final TypeArray localTypes; + private final TypeArray stackTypes; + + private int nextFrameIndex; + private int lastFrameByteCodeOffset; + + private int byteCodeOffset; + + StackMapIterator(final MethodData methodData) { + this(methodData.getStackMapTable(), + methodData.getInternalSig(), + methodData.isStatic()); + } + + StackMapIterator(final StackMapTableData[] stackMapTable, + final String methodSignature, + final boolean isStaticMethod) { + this.stackMapTable = (stackMapTable != null) + ? stackMapTable + : new StackMapTableData[0]; + + argTypes = getArgTypes(methodSignature, isStaticMethod); + localTypes = new TypeArray(); + stackTypes = new TypeArray(); + + localTypes.addAll(argTypes); + + lastFrameByteCodeOffset = -1; + advanceBy(0); + } + + public String getFrameAsString() { + return (nextFrameIndex == 0) + ? StackMapTableData.toString("INITIAL", 0, null, null) + : stackMapTable[nextFrameIndex - 1].toString(); + } + + public int getFrameIndex() { + return nextFrameIndex; + } + + public TypeArray getFrameStack() { + return stackTypes; + } + + public TypeArray getFrameLocals() { + return localTypes; + } + + public TypeArray getArguments() { + return argTypes; + } + + public void advanceBy(final int numByteCodes) { + if (numByteCodes < 0) { + throw new IllegalStateException("Forward only iterator"); + } + + byteCodeOffset += numByteCodes; + while ((nextFrameIndex < stackMapTable.length) + && ((byteCodeOffset - lastFrameByteCodeOffset) + >= (stackMapTable[nextFrameIndex].offsetDelta + + 1))) { + final StackMapTableData nextFrame = stackMapTable[nextFrameIndex]; + + lastFrameByteCodeOffset += nextFrame.offsetDelta + 1; + nextFrame.applyTo(localTypes, stackTypes); + + ++nextFrameIndex; + } + } + + public void advanceTo(final int nextByteCodeOffset) { + advanceBy(nextByteCodeOffset - byteCodeOffset); + } + + private static TypeArray getArgTypes(final String methodSignature, + final boolean isStaticMethod) { + final TypeArray argTypes = new TypeArray(); + + if (!isStaticMethod) { + argTypes.add(ITEM_Object); + } + + if (methodSignature.charAt(0) != '(') { + throw new IllegalArgumentException("Invalid method signature"); + } + + final int length = methodSignature.length(); + int skipType = 0; + int argType; + for (int i = 1; i < length; ++i) { + switch (methodSignature.charAt(i)) { + case 'B': + case 'C': + case 'S': + case 'Z': + case 'I': + argType = ITEM_Integer; + break; + case 'J': + argType = ITEM_Long; + break; + case 'F': + argType = ITEM_Float; + break; + case 'D': + argType = ITEM_Double; + break; + case 'L': { + i = methodSignature.indexOf(';', i + 1); + if (i == -1) { + throw new IllegalArgumentException( + "Invalid method signature"); + } + argType = ITEM_Object; + break; + } + case ')': + // not interested in the return value type + return argTypes; + case '[': + if (skipType == 0) { + argTypes.add(ITEM_Object); + } + ++skipType; + continue; + + default: + throw new IllegalArgumentException( + "Invalid method signature"); + } + + if (skipType == 0) { + argTypes.add(argType); + } else { + --skipType; + } + } + + return argTypes; + } +} diff -r 8da329789435 -r 5876fdbbca11 javap/src/main/java/org/apidesign/javap/StackMapTableData.java --- a/javap/src/main/java/org/apidesign/javap/StackMapTableData.java Thu Dec 13 23:20:47 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/StackMapTableData.java Fri Dec 14 11:32:45 2012 +0100 @@ -32,7 +32,7 @@ /* represents one entry of StackMapTable attribute */ -class StackMapTableData { +abstract class StackMapTableData { final int frameType; int offsetDelta; @@ -40,11 +40,59 @@ this.frameType = frameType; } + abstract void applyTo(TypeArray localTypes, TypeArray stackTypes); + + protected static String toString( + final String frameType, + final int offset, + final int[] localTypes, + final int[] stackTypes) { + final StringBuilder sb = new StringBuilder(frameType); + + sb.append("(off: +").append(offset); + if (localTypes != null) { + sb.append(", locals: "); + appendTypes(sb, localTypes); + } + if (stackTypes != null) { + sb.append(", stack: "); + appendTypes(sb, stackTypes); + } + sb.append(')'); + + return sb.toString(); + } + + private static void appendTypes(final StringBuilder sb, final int[] types) { + sb.append('['); + if (types.length > 0) { + sb.append(TypeArray.typeString(types[0])); + for (int i = 1; i < types.length; ++i) { + sb.append(", "); + sb.append(TypeArray.typeString(types[i])); + } + } + sb.append(']'); + } + static class SameFrame extends StackMapTableData { SameFrame(int frameType, int offsetDelta) { super(frameType); this.offsetDelta = offsetDelta; } + + @Override + void applyTo(TypeArray localTypes, TypeArray stackTypes) { + stackTypes.clear(); + } + + @Override + public String toString() { + return toString("SAME" + ((frameType == SAME_FRAME_EXTENDED) + ? "_FRAME_EXTENDED" : ""), + offsetDelta, + null, null); + } } static class SameLocals1StackItem extends StackMapTableData { @@ -54,6 +102,21 @@ this.offsetDelta = offsetDelta; this.stack = stack; } + + @Override + void applyTo(TypeArray localTypes, TypeArray stackTypes) { + stackTypes.setAll(stack); + } + + @Override + public String toString() { + return toString( + "SAME_LOCALS_1_STACK_ITEM" + + ((frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) + ? "_EXTENDED" : ""), + offsetDelta, + null, stack); + } } static class ChopFrame extends StackMapTableData { @@ -61,6 +124,18 @@ super(frameType); this.offsetDelta = offsetDelta; } + + @Override + void applyTo(TypeArray localTypes, TypeArray stackTypes) { + localTypes.setSize(localTypes.getSize() + - (SAME_FRAME_EXTENDED - frameType)); + stackTypes.clear(); + } + + @Override + public String toString() { + return toString("CHOP", offsetDelta, null, null); + } } static class AppendFrame extends StackMapTableData { @@ -70,6 +145,17 @@ this.offsetDelta = offsetDelta; this.locals = locals; } + + @Override + void applyTo(TypeArray localTypes, TypeArray stackTypes) { + localTypes.addAll(locals); + stackTypes.clear(); + } + + @Override + public String toString() { + return toString("APPEND", offsetDelta, locals, null); + } } static class FullFrame extends StackMapTableData { @@ -81,6 +167,17 @@ this.locals = locals; this.stack = stack; } + + @Override + void applyTo(TypeArray localTypes, TypeArray stackTypes) { + localTypes.setAll(locals); + stackTypes.setAll(stack); + } + + @Override + public String toString() { + return toString("FULL", offsetDelta, locals, stack); + } } static StackMapTableData getInstance(DataInputStream in, MethodData method) diff -r 8da329789435 -r 5876fdbbca11 javap/src/main/java/org/apidesign/javap/TypeArray.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javap/src/main/java/org/apidesign/javap/TypeArray.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.apidesign.javap; + +import static org.apidesign.javap.RuntimeConstants.ITEM_Bogus; +import static org.apidesign.javap.RuntimeConstants.ITEM_Integer; +import static org.apidesign.javap.RuntimeConstants.ITEM_Float; +import static org.apidesign.javap.RuntimeConstants.ITEM_Double; +import static org.apidesign.javap.RuntimeConstants.ITEM_Long; +import static org.apidesign.javap.RuntimeConstants.ITEM_Null; +import static org.apidesign.javap.RuntimeConstants.ITEM_InitObject; +import static org.apidesign.javap.RuntimeConstants.ITEM_Object; +import static org.apidesign.javap.RuntimeConstants.ITEM_NewObject; + +public final class TypeArray { + private static final int CAPACITY_INCREMENT = 16; + + private int[] types; + private int size; + + public TypeArray() { + } + + public TypeArray(final TypeArray initialTypes) { + setAll(initialTypes); + } + + public void add(final int newType) { + ensureCapacity(size + 1); + types[size++] = newType; + } + + public void addAll(final TypeArray newTypes) { + addAll(newTypes.types, 0, newTypes.size); + } + + public void addAll(final int[] newTypes) { + addAll(newTypes, 0, newTypes.length); + } + + public void addAll(final int[] newTypes, + final int offset, + final int count) { + if (count > 0) { + ensureCapacity(size + count); + arraycopy(newTypes, offset, types, size, count); + size += count; + } + } + + public void set(final int index, final int newType) { + types[index] = newType; + } + + public void setAll(final TypeArray newTypes) { + setAll(newTypes.types, 0, newTypes.size); + } + + public void setAll(final int[] newTypes) { + setAll(newTypes, 0, newTypes.length); + } + + public void setAll(final int[] newTypes, + final int offset, + final int count) { + if (count > 0) { + ensureCapacity(count); + arraycopy(newTypes, offset, types, 0, count); + size = count; + } else { + clear(); + } + } + + public void setSize(final int newSize) { + if (size != newSize) { + ensureCapacity(newSize); + + for (int i = size; i < newSize; ++i) { + types[i] = 0; + } + size = newSize; + } + } + + public void clear() { + size = 0; + } + + public int getSize() { + return size; + } + + public int get(final int index) { + return types[index]; + } + + public static String typeString(final int type) { + switch (type & 0xff) { + case ITEM_Bogus: + return "_top_"; + case ITEM_Integer: + return "_int_"; + case ITEM_Float: + return "_float_"; + case ITEM_Double: + return "_double_"; + case ITEM_Long: + return "_long_"; + case ITEM_Null: + return "_null_"; + case ITEM_InitObject: // UninitializedThis + return "_init_"; + case ITEM_Object: + return "_object_"; + case ITEM_NewObject: // Uninitialized + return "_new_"; + default: + throw new IllegalArgumentException("Unknown type"); + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("["); + if (size > 0) { + sb.append(typeString(types[0])); + for (int i = 1; i < size; ++i) { + sb.append(", "); + sb.append(typeString(types[i])); + } + } + + return sb.append(']').toString(); + } + + private void ensureCapacity(final int minCapacity) { + if ((minCapacity == 0) + || (types != null) && (minCapacity <= types.length)) { + return; + } + + final int newCapacity = + ((minCapacity + CAPACITY_INCREMENT - 1) / CAPACITY_INCREMENT) + * CAPACITY_INCREMENT; + final int[] newTypes = new int[newCapacity]; + + if (size > 0) { + arraycopy(types, 0, newTypes, 0, size); + } + + types = newTypes; + } + + // no System.arraycopy + private void arraycopy(final int[] src, final int srcPos, + final int[] dest, final int destPos, + final int length) { + for (int i = 0; i < length; ++i) { + dest[destPos + i] = src[srcPos + i]; + } + } +} diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Thu Dec 13 23:20:47 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Dec 14 11:32:45 2012 +0100 @@ -24,6 +24,7 @@ import org.apidesign.javap.ClassData; import org.apidesign.javap.FieldData; import org.apidesign.javap.MethodData; +import org.apidesign.javap.StackMapIterator; import static org.apidesign.javap.RuntimeConstants.*; /** Translator of the code inside class files to JavaScript. @@ -195,257 +196,438 @@ if (jsb != null) { return jsb; } - StringBuilder argsCnt = new StringBuilder(); - final String mn = findMethodName(m, argsCnt); - out.append(prefix).append(mn).append(" = function"); + final String mn = findMethodName(m, new StringBuilder()); if (mn.equals("class__V")) { toInitilize.add(accessClass(className(jc)) + "(false)." + mn); } - out.append('('); - String space = ""; - for (int index = 0, i = 0; i < argsCnt.length(); i++) { - out.append(space); - out.append("arg").append(String.valueOf(index)); - space = ","; - final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor()); - if (argsCnt.charAt(i) == '1') { - index += 2; - } else { - index++; - } - } - out.append(") {").append("\n"); - final byte[] code = m.getCode(); - if (code != null) { - int len = m.getMaxLocals(); - for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) { - out.append(" var "); - out.append("arg").append(String.valueOf(i)).append(";\n"); - } - out.append(" var s = new Array();\n"); - produceCode(code); - } else { - out.append(" throw 'no code found for ").append(m.getInternalSig()).append("';\n"); - } - out.append("};"); + generateMethod(prefix, mn, m); return mn; } - + private String generateInstanceMethod(String prefix, MethodData m) throws IOException { String jsb = javaScriptBody(prefix, m, false); if (jsb != null) { return jsb; } - StringBuilder argsCnt = new StringBuilder(); - final String mn = findMethodName(m, argsCnt); - out.append(prefix).append(mn).append(" = function"); - out.append("(arg0"); - String space = ","; - for (int index = 1, i = 0; i < argsCnt.length(); i++) { - out.append(space); - out.append("arg").append(String.valueOf(index)); - if (argsCnt.charAt(i) == '1') { - index += 2; - } else { - index++; - } - } - out.append(") {").append("\n"); - final byte[] code = m.getCode(); - if (code != null) { - int len = m.getMaxLocals(); - for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) { - out.append(" var "); - out.append("arg").append(String.valueOf(i + 1)).append(";\n"); - } - out.append(";\n var s = new Array();\n"); - produceCode(code); - } else { - out.append(" throw 'no code found for ").append(m.getInternalSig()).append("';\n"); - } - out.append("};"); + final String mn = findMethodName(m, new StringBuilder()); + generateMethod(prefix, mn, m); return mn; } - private void produceCode(byte[] byteCodes) throws IOException { + private void generateMethod(String prefix, String name, MethodData m) + throws IOException { + final StackMapIterator stackMapIterator = m.createStackMapIterator(); + final LocalsMapper lmapper = + new LocalsMapper(stackMapIterator.getArguments()); + + out.append(prefix).append(name).append(" = function("); + lmapper.outputArguments(out); + out.append(") {").append("\n"); + + final byte[] byteCodes = m.getCode(); + if (byteCodes == null) { + out.append(" throw 'no code found for ") + .append(m.getInternalSig()).append("';\n"); + out.append("};"); + return; + } + + final StackMapper smapper = new StackMapper(); + + final int maxLocals = m.getMaxLocals(); + if (maxLocals > 0) { + // TODO: generate only used local variables + for (int j = 0; j <= VarType.LAST; ++j) { + out.append("\n var ").append(Variable.getLocalVariable(j, 0)); + for (int i = 1; i < maxLocals; ++i) { + out.append(", "); + out.append(Variable.getLocalVariable(j, i)); + } + out.append(';'); + } + } + + // maxStack includes two stack positions for every pushed long / double + // so this might generate more stack variables than we need + final int maxStack = m.getMaxStack(); + if (maxStack > 0) { + // TODO: generate only used stack variables + for (int j = 0; j <= VarType.LAST; ++j) { + out.append("\n var ").append(Variable.getStackVariable(j, 0)); + for (int i = 1; i < maxStack; ++i) { + out.append(", "); + out.append(Variable.getStackVariable(j, i)); + } + out.append(';'); + } + } + + int lastStackFrame = -1; + out.append("\n var gt = 0;\n for(;;) switch(gt) {\n"); for (int i = 0; i < byteCodes.length; i++) { int prev = i; - out.append(" case " + i).append(": "); + stackMapIterator.advanceTo(i); + if (lastStackFrame != stackMapIterator.getFrameIndex()) { + lastStackFrame = stackMapIterator.getFrameIndex(); + lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals()); + smapper.syncWithFrameStack(stackMapIterator.getFrameStack()); + out.append(" case " + i).append(": "); + } else { + out.append(" /* " + i).append(" */ "); + } final int c = readByte(byteCodes, i); switch (c) { case opc_aload_0: + emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(0)); + break; case opc_iload_0: + emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(0)); + break; case opc_lload_0: + emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(0)); + break; case opc_fload_0: + emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(0)); + break; case opc_dload_0: - out.append("s.push(arg0);"); + emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(0)); break; case opc_aload_1: + emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(1)); + break; case opc_iload_1: + emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(1)); + break; case opc_lload_1: + emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(1)); + break; case opc_fload_1: + emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(1)); + break; case opc_dload_1: - out.append("s.push(arg1);"); + emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(1)); break; case opc_aload_2: + emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(2)); + break; case opc_iload_2: + emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(2)); + break; case opc_lload_2: + emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(2)); + break; case opc_fload_2: + emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(2)); + break; case opc_dload_2: - out.append("s.push(arg2);"); + emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(2)); break; case opc_aload_3: + emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(3)); + break; case opc_iload_3: + emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(3)); + break; case opc_lload_3: + emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(3)); + break; case opc_fload_3: + emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(3)); + break; case opc_dload_3: - out.append("s.push(arg3);"); + emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(3)); break; - case opc_iload: - case opc_lload: - case opc_fload: - case opc_dload: + case opc_iload: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(indx)); + break; + } + case opc_lload: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(indx)); + break; + } + case opc_fload: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(indx)); + break; + } + case opc_dload: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(indx)); + break; + } case opc_aload: { final int indx = readByte(byteCodes, ++i); - out.append("s.push(arg").append(indx + ");"); + emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(indx)); break; } - case opc_istore: - case opc_lstore: - case opc_fstore: - case opc_dstore: + case opc_istore: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", lmapper.setI(indx), smapper.popI()); + break; + } + case opc_lstore: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", lmapper.setL(indx), smapper.popL()); + break; + } + case opc_fstore: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", lmapper.setF(indx), smapper.popF()); + break; + } + case opc_dstore: { + final int indx = readByte(byteCodes, ++i); + emit(out, "@1 = @2;", lmapper.setD(indx), smapper.popD()); + break; + } case opc_astore: { final int indx = readByte(byteCodes, ++i); - out.append("arg" + indx).append(" = s.pop();"); + emit(out, "@1 = @2;", lmapper.setA(indx), smapper.popA()); break; } case opc_astore_0: + emit(out, "@1 = @2;", lmapper.setA(0), smapper.popA()); + break; case opc_istore_0: + emit(out, "@1 = @2;", lmapper.setI(0), smapper.popI()); + break; case opc_lstore_0: + emit(out, "@1 = @2;", lmapper.setL(0), smapper.popL()); + break; case opc_fstore_0: + emit(out, "@1 = @2;", lmapper.setF(0), smapper.popF()); + break; case opc_dstore_0: - out.append("arg0 = s.pop();"); + emit(out, "@1 = @2;", lmapper.setD(0), smapper.popD()); break; case opc_astore_1: + emit(out, "@1 = @2;", lmapper.setA(1), smapper.popA()); + break; case opc_istore_1: + emit(out, "@1 = @2;", lmapper.setI(1), smapper.popI()); + break; case opc_lstore_1: + emit(out, "@1 = @2;", lmapper.setL(1), smapper.popL()); + break; case opc_fstore_1: + emit(out, "@1 = @2;", lmapper.setF(1), smapper.popF()); + break; case opc_dstore_1: - out.append("arg1 = s.pop();"); + emit(out, "@1 = @2;", lmapper.setD(1), smapper.popD()); break; case opc_astore_2: + emit(out, "@1 = @2;", lmapper.setA(2), smapper.popA()); + break; case opc_istore_2: + emit(out, "@1 = @2;", lmapper.setI(2), smapper.popI()); + break; case opc_lstore_2: + emit(out, "@1 = @2;", lmapper.setL(2), smapper.popL()); + break; case opc_fstore_2: + emit(out, "@1 = @2;", lmapper.setF(2), smapper.popF()); + break; case opc_dstore_2: - out.append("arg2 = s.pop();"); + emit(out, "@1 = @2;", lmapper.setD(2), smapper.popD()); break; case opc_astore_3: + emit(out, "@1 = @2;", lmapper.setA(3), smapper.popA()); + break; case opc_istore_3: + emit(out, "@1 = @2;", lmapper.setI(3), smapper.popI()); + break; case opc_lstore_3: + emit(out, "@1 = @2;", lmapper.setL(3), smapper.popL()); + break; case opc_fstore_3: + emit(out, "@1 = @2;", lmapper.setF(3), smapper.popF()); + break; case opc_dstore_3: - out.append("arg3 = s.pop();"); + emit(out, "@1 = @2;", lmapper.setD(3), smapper.popD()); break; case opc_iadd: + emit(out, "@1 += @2;", smapper.getI(1), smapper.popI()); + break; case opc_ladd: + emit(out, "@1 += @2;", smapper.getL(1), smapper.popL()); + break; case opc_fadd: + emit(out, "@1 += @2;", smapper.getF(1), smapper.popF()); + break; case opc_dadd: - out.append("s.push(s.pop() + s.pop());"); + emit(out, "@1 += @2;", smapper.getD(1), smapper.popD()); break; case opc_isub: + emit(out, "@1 -= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lsub: + emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL()); + break; case opc_fsub: + emit(out, "@1 -= @2;", smapper.getF(1), smapper.popF()); + break; case opc_dsub: - out.append("{ var tmp = s.pop(); s.push(s.pop() - tmp); }"); + emit(out, "@1 -= @2;", smapper.getD(1), smapper.popD()); break; case opc_imul: + emit(out, "@1 *= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lmul: + emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL()); + break; case opc_fmul: + emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF()); + break; case opc_dmul: - out.append("s.push(s.pop() * s.pop());"); + emit(out, "@1 *= @2;", smapper.getD(1), smapper.popD()); break; case opc_idiv: + emit(out, "@1 = Math.floor(@1 / @2);", + smapper.getI(1), smapper.popI()); + break; case opc_ldiv: - out.append("{ var tmp = s.pop(); s.push(Math.floor(s.pop() / tmp)); }"); + emit(out, "@1 = Math.floor(@1 / @2);", + smapper.getL(1), smapper.popL()); break; case opc_fdiv: + emit(out, "@1 /= @2;", smapper.getF(1), smapper.popF()); + break; case opc_ddiv: - out.append("{ var tmp = s.pop(); s.push(s.pop() / tmp); }"); + emit(out, "@1 /= @2;", smapper.getD(1), smapper.popD()); break; case opc_irem: + emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lrem: + emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL()); + break; case opc_frem: + emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF()); + break; case opc_drem: - out.append("{ var d = s.pop(); s.push(s.pop() % d); }"); + emit(out, "@1 %= @2;", smapper.getD(1), smapper.popD()); break; case opc_iand: + emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI()); + break; case opc_land: - out.append("s.push(s.pop() & s.pop());"); + emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL()); break; case opc_ior: + emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lor: - out.append("s.push(s.pop() | s.pop());"); + emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL()); break; case opc_ixor: + emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lxor: - out.append("s.push(s.pop() ^ s.pop());"); + emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL()); break; case opc_ineg: + emit(out, "@1 = -@1;", smapper.getI(0)); + break; case opc_lneg: + emit(out, "@1 = -@1;", smapper.getL(0)); + break; case opc_fneg: + emit(out, "@1 = -@1;", smapper.getF(0)); + break; case opc_dneg: - out.append("s.push(- s.pop());"); + emit(out, "@1 = -@1;", smapper.getD(0)); break; case opc_ishl: + emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lshl: - out.append("{ var v = s.pop(); s.push(s.pop() << v); }"); + emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI()); break; case opc_ishr: + emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lshr: - out.append("{ var v = s.pop(); s.push(s.pop() >> v); }"); + emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI()); break; case opc_iushr: + emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI()); + break; case opc_lushr: - out.append("{ var v = s.pop(); s.push(s.pop() >>> v); }"); + emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI()); break; case opc_iinc: { final int varIndx = readByte(byteCodes, ++i); final int incrBy = byteCodes[++i]; if (incrBy == 1) { - out.append("arg" + varIndx).append("++;"); + emit(out, "@1++;", lmapper.getI(varIndx)); } else { - out.append("arg" + varIndx).append(" += " + incrBy).append(";"); + emit(out, "@1 += @2;", + lmapper.getI(varIndx), + Integer.toString(incrBy)); } break; } case opc_return: - out.append("return;"); + emit(out, "return;"); break; case opc_ireturn: + emit(out, "return @1;", smapper.popI()); + break; case opc_lreturn: + emit(out, "return @1;", smapper.popL()); + break; case opc_freturn: + emit(out, "return @1;", smapper.popF()); + break; case opc_dreturn: + emit(out, "return @1;", smapper.popD()); + break; case opc_areturn: - out.append("return s.pop();"); + emit(out, "return @1;", smapper.popA()); break; case opc_i2l: + emit(out, "@2 = @1;", smapper.popI(), smapper.pushL()); + break; case opc_i2f: + emit(out, "@2 = @1;", smapper.popI(), smapper.pushF()); + break; case opc_i2d: + emit(out, "@2 = @1;", smapper.popI(), smapper.pushD()); + break; case opc_l2i: + emit(out, "@2 = @1;", smapper.popL(), smapper.pushI()); + break; // max int check? case opc_l2f: + emit(out, "@2 = @1;", smapper.popL(), smapper.pushF()); + break; case opc_l2d: + emit(out, "@2 = @1;", smapper.popL(), smapper.pushD()); + break; case opc_f2d: + emit(out, "@2 = @1;", smapper.popF(), smapper.pushD()); + break; case opc_d2f: - out.append("/* number conversion */"); + emit(out, "@2 = @1;", smapper.popD(), smapper.pushF()); break; case opc_f2i: + emit(out, "@2 = Math.floor(@1);", + smapper.popF(), smapper.pushI()); + break; case opc_f2l: + emit(out, "@2 = Math.floor(@1);", + smapper.popF(), smapper.pushL()); + break; case opc_d2i: + emit(out, "@2 = Math.floor(@1);", + smapper.popD(), smapper.pushI()); + break; case opc_d2l: - out.append("s.push(Math.floor(s.pop()));"); + emit(out, "@2 = Math.floor(@1);", + smapper.popD(), smapper.pushL()); break; case opc_i2b: case opc_i2c: @@ -453,40 +635,55 @@ out.append("/* number conversion */"); break; case opc_aconst_null: - out.append("s.push(null);"); + emit(out, "@1 = null;", smapper.pushA()); break; case opc_iconst_m1: - out.append("s.push(-1);"); + emit(out, "@1 = -1;", smapper.pushI()); break; case opc_iconst_0: + emit(out, "@1 = 0;", smapper.pushI()); + break; case opc_dconst_0: + emit(out, "@1 = 0;", smapper.pushD()); + break; case opc_lconst_0: + emit(out, "@1 = 0;", smapper.pushL()); + break; case opc_fconst_0: - out.append("s.push(0);"); + emit(out, "@1 = 0;", smapper.pushF()); break; case opc_iconst_1: + emit(out, "@1 = 1;", smapper.pushI()); + break; case opc_lconst_1: + emit(out, "@1 = 1;", smapper.pushL()); + break; case opc_fconst_1: + emit(out, "@1 = 1;", smapper.pushF()); + break; case opc_dconst_1: - out.append("s.push(1);"); + emit(out, "@1 = 1;", smapper.pushD()); break; case opc_iconst_2: + emit(out, "@1 = 2;", smapper.pushI()); + break; case opc_fconst_2: - out.append("s.push(2);"); + emit(out, "@1 = 2;", smapper.pushF()); break; case opc_iconst_3: - out.append("s.push(3);"); + emit(out, "@1 = 3;", smapper.pushI()); break; case opc_iconst_4: - out.append("s.push(4);"); + emit(out, "@1 = 4;", smapper.pushI()); break; case opc_iconst_5: - out.append("s.push(5);"); + emit(out, "@1 = 5;", smapper.pushI()); break; case opc_ldc: { int indx = readByte(byteCodes, ++i); String v = encodeConstant(indx); - out.append("s.push(").append(v).append(");"); + int type = VarType.fromConstantType(jc.getTag(indx)); + emit(out, "@1 = @2;", smapper.pushT(type), v); break; } case opc_ldc_w: @@ -494,101 +691,115 @@ int indx = readIntArg(byteCodes, i); i += 2; String v = encodeConstant(indx); - out.append("s.push(").append(v).append(");"); + int type = VarType.fromConstantType(jc.getTag(indx)); + emit(out, "@1 = @2;", smapper.pushT(type), v); break; } case opc_lcmp: + emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", + smapper.popL(), smapper.popL(), smapper.pushI()); + break; case opc_fcmpl: case opc_fcmpg: + emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", + smapper.popF(), smapper.popF(), smapper.pushI()); + break; case opc_dcmpl: - case opc_dcmpg: { - out.append("{ var delta = s.pop() - s.pop(); s.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }"); + case opc_dcmpg: + emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", + smapper.popD(), smapper.popD(), smapper.pushI()); break; - } case opc_if_acmpeq: - i = generateIf(byteCodes, i, "==="); + i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(), + "==="); break; case opc_if_acmpne: - i = generateIf(byteCodes, i, "!="); + i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(), + "!="); break; - case opc_if_icmpeq: { - i = generateIf(byteCodes, i, "=="); + case opc_if_icmpeq: + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + "=="); break; - } case opc_ifeq: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() == 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 == 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_ifne: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() != 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 != 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_iflt: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() < 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 < 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_ifle: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() <= 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 <= 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_ifgt: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() > 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 > 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_ifge: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() >= 0) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 >= 0) { gt = @2; continue; }", + smapper.popI(), Integer.toString(indx)); i += 2; break; } case opc_ifnonnull: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() !== null) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 !== null) { gt = @2; continue; }", + smapper.popA(), Integer.toString(indx)); i += 2; break; } case opc_ifnull: { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() === null) { gt = " + indx); - out.append("; continue; }"); + emit(out, "if (@1 === null) { gt = @2; continue; }", + smapper.popA(), Integer.toString(indx)); i += 2; break; } case opc_if_icmpne: - i = generateIf(byteCodes, i, "!="); + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + "!="); break; case opc_if_icmplt: - i = generateIf(byteCodes, i, ">"); + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + "<"); break; case opc_if_icmple: - i = generateIf(byteCodes, i, ">="); + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + "<="); break; case opc_if_icmpgt: - i = generateIf(byteCodes, i, "<"); + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + ">"); break; case opc_if_icmpge: - i = generateIf(byteCodes, i, "<="); + i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(), + ">="); break; case opc_goto: { int indx = i + readIntArg(byteCodes, i); - out.append("gt = " + indx).append("; continue;"); + emit(out, "gt = @1; continue;", Integer.toString(indx)); i += 2; break; } @@ -598,7 +809,7 @@ table += 4; int n = readInt4(byteCodes, table); table += 4; - out.append("switch (s.pop()) {\n"); + out.append("switch (").append(smapper.popI()).append(") {\n"); while (n-- > 0) { int cnstnt = readInt4(byteCodes, table); table += 4; @@ -618,7 +829,7 @@ table += 4; int high = readInt4(byteCodes, table); table += 4; - out.append("switch (s.pop()) {\n"); + out.append("switch (").append(smapper.popI()).append(") {\n"); while (low <= high) { int offset = i + readInt4(byteCodes, table); table += 4; @@ -630,44 +841,45 @@ break; } case opc_invokeinterface: { - i = invokeVirtualMethod(byteCodes, i) + 2; + i = invokeVirtualMethod(byteCodes, i, smapper) + 2; break; } case opc_invokevirtual: - i = invokeVirtualMethod(byteCodes, i); + i = invokeVirtualMethod(byteCodes, i, smapper); break; case opc_invokespecial: - i = invokeStaticMethod(byteCodes, i, false); + i = invokeStaticMethod(byteCodes, i, smapper, false); break; case opc_invokestatic: - i = invokeStaticMethod(byteCodes, i, true); + i = invokeStaticMethod(byteCodes, i, smapper, true); break; case opc_new: { int indx = readIntArg(byteCodes, i); String ci = jc.getClassName(indx); - out.append("s.push(new "); - out.append(accessClass(ci.replace('/','_'))); - out.append("());"); + emit(out, "@1 = new @2;", + smapper.pushA(), accessClass(ci.replace('/', '_'))); addReference(ci); i += 2; break; } - case opc_newarray: { - int type = byteCodes[i++]; - out.append("s.push(new Array(s.pop()).fillNulls());"); + case opc_newarray: + ++i; // skip type of array + emit(out, "@2 = new Array(@1).fillNulls();", + smapper.popI(), smapper.pushA()); break; - } - case opc_anewarray: { + case opc_anewarray: i += 2; // skip type of array - out.append("s.push(new Array(s.pop()).fillNulls());"); + emit(out, "@2 = new Array(@1).fillNulls();", + smapper.popI(), smapper.pushA()); break; - } case opc_multianewarray: { i += 2; int dim = readByte(byteCodes, ++i); - out.append("{ var a0 = new Array(s.pop()).fillNulls();"); + out.append("{ var a0 = new Array(").append(smapper.popI()) + .append(").fillNulls();"); for (int d = 1; d < dim; d++) { - out.append("\n var l" + d).append(" = s.pop();"); + out.append("\n var l" + d).append(" = ") + .append(smapper.popI()).append(';'); out.append("\n for (var i" + d).append (" = 0; i" + d). append(" < a" + (d - 1)). append(".length; i" + d).append("++) {"); @@ -679,77 +891,142 @@ for (int d = 1; d < dim; d++) { out.append("\n }"); } - out.append("\ns.push(a0); }"); + out.append("\n").append(smapper.pushA()).append(" = a0; }"); break; } case opc_arraylength: - out.append("s.push(s.pop().length);"); + emit(out, "@2 = @1.length;", smapper.popA(), smapper.pushI()); + break; + case opc_lastore: + emit(out, "@3[@2] = @1;", + smapper.popL(), smapper.popI(), smapper.popA()); + break; + case opc_fastore: + emit(out, "@3[@2] = @1;", + smapper.popF(), smapper.popI(), smapper.popA()); + break; + case opc_dastore: + emit(out, "@3[@2] = @1;", + smapper.popD(), smapper.popI(), smapper.popA()); + break; + case opc_aastore: + emit(out, "@3[@2] = @1;", + smapper.popA(), smapper.popI(), smapper.popA()); break; case opc_iastore: - case opc_lastore: - case opc_fastore: - case opc_dastore: - case opc_aastore: case opc_bastore: case opc_castore: - case opc_sastore: { - out.append("{ var value = s.pop(); var indx = s.pop(); s.pop()[indx] = value; }"); + case opc_sastore: + emit(out, "@3[@2] = @1;", + smapper.popI(), smapper.popI(), smapper.popA()); + break; + case opc_laload: + emit(out, "@3 = @2[@1];", + smapper.popI(), smapper.popA(), smapper.pushL()); + break; + case opc_faload: + emit(out, "@3 = @2[@1];", + smapper.popI(), smapper.popA(), smapper.pushF()); + break; + case opc_daload: + emit(out, "@3 = @2[@1];", + smapper.popI(), smapper.popA(), smapper.pushD()); + break; + case opc_aaload: + emit(out, "@3 = @2[@1];", + smapper.popI(), smapper.popA(), smapper.pushA()); + break; + case opc_iaload: + case opc_baload: + case opc_caload: + case opc_saload: + emit(out, "@3 = @2[@1];", + smapper.popI(), smapper.popA(), smapper.pushI()); + break; + case opc_pop: + case opc_pop2: + smapper.pop(1); + out.append("/* pop */"); + break; + case opc_dup: { + final Variable v = smapper.get(0); + emit(out, "@1 = @2;", smapper.pushT(v.getType()), v); break; } - case opc_iaload: - case opc_laload: - case opc_faload: - case opc_daload: - case opc_aaload: - case opc_baload: - case opc_caload: - case opc_saload: { - out.append("{ var indx = s.pop(); s.push(s.pop()[indx]); }"); + case opc_dup2: { + if (smapper.get(0).isCategory2()) { + final Variable v = smapper.get(0); + emit(out, "@1 = @2;", smapper.pushT(v.getType()), v); + } else { + final Variable v1 = smapper.get(0); + final Variable v2 = smapper.get(1); + emit(out, "{ @1 = @2; @3 = @4; }", + smapper.pushT(v2.getType()), v2, + smapper.pushT(v1.getType()), v1); + } break; } - case opc_pop2: - out.append("s.pop();"); - case opc_pop: - out.append("s.pop();"); + case opc_dup_x1: { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + final Variable vo3 = smapper.pushT(vi1.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }", + vo1, vi1, vo2, vi2, vo3, vo1); break; - case opc_dup: - out.append("s.push(s[s.length - 1]);"); + } + case opc_dup_x2: { + if (smapper.get(1).isCategory2()) { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + final Variable vo3 = smapper.pushT(vi1.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }", + vo1, vi1, vo2, vi2, vo3, vo1); + } else { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + final Variable vi3 = smapper.pop(); + final Variable vo4 = smapper.pushT(vi1.getType()); + final Variable vo3 = smapper.pushT(vi3.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "{ @1 = @2; @3 = @4; @5 = @6; @7 = @8; }", + vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1); + } break; - case opc_dup_x1: - out.append("{ var v1 = s.pop(); var v2 = s.pop(); s.push(v1); s.push(v2); s.push(v1); }"); - break; - case opc_dup_x2: - 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); }"); - break; + } case opc_bipush: - out.append("s.push(" + byteCodes[++i] + ");"); + emit(out, "@1 = @2;", + smapper.pushI(), Integer.toString(byteCodes[++i])); break; case opc_sipush: - out.append("s.push(" + readIntArg(byteCodes, i) + ");"); + emit(out, "@1 = @2;", + smapper.pushI(), + Integer.toString(readIntArg(byteCodes, i))); i += 2; break; case opc_getfield: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); - out.append("s.push(s.pop().fld_"). - append(fi[1]).append(");"); + final int type = VarType.fromFieldType(fi[2].charAt(0)); + emit(out, "@2 = @1.fld_@3;", + smapper.popA(), smapper.pushT(type), fi[1]); i += 2; break; } case opc_getstatic: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); - out.append("s.push(").append(accessClass(fi[0].replace('/', '_'))); - out.append('.').append(fi[1]).append(");"); - i += 2; - addReference(fi[0]); - break; - } - case opc_putstatic: { - int indx = readIntArg(byteCodes, i); - String[] fi = jc.getFieldInfoName(indx); - out.append(accessClass(fi[0].replace('/', '_'))); - out.append('.').append(fi[1]).append(" = s.pop();"); + final int type = VarType.fromFieldType(fi[2].charAt(0)); + emit(out, "@1 = @2.@3;", + smapper.pushT(type), + accessClass(fi[0].replace('/', '_')), fi[1]); i += 2; addReference(fi[0]); break; @@ -757,19 +1034,32 @@ case opc_putfield: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); - out.append("{ var v = s.pop(); s.pop().fld_") - .append(fi[1]).append(" = v; }"); + final int type = VarType.fromFieldType(fi[2].charAt(0)); + emit(out, "@2.fld_@3 = @1;", + smapper.popT(type), smapper.popA(), fi[1]); i += 2; break; } + case opc_putstatic: { + int indx = readIntArg(byteCodes, i); + String[] fi = jc.getFieldInfoName(indx); + final int type = VarType.fromFieldType(fi[2].charAt(0)); + emit(out, "@1.@2 = @3;", + accessClass(fi[0].replace('/', '_')), fi[1], + smapper.popT(type)); + i += 2; + addReference(fi[0]); + break; + } case opc_checkcast: { int indx = readIntArg(byteCodes, i); final String type = jc.getClassName(indx); if (!type.startsWith("[")) { // no way to check arrays right now - out.append("if(s[s.length - 1] !== null && !s[s.length - 1].$instOf_") - .append(type.replace('/', '_')) - .append(") throw {};"); // XXX proper exception + // XXX proper exception + emit(out, + "if (@1 !== null && !@1.$instOf_@2) throw {};", + smapper.getA(0), type.replace('/', '_')); } i += 2; break; @@ -777,20 +1067,37 @@ case opc_instanceof: { int indx = readIntArg(byteCodes, i); final String type = jc.getClassName(indx); - out.append("s.push(s.pop().$instOf_") - .append(type.replace('/', '_')) - .append(" ? 1 : 0);"); + emit(out, "@2 = @1.$instOf_@3 ? 1 : 0;", + smapper.popA(), smapper.pushI(), + type.replace('/', '_')); i += 2; break; } case opc_athrow: { - out.append("{ var t = s.pop(); s = new Array(1); s[0] = t; throw t; }"); + final Variable v = smapper.popA(); + smapper.clear(); + + emit(out, "{ @1 = @2; throw @2; }", + smapper.pushA(), v); break; } + + case opc_monitorenter: { + out.append("/* monitor enter */"); + smapper.popA(); + break; + } + + case opc_monitorexit: { + out.append("/* monitor exit */"); + smapper.popA(); + break; + } + default: { - out.append("throw 'unknown bytecode " + c + "';"); + emit(out, "throw 'unknown bytecode @1';", + Integer.toString(c)); } - } out.append(" //"); for (int j = prev; j <= i; j++) { @@ -801,12 +1108,17 @@ out.append("\n"); } out.append(" }\n"); + out.append("};"); } - private int generateIf(byte[] byteCodes, int i, final String test) throws IOException { + private int generateIf(byte[] byteCodes, int i, + final Variable v2, final Variable v1, + final String test) throws IOException { int indx = i + readIntArg(byteCodes, i); - out.append("if (s.pop() ").append(test).append(" s.pop()) { gt = " + indx); - out.append("; continue; }"); + out.append("if (").append(v1) + .append(' ').append(test).append(' ') + .append(v2).append(") { gt = " + indx) + .append("; continue; }"); return i + 2; } @@ -823,10 +1135,10 @@ return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff); } private int readByte(byte[] byteCodes, int offsetInstruction) { - return (byteCodes[offsetInstruction] + 256) % 256; + return byteCodes[offsetInstruction] & 0xff; } - private static void countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig, StringBuilder cnt) { + private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) { int i = 0; Boolean count = null; boolean array = false; @@ -860,17 +1172,19 @@ cnt.append('0'); } } else { - hasReturnType[0] = true; sig.insert(firstPos, ch); if (array) { + returnType[0] = '['; sig.insert(firstPos, "_3"); + } else { + returnType[0] = ch; } } array = false; continue; case 'V': assert !count; - hasReturnType[0] = false; + returnType[0] = 'V'; sig.insert(firstPos, 'V'); continue; case 'L': @@ -887,7 +1201,7 @@ if (array) { sig.insert(firstPos, "_3"); } - hasReturnType[0] = true; + returnType[0] = 'L'; } i = next + 1; continue; @@ -925,12 +1239,11 @@ name.append(m.getName()); } - boolean hasReturn[] = { false }; - countArgs(m.getInternalSig(), hasReturn, name, cnt); + countArgs(m.getInternalSig(), new char[1], name, cnt); return name.toString(); } - static String findMethodName(String[] mi, StringBuilder cnt, boolean[] hasReturn) { + static String findMethodName(String[] mi, StringBuilder cnt, char[] returnType) { StringBuilder name = new StringBuilder(); String descr = mi[2];//mi.getDescriptor(); String nm= mi[1]; @@ -939,81 +1252,80 @@ } else { name.append(nm); } - countArgs(descr, hasReturn, name, cnt); + countArgs(descr, returnType, name, cnt); return name.toString(); } - private int invokeStaticMethod(byte[] byteCodes, int i, boolean isStatic) + private int invokeStaticMethod(byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic) throws IOException { int methodIndex = readIntArg(byteCodes, i); String[] mi = jc.getFieldInfoName(methodIndex); - boolean[] hasReturn = { false }; + char[] returnType = { 'V' }; StringBuilder cnt = new StringBuilder(); - String mn = findMethodName(mi, cnt, hasReturn); - out.append("{ "); - for (int j = cnt.length() - 1; j >= 0; j--) { - out.append("var v" + j).append(" = s.pop(); "); + String mn = findMethodName(mi, cnt, returnType); + + final int numArguments = isStatic ? cnt.length() : cnt.length() + 1; + final Variable[] vars = new Variable[numArguments]; + + for (int j = numArguments - 1; j >= 0; --j) { + vars[j] = mapper.pop(); } - - if (hasReturn[0]) { - out.append("s.push("); + + if (returnType[0] != 'V') { + out.append(mapper.pushT(VarType.fromFieldType(returnType[0]))) + .append(" = "); } + final String in = mi[0]; out.append(accessClass(in.replace('/', '_'))); out.append("(false)."); out.append(mn); out.append('('); - String sep = ""; - if (!isStatic) { - out.append("s.pop()"); - sep = ", "; + if (numArguments > 0) { + out.append(vars[0]); + for (int j = 1; j < numArguments; ++j) { + out.append(", "); + out.append(vars[j]); + } } - for (int j = 0; j < cnt.length(); j++) { - out.append(sep); - out.append("v" + j); - sep = ", "; - } - out.append(")"); - if (hasReturn[0]) { - out.append(")"); - } - out.append("; }"); + out.append(");"); i += 2; addReference(in); return i; } - private int invokeVirtualMethod(byte[] byteCodes, int i) + private int invokeVirtualMethod(byte[] byteCodes, int i, final StackMapper mapper) throws IOException { int methodIndex = readIntArg(byteCodes, i); String[] mi = jc.getFieldInfoName(methodIndex); - boolean[] hasReturn = { false }; + char[] returnType = { 'V' }; StringBuilder cnt = new StringBuilder(); - String mn = findMethodName(mi, cnt, hasReturn); - out.append("{ "); - for (int j = cnt.length() - 1; j >= 0; j--) { - out.append("var v" + j).append(" = s.pop(); "); + String mn = findMethodName(mi, cnt, returnType); + + final int numArguments = cnt.length() + 1; + final Variable[] vars = new Variable[numArguments]; + + for (int j = numArguments - 1; j >= 0; --j) { + vars[j] = mapper.pop(); } - out.append("var self = s.pop(); "); - if (hasReturn[0]) { - out.append("s.push("); + + if (returnType[0] != 'V') { + out.append(mapper.pushT(VarType.fromFieldType(returnType[0]))) + .append(" = "); } - out.append("self."); + + out.append(vars[0]).append('.'); out.append(mn); out.append('('); - out.append("self"); - for (int j = 0; j < cnt.length(); j++) { + out.append(vars[0]); + for (int j = 1; j < numArguments; ++j) { out.append(", "); - out.append("v" + j); + out.append(vars[j]); } - out.append(")"); - if (hasReturn[0]) { - out.append(")"); - } - out.append("; }"); + out.append(");"); i += 2; return i; } - + private void addReference(String cn) throws IOException { if (requireReference(cn)) { out.append(" /* needs ").append(cn).append(" */"); @@ -1197,4 +1509,29 @@ out.append(name); return ","; } + + private static void emit(final Appendable out, + final String format, + final CharSequence... params) throws IOException { + final int length = format.length(); + + int processed = 0; + int paramOffset = format.indexOf('@'); + while ((paramOffset != -1) && (paramOffset < (length - 1))) { + final char paramChar = format.charAt(paramOffset + 1); + if ((paramChar >= '1') && (paramChar <= '9')) { + final int paramIndex = paramChar - '0' - 1; + + out.append(format, processed, paramOffset); + out.append(params[paramIndex]); + + ++paramOffset; + processed = paramOffset + 1; + } + + paramOffset = format.indexOf('@', paramOffset + 1); + } + + out.append(format, processed, length); + } } diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,141 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import org.apidesign.javap.RuntimeConstants; +import org.apidesign.javap.TypeArray; + +final class LocalsMapper { + private final TypeArray argTypeRecords; + private final TypeArray localTypeRecords; + + public LocalsMapper(final TypeArray stackMapArgs) { + final TypeArray initTypeRecords = new TypeArray(); + updateRecords(initTypeRecords, stackMapArgs); + + argTypeRecords = initTypeRecords; + localTypeRecords = new TypeArray(initTypeRecords); + } + + public void outputArguments(final Appendable out) throws IOException { + final int argRecordCount = argTypeRecords.getSize(); + if (argRecordCount > 0) { + Variable variable = getVariable(argTypeRecords, 0); + out.append(variable); + + int i = variable.isCategory2() ? 2 : 1; + while (i < argRecordCount) { + variable = getVariable(argTypeRecords, i); + out.append(", "); + out.append(variable); + i += variable.isCategory2() ? 2 : 1; + } + } + } + + public void syncWithFrameLocals(final TypeArray frameLocals) { + updateRecords(localTypeRecords, frameLocals); + } + + public Variable setI(final int index) { + return setT(index, VarType.INTEGER); + } + + public Variable setL(final int index) { + return setT(index, VarType.LONG); + } + + public Variable setF(final int index) { + return setT(index, VarType.FLOAT); + } + + public Variable setD(final int index) { + return setT(index, VarType.DOUBLE); + } + + public Variable setA(final int index) { + return setT(index, VarType.REFERENCE); + } + + public Variable setT(final int index, final int type) { + updateRecord(localTypeRecords, index, type); + return Variable.getLocalVariable(type, index); + } + + public Variable getI(final int index) { + return getT(index, VarType.INTEGER); + } + + public Variable getL(final int index) { + return getT(index, VarType.LONG); + } + + public Variable getF(final int index) { + return getT(index, VarType.FLOAT); + } + + public Variable getD(final int index) { + return getT(index, VarType.DOUBLE); + } + + public Variable getA(final int index) { + return getT(index, VarType.REFERENCE); + } + + public Variable getT(final int index, final int type) { + final int oldRecordValue = localTypeRecords.get(index); + if ((oldRecordValue & 0xff) != type) { + throw new IllegalStateException("Type mismatch"); + } + + return Variable.getLocalVariable(type, index); + } + + private static void updateRecords(final TypeArray typeRecords, + final TypeArray stackMapTypes) { + final int srcSize = stackMapTypes.getSize(); + for (int i = 0, dstIndex = 0; i < srcSize; ++i) { + final int smType = stackMapTypes.get(i); + if (smType == RuntimeConstants.ITEM_Bogus) { + ++dstIndex; + continue; + } + final int varType = VarType.fromStackMapType(smType); + updateRecord(typeRecords, dstIndex, varType); + dstIndex += VarType.isCategory2(varType) ? 2 : 1; + } + } + + private static void updateRecord(final TypeArray typeRecords, + final int index, final int type) { + if (typeRecords.getSize() < (index + 1)) { + typeRecords.setSize(index + 1); + } + + final int oldRecordValue = typeRecords.get(index); + final int usedTypesMask = + (oldRecordValue >> 8) | (1 << type); + typeRecords.set(index, (usedTypesMask << 8) | type); + } + + private static Variable getVariable(final TypeArray typeRecords, + final int index) { + return Variable.getLocalVariable(typeRecords.get(index) & 0xff, index); + } +} diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Thu Dec 13 23:20:47 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Fri Dec 14 11:32:45 2012 +0100 @@ -40,7 +40,11 @@ Writer w = new BufferedWriter(new FileWriter(args[0])); StringArray classes = StringArray.asList(args); classes.delete(0); - Bck2Brwsr.generate(w, Main.class.getClassLoader(), classes.toArray()); - w.close(); + try { + Bck2Brwsr.generate(w, Main.class.getClassLoader(), + classes.toArray()); + } finally { + w.close(); + } } } diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/StackMapper.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,194 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import org.apidesign.javap.TypeArray; + +final class StackMapper { + private final TypeArray stackTypeIndexPairs; + private int[] typeCounters; + private int[] typeMaxCounters; + + public StackMapper() { + stackTypeIndexPairs = new TypeArray(); + typeCounters = new int[VarType.LAST + 1]; + typeMaxCounters = new int[VarType.LAST + 1]; + } + + public void clear() { + for (int type = 0; type <= VarType.LAST; ++type) { + typeCounters[type] = 0; + } + stackTypeIndexPairs.clear(); + } + + public void syncWithFrameStack(final TypeArray frameStack) { + clear(); + + final int size = frameStack.getSize(); + for (int i = 0; i < size; ++i) { + pushTypeImpl(VarType.fromStackMapType(frameStack.get(i))); + } + } + + public Variable pushI() { + return pushT(VarType.INTEGER); + } + + public Variable pushL() { + return pushT(VarType.LONG); + } + + public Variable pushF() { + return pushT(VarType.FLOAT); + } + + public Variable pushD() { + return pushT(VarType.DOUBLE); + } + + public Variable pushA() { + return pushT(VarType.REFERENCE); + } + + public Variable pushT(final int type) { + return getVariable(pushTypeImpl(type)); + } + + public Variable popI() { + return popT(VarType.INTEGER); + } + + public Variable popL() { + return popT(VarType.LONG); + } + + public Variable popF() { + return popT(VarType.FLOAT); + } + + public Variable popD() { + return popT(VarType.DOUBLE); + } + + public Variable popA() { + return popT(VarType.REFERENCE); + } + + public Variable popT(final int type) { + final Variable variable = getT(0, type); + popImpl(1); + return variable; + } + + public Variable pop() { + final Variable variable = get(0); + popImpl(1); + return variable; + } + + public void pop(final int count) { + final int stackSize = stackTypeIndexPairs.getSize(); + if (count > stackSize) { + throw new IllegalStateException("Stack underflow"); + } + popImpl(count); + } + + public Variable getI(final int indexFromTop) { + return getT(indexFromTop, VarType.INTEGER); + } + + public Variable getL(final int indexFromTop) { + return getT(indexFromTop, VarType.LONG); + } + + public Variable getF(final int indexFromTop) { + return getT(indexFromTop, VarType.FLOAT); + } + + public Variable getD(final int indexFromTop) { + return getT(indexFromTop, VarType.DOUBLE); + } + + public Variable getA(final int indexFromTop) { + return getT(indexFromTop, VarType.REFERENCE); + } + + public Variable getT(final int indexFromTop, final int type) { + final int stackSize = stackTypeIndexPairs.getSize(); + if (indexFromTop >= stackSize) { + throw new IllegalStateException("Stack underflow"); + } + final int stackValue = + stackTypeIndexPairs.get(stackSize - indexFromTop - 1); + if ((stackValue & 0xff) != type) { + throw new IllegalStateException("Type mismatch"); + } + + return getVariable(stackValue); + } + + public Variable get(final int indexFromTop) { + final int stackSize = stackTypeIndexPairs.getSize(); + if (indexFromTop >= stackSize) { + throw new IllegalStateException("Stack underflow"); + } + final int stackValue = + stackTypeIndexPairs.get(stackSize - indexFromTop - 1); + + return getVariable(stackValue); + } + + private int pushTypeImpl(final int type) { + final int count = typeCounters[type]; + final int value = (count << 8) | (type & 0xff); + incCounter(type); + stackTypeIndexPairs.add(value); + + return value; + } + + private void popImpl(final int count) { + final int stackSize = stackTypeIndexPairs.getSize(); + for (int i = stackSize - count; i < stackSize; ++i) { + final int value = stackTypeIndexPairs.get(i); + decCounter(value & 0xff); + } + + stackTypeIndexPairs.setSize(stackSize - count); + } + + private void incCounter(final int type) { + final int newValue = ++typeCounters[type]; + if (typeMaxCounters[type] < newValue) { + typeMaxCounters[type] = newValue; + } + } + + private void decCounter(final int type) { + --typeCounters[type]; + } + + public Variable getVariable(final int typeAndIndex) { + final int type = typeAndIndex & 0xff; + final int index = typeAndIndex >> 8; + + return Variable.getStackVariable(type, index); + } +} diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/VarType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VarType.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,110 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import org.apidesign.javap.RuntimeConstants; + +final class VarType { + public static final int INTEGER = 0; + public static final int LONG = 1; + public static final int FLOAT = 2; + public static final int DOUBLE = 3; + public static final int REFERENCE = 4; + + public static final int LAST = REFERENCE; + + private VarType() { + } + + public static boolean isCategory2(final int varType) { + return (varType == LONG) || (varType == DOUBLE); + } + + public static int fromStackMapType(final int smType) { + switch (smType & 0xff) { + case RuntimeConstants.ITEM_Integer: + return VarType.INTEGER; + case RuntimeConstants.ITEM_Float: + return VarType.FLOAT; + case RuntimeConstants.ITEM_Double: + return VarType.DOUBLE; + case RuntimeConstants.ITEM_Long: + return VarType.LONG; + case RuntimeConstants.ITEM_Object: + return VarType.REFERENCE; + + case RuntimeConstants.ITEM_Bogus: + case RuntimeConstants.ITEM_Null: + case RuntimeConstants.ITEM_InitObject: + case RuntimeConstants.ITEM_NewObject: + /* unclear how to handle for now */ + default: + throw new IllegalStateException("Unhandled stack map type"); + } + } + + public static int fromConstantType(final byte constantTag) { + switch (constantTag) { + case RuntimeConstants.CONSTANT_INTEGER: + return VarType.INTEGER; + case RuntimeConstants.CONSTANT_FLOAT: + return VarType.FLOAT; + case RuntimeConstants.CONSTANT_LONG: + return VarType.LONG; + case RuntimeConstants.CONSTANT_DOUBLE: + return VarType.DOUBLE; + + case RuntimeConstants.CONSTANT_CLASS: + case RuntimeConstants.CONSTANT_UTF8: + case RuntimeConstants.CONSTANT_UNICODE: + case RuntimeConstants.CONSTANT_STRING: + return VarType.REFERENCE; + + case RuntimeConstants.CONSTANT_FIELD: + case RuntimeConstants.CONSTANT_METHOD: + case RuntimeConstants.CONSTANT_INTERFACEMETHOD: + case RuntimeConstants.CONSTANT_NAMEANDTYPE: + /* unclear how to handle for now */ + default: + throw new IllegalStateException("Unhandled constant tag"); + } + } + + public static int fromFieldType(final char fieldType) { + switch (fieldType) { + case 'B': + case 'C': + case 'S': + case 'Z': + case 'I': + return VarType.INTEGER; + case 'J': + return VarType.LONG; + case 'F': + return VarType.FLOAT; + case 'D': + return VarType.DOUBLE; + case 'L': + case '[': + return VarType.REFERENCE; + + default: + throw new IllegalStateException("Unhandled field type"); + } + } +} diff -r 8da329789435 -r 5876fdbbca11 vm/src/main/java/org/apidesign/vm4brwsr/Variable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Variable.java Fri Dec 14 11:32:45 2012 +0100 @@ -0,0 +1,100 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +final class Variable implements CharSequence { + private static final String STACK_VAR_PREFIX = "st"; + private static final String LOCAL_VAR_PREFIX = "lc"; + + private final String name; + private final int type; + private final int index; + + private static final char[] TYPE_IDS = { 'I', 'L', 'F', 'D', 'A' }; + + private Variable(final String prefix, final int type, final int index) { + this.name = prefix + TYPE_IDS[type] + index; + this.type = type; + this.index = index; + } + + public static Variable getStackVariable( + final int type, final int index) { + // TODO: precreate frequently used variables + return new Variable(STACK_VAR_PREFIX, type, index); + } + + public static Variable getLocalVariable( + final int type, final int index) { + // TODO: precreate frequently used variables + return new Variable(LOCAL_VAR_PREFIX, type, index); + } + + public String getName() { + return name; + } + + public int getType() { + return type; + } + + public int getIndex() { + return index; + } + + public boolean isCategory2() { + return VarType.isCategory2(type); + } + + @Override + public int length() { + return name.length(); + } + + @Override + public char charAt(final int index) { + return name.charAt(index); + } + + @Override + public CharSequence subSequence(final int start, final int end) { + return name.subSequence(start, end); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Variable)) { + return false; + } + + return name.equals(((Variable) other).name); + } + + @Override + public String toString() { + return name; + } +} diff -r 8da329789435 -r 5876fdbbca11 vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java Thu Dec 13 23:20:47 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ByteCodeToJavaScriptTest.java Fri Dec 14 11:32:45 2012 +0100 @@ -32,24 +32,24 @@ @Test public void findMethodNameManglesObjectsCorrectly() { StringBuilder cnt = new StringBuilder(); - boolean[] hasReturn = { false }; + char[] returnType = { 'V' }; String ret = ByteCodeToJavaScript.findMethodName(new String[] { "StringTest", "replace", "(Ljava/lang/String;CC)Ljava/lang/String;" - }, cnt, hasReturn); + }, cnt, returnType); assertEquals(cnt.toString(), "000", "No doubles or longs"); - assertTrue(hasReturn[0], "Returns string"); + assertTrue(returnType[0] != 'V', "Returns string"); assertEquals(ret, "replace__Ljava_lang_String_2Ljava_lang_String_2CC"); } @Test public void manglingArrays() { StringBuilder cnt = new StringBuilder(); - boolean[] hasReturn = { false }; + char[] returnType = { 'V' }; String ret = ByteCodeToJavaScript.findMethodName(new String[] { "VMinVM", "toJavaScript", "([B)Ljava/lang/String;" - }, cnt, hasReturn); + }, cnt, returnType); assertEquals(cnt.toString(), "0", "No doubles or longs"); - assertTrue(hasReturn[0], "Returns string"); + assertTrue(returnType[0] != 'V', "Returns string"); assertEquals(ret, "toJavaScript__Ljava_lang_String_2_3B"); } }