Improve WasmCodeBuilder for later use with WatParser.

This commit is contained in:
Volker Berlin 2018-11-09 20:14:30 +01:00
parent db4a6e19bd
commit 4fd7d44263
3 changed files with 263 additions and 153 deletions

View File

@ -16,10 +16,8 @@
package de.inetsoftware.jwebassembly.module; package de.inetsoftware.jwebassembly.module;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import de.inetsoftware.classparser.Code; import de.inetsoftware.classparser.Code;
@ -35,26 +33,7 @@ import de.inetsoftware.jwebassembly.WasmException;
*/ */
class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
private LocaleVariableManager localVariables = new LocaleVariableManager(); private BranchManger branchManager = new BranchManger( getInstructions() );
private final List<WasmInstruction> instructions = new ArrayList<>();
private BranchManger branchManager = new BranchManger( instructions );
/**
* {@inheritDoc}
*/
List<WasmInstruction> getInstructions() {
return instructions;
}
/**
* {@inheritDoc}
*/
@Override
List<ValueType> getLocalTypes( int paramCount ) {
return localVariables.getLocalTypes( paramCount );
}
/** /**
* Build the wasm instructions * Build the wasm instructions
@ -69,12 +48,12 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
void buildCode( @Nonnull Code code, boolean hasReturn ) { void buildCode( @Nonnull Code code, boolean hasReturn ) {
CodeInputStream byteCode = null; CodeInputStream byteCode = null;
try { try {
localVariables.reset(); reset();
branchManager.reset( code.getExceptionTable() ); branchManager.reset( code.getExceptionTable() );
byteCode = code.getByteCode(); byteCode = code.getByteCode();
writeCode( byteCode, code.getConstantPool(), hasReturn ); writeCode( byteCode, code.getConstantPool(), hasReturn );
localVariables.calculate(); calculateVariables();
} catch( Exception ioex ) { } catch( Exception ioex ) {
int lineNumber = byteCode == null ? -1 : byteCode.getLineNumber(); int lineNumber = byteCode == null ? -1 : byteCode.getLineNumber();
throw WasmException.create( ioex, lineNumber ); throw WasmException.create( ioex, lineNumber );
@ -95,11 +74,9 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
* if some Java code can't converted * if some Java code can't converted
*/ */
private void writeCode( CodeInputStream byteCode, ConstantPool constantPool, boolean hasReturn ) throws WasmException { private void writeCode( CodeInputStream byteCode, ConstantPool constantPool, boolean hasReturn ) throws WasmException {
instructions.clear();
boolean endWithReturn = false; boolean endWithReturn = false;
try { try {
while( byteCode.available() > 0 ) { while( byteCode.available() > 0 ) {
WasmInstruction instr = null;
int codePos = byteCode.getCodePosition(); int codePos = byteCode.getCodePosition();
endWithReturn = false; endWithReturn = false;
int op = byteCode.readUnsignedByte(); int op = byteCode.readUnsignedByte();
@ -114,70 +91,70 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 6: // iconst_3 case 6: // iconst_3
case 7: // iconst_4 case 7: // iconst_4
case 8: // iconst_5 case 8: // iconst_5
instr = new WasmConstInstruction( Integer.valueOf( op - 3 ), ValueType.i32, codePos ); addConstInstruction( Integer.valueOf( op - 3 ), ValueType.i32, codePos );
break; break;
case 9: // lconst_0 case 9: // lconst_0
case 10: // lconst_1 case 10: // lconst_1
instr = new WasmConstInstruction( Long.valueOf( op - 9 ), ValueType.i64, codePos ); addConstInstruction( Long.valueOf( op - 9 ), ValueType.i64, codePos );
break; break;
case 11: // fconst_0 case 11: // fconst_0
case 12: // fconst_1 case 12: // fconst_1
case 13: // fconst_2 case 13: // fconst_2
instr = new WasmConstInstruction( Float.valueOf( op - 11 ), ValueType.f32, codePos ); addConstInstruction( Float.valueOf( op - 11 ), ValueType.f32, codePos );
break; break;
case 14: // dconst_0 case 14: // dconst_0
case 15: // dconst_1 case 15: // dconst_1
instr = new WasmConstInstruction( Double.valueOf( op - 14 ), ValueType.f64, codePos ); addConstInstruction( Double.valueOf( op - 14 ), ValueType.f64, codePos );
break; break;
case 16: // bipush case 16: // bipush
instr = new WasmConstInstruction( Integer.valueOf( byteCode.readByte() ), ValueType.i32, codePos ); addConstInstruction( Integer.valueOf( byteCode.readByte() ), ValueType.i32, codePos );
break; break;
case 17: // sipush case 17: // sipush
instr = new WasmConstInstruction( Integer.valueOf( byteCode.readShort() ), ValueType.i32, codePos ); addConstInstruction( Integer.valueOf( byteCode.readShort() ), ValueType.i32, codePos );
break; break;
case 18: // ldc case 18: // ldc
instr = new WasmConstInstruction( (Number)constantPool.get( byteCode.readUnsignedByte() ), codePos ); addConstInstruction( (Number)constantPool.get( byteCode.readUnsignedByte() ), codePos );
break; break;
case 19: // ldc_w case 19: // ldc_w
case 20: // ldc2_w case 20: // ldc2_w
instr = new WasmConstInstruction( (Number)constantPool.get( byteCode.readUnsignedShort() ), codePos ); addConstInstruction( (Number)constantPool.get( byteCode.readUnsignedShort() ), codePos );
break; break;
case 21: // iload case 21: // iload
instr = loadStore( ValueType.i32, true, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.i32, true, byteCode.readUnsignedByte(), codePos );
break; break;
case 22: // lload case 22: // lload
instr = loadStore( ValueType.i64, true, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.i64, true, byteCode.readUnsignedByte(), codePos );
break; break;
case 23: // fload case 23: // fload
instr = loadStore( ValueType.f32, true, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.f32, true, byteCode.readUnsignedByte(), codePos );
break; break;
case 24: // dload case 24: // dload
instr = loadStore( ValueType.f64, true, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.f64, true, byteCode.readUnsignedByte(), codePos );
break; break;
//TODO case 25: // aload //TODO case 25: // aload
case 26: // iload_0 case 26: // iload_0
case 27: // iload_1 case 27: // iload_1
case 28: // iload_2 case 28: // iload_2
case 29: // iload_3 case 29: // iload_3
instr = loadStore( ValueType.i32, true, op - 26, codePos ); addLoadStoreInstruction( ValueType.i32, true, op - 26, codePos );
break; break;
case 30: // lload_0 case 30: // lload_0
case 31: // lload_1 case 31: // lload_1
case 32: // lload_2 case 32: // lload_2
case 33: // lload_3 case 33: // lload_3
instr = loadStore( ValueType.i64, true, op - 30, codePos ); addLoadStoreInstruction( ValueType.i64, true, op - 30, codePos );
break; break;
case 34: // fload_0 case 34: // fload_0
case 35: // fload_1 case 35: // fload_1
case 36: // fload_2 case 36: // fload_2
case 37: // fload_3 case 37: // fload_3
instr = loadStore( ValueType.f32, true, op - 34, codePos ); addLoadStoreInstruction( ValueType.f32, true, op - 34, codePos );
break; break;
case 38: // dload_0 case 38: // dload_0
case 39: // dload_1 case 39: // dload_1
case 40: // dload_2 case 40: // dload_2
case 41: // dload_3 case 41: // dload_3
instr = loadStore( ValueType.f64, true, op - 38, codePos ); addLoadStoreInstruction( ValueType.f64, true, op - 38, codePos );
break; break;
//TODO case 42: //aload_0 //TODO case 42: //aload_0
//TODO case 43: //aload_1 //TODO case 43: //aload_1
@ -192,47 +169,47 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
//TODO case 52: // caload //TODO case 52: // caload
//TODO case 53: // saload //TODO case 53: // saload
case 54: // istore case 54: // istore
instr = loadStore( ValueType.i32, false, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.i32, false, byteCode.readUnsignedByte(), codePos );
break; break;
case 55: // lstore case 55: // lstore
instr = loadStore( ValueType.i64, false, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.i64, false, byteCode.readUnsignedByte(), codePos );
break; break;
case 56: // fstore case 56: // fstore
instr = loadStore( ValueType.f32, false, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.f32, false, byteCode.readUnsignedByte(), codePos );
break; break;
case 57: // dstore case 57: // dstore
instr = loadStore( ValueType.f64, false, byteCode.readUnsignedByte(), codePos ); addLoadStoreInstruction( ValueType.f64, false, byteCode.readUnsignedByte(), codePos );
break; break;
//TODO case 58: // astore //TODO case 58: // astore
case 59: // istore_0 case 59: // istore_0
case 60: // istore_1 case 60: // istore_1
case 61: // istore_2 case 61: // istore_2
case 62: // istore_3 case 62: // istore_3
instr = loadStore( ValueType.i32, false, op - 59, codePos ); addLoadStoreInstruction( ValueType.i32, false, op - 59, codePos );
break; break;
case 63: // lstore_0 case 63: // lstore_0
case 64: // lstore_1 case 64: // lstore_1
case 65: // lstore_2 case 65: // lstore_2
case 66: // lstore_3 case 66: // lstore_3
instr = loadStore( ValueType.i64, false, op - 63, codePos ); addLoadStoreInstruction( ValueType.i64, false, op - 63, codePos );
break; break;
case 67: // fstore_0 case 67: // fstore_0
case 68: // fstore_1 case 68: // fstore_1
case 69: // fstore_2 case 69: // fstore_2
case 70: // fstore_3 case 70: // fstore_3
instr = loadStore( ValueType.f32, false, op - 67, codePos ); addLoadStoreInstruction( ValueType.f32, false, op - 67, codePos );
break; break;
case 71: // dstore_0 case 71: // dstore_0
case 72: // dstore_1 case 72: // dstore_1
case 73: // dstore_2 case 73: // dstore_2
case 74: // dstore_3 case 74: // dstore_3
instr = loadStore( ValueType.f64, false, op - 71, codePos ); addLoadStoreInstruction( ValueType.f64, false, op - 71, codePos );
break; break;
case 75: // astore_0 case 75: // astore_0
case 76: // astore_1 case 76: // astore_1
case 77: // astore_2 case 77: // astore_2
case 78: // astore_3 case 78: // astore_3
instr = loadStore( ValueType.anyref, false, op - 75, codePos ); addLoadStoreInstruction( ValueType.anyref, false, op - 75, codePos );
break; break;
//TODO case 79: // iastore //TODO case 79: // iastore
//TODO case 80: // lastore //TODO case 80: // lastore
@ -244,7 +221,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
//TODO case 86: // sastore //TODO case 86: // sastore
case 87: // pop case 87: // pop
case 88: // pop2 case 88: // pop2
instr = new WasmBlockInstruction( WasmBlockOperator.DROP, null, codePos ); addBlockInstruction( WasmBlockOperator.DROP, null, codePos );
break; break;
case 89: // dup: duplicate the value on top of the stack case 89: // dup: duplicate the value on top of the stack
case 90: // dup_x1 case 90: // dup_x1
@ -256,168 +233,168 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
// can be do with functions with more as one return value in future WASM standard // can be do with functions with more as one return value in future WASM standard
throw new WasmException( "Stack duplicate is not supported in current WASM. try to save immediate values in a local variable: " + op, byteCode.getLineNumber() ); throw new WasmException( "Stack duplicate is not supported in current WASM. try to save immediate values in a local variable: " + op, byteCode.getLineNumber() );
case 96: // iadd case 96: // iadd
instr = new WasmNumericInstruction( NumericOperator.add, ValueType.i32, codePos); addNumericInstruction( NumericOperator.add, ValueType.i32, codePos);
break; break;
case 97: // ladd case 97: // ladd
instr = new WasmNumericInstruction( NumericOperator.add, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.add, ValueType.i64, codePos );
break; break;
case 98: // fadd case 98: // fadd
instr = new WasmNumericInstruction( NumericOperator.add, ValueType.f32, codePos ); addNumericInstruction( NumericOperator.add, ValueType.f32, codePos );
break; break;
case 99: // dadd case 99: // dadd
instr = new WasmNumericInstruction( NumericOperator.add, ValueType.f64, codePos ); addNumericInstruction( NumericOperator.add, ValueType.f64, codePos );
break; break;
case 100: // isub case 100: // isub
instr = new WasmNumericInstruction( NumericOperator.sub, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos );
break; break;
case 101: // lsub case 101: // lsub
instr = new WasmNumericInstruction( NumericOperator.sub, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.sub, ValueType.i64, codePos );
break; break;
case 102: // fsub case 102: // fsub
instr = new WasmNumericInstruction( NumericOperator.sub, ValueType.f32, codePos ); addNumericInstruction( NumericOperator.sub, ValueType.f32, codePos );
break; break;
case 103: // dsub case 103: // dsub
instr = new WasmNumericInstruction( NumericOperator.sub, ValueType.f64, codePos ); addNumericInstruction( NumericOperator.sub, ValueType.f64, codePos );
break; break;
case 104: // imul; case 104: // imul;
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.i32, codePos );
break; break;
case 105: // lmul case 105: // lmul
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.i64, codePos );
break; break;
case 106: // fmul case 106: // fmul
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.f32, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.f32, codePos );
break; break;
case 107: // dmul case 107: // dmul
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.f64, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.f64, codePos );
break; break;
case 108: // idiv case 108: // idiv
instr = new WasmNumericInstruction( NumericOperator.div, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.div, ValueType.i32, codePos );
break; break;
case 109: // ldiv case 109: // ldiv
instr = new WasmNumericInstruction( NumericOperator.div, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.div, ValueType.i64, codePos );
break; break;
case 110: // fdiv case 110: // fdiv
instr = new WasmNumericInstruction( NumericOperator.div, ValueType.f32, codePos ); addNumericInstruction( NumericOperator.div, ValueType.f32, codePos );
break; break;
case 111: // ddiv case 111: // ddiv
instr = new WasmNumericInstruction( NumericOperator.div, ValueType.f64, codePos ); addNumericInstruction( NumericOperator.div, ValueType.f64, codePos );
break; break;
case 112: // irem case 112: // irem
instr = new WasmNumericInstruction( NumericOperator.rem, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.rem, ValueType.i32, codePos );
break; break;
case 113: // lrem case 113: // lrem
instr = new WasmNumericInstruction( NumericOperator.rem, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.rem, ValueType.i64, codePos );
break; break;
case 114: // frem case 114: // frem
case 115: // drem case 115: // drem
//TODO can be implemented with a helper function like: (a - (long)(a / b) * (double)b) //TODO can be implemented with a helper function like: (a - (long)(a / b) * (double)b)
throw new WasmException( "Modulo/Remainder for floating numbers is not supported in WASM. Use int or long data types." + op, byteCode.getLineNumber() ); throw new WasmException( "Modulo/Remainder for floating numbers is not supported in WASM. Use int or long data types." + op, byteCode.getLineNumber() );
case 116: // ineg case 116: // ineg
instructions.add( new WasmConstInstruction( -1, ValueType.i32, codePos ) ); addConstInstruction( -1, ValueType.i32, codePos );
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.i32, codePos );
break; break;
case 117: // lneg case 117: // lneg
instructions.add( new WasmConstInstruction( (long)-1, ValueType.i64, codePos ) ) ; addConstInstruction( (long)-1, ValueType.i64, codePos );
instr = new WasmNumericInstruction( NumericOperator.mul, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.mul, ValueType.i64, codePos );
break; break;
case 118: // fneg case 118: // fneg
instr = new WasmNumericInstruction( NumericOperator.neg, ValueType.f32, codePos ); addNumericInstruction( NumericOperator.neg, ValueType.f32, codePos );
break; break;
case 119: // dneg case 119: // dneg
instr = new WasmNumericInstruction( NumericOperator.neg, ValueType.f64, codePos ); addNumericInstruction( NumericOperator.neg, ValueType.f64, codePos );
break; break;
case 120: // ishl case 120: // ishl
instr = new WasmNumericInstruction( NumericOperator.shl, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.shl, ValueType.i32, codePos );
break; break;
case 121: // lshl case 121: // lshl
instructions.add( new WasmConvertInstruction( ValueTypeConvertion.i2l, codePos ) ); // the shift parameter must be of type long!!! addConvertInstruction( ValueTypeConvertion.i2l, codePos ); // the shift parameter must be of type long!!!
instr = new WasmNumericInstruction( NumericOperator.shl, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.shl, ValueType.i64, codePos );
break; break;
case 122: // ishr case 122: // ishr
instr = new WasmNumericInstruction( NumericOperator.shr_s, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.shr_s, ValueType.i32, codePos );
break; break;
case 123: // lshr case 123: // lshr
instructions.add( new WasmConvertInstruction( ValueTypeConvertion.i2l, codePos ) ); // the shift parameter must be of type long!!! addConvertInstruction( ValueTypeConvertion.i2l, codePos ); // the shift parameter must be of type long!!!
instr = new WasmNumericInstruction( NumericOperator.shr_s, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.shr_s, ValueType.i64, codePos );
break; break;
case 124: // iushr case 124: // iushr
instr = new WasmNumericInstruction( NumericOperator.shr_u, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.shr_u, ValueType.i32, codePos );
break; break;
case 125: // lushr case 125: // lushr
instructions.add( new WasmConvertInstruction( ValueTypeConvertion.i2l, codePos ) ); // the shift parameter must be of type long!!! addConvertInstruction( ValueTypeConvertion.i2l, codePos ); // the shift parameter must be of type long!!!
instr = new WasmNumericInstruction( NumericOperator.shr_u, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.shr_u, ValueType.i64, codePos );
break; break;
case 126: // iand case 126: // iand
instr = new WasmNumericInstruction( NumericOperator.and, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.and, ValueType.i32, codePos );
break; break;
case 127: // land case 127: // land
instr = new WasmNumericInstruction( NumericOperator.and, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.and, ValueType.i64, codePos );
break; break;
case 128: // ior case 128: // ior
instr = new WasmNumericInstruction( NumericOperator.or, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.or, ValueType.i32, codePos );
break; break;
case 129: // lor case 129: // lor
instr = new WasmNumericInstruction( NumericOperator.or, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.or, ValueType.i64, codePos );
break; break;
case 130: // ixor case 130: // ixor
instr = new WasmNumericInstruction( NumericOperator.xor, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.xor, ValueType.i32, codePos );
break; break;
case 131: // lxor case 131: // lxor
instr = new WasmNumericInstruction( NumericOperator.xor, ValueType.i64, codePos ); addNumericInstruction( NumericOperator.xor, ValueType.i64, codePos );
break; break;
case 132: // iinc case 132: // iinc
int idx = byteCode.readUnsignedByte(); int idx = byteCode.readUnsignedByte();
instructions.add( new WasmLoadStoreInstruction( true, idx, localVariables, codePos ) ); addLoadStoreInstruction( ValueType.i32, true, idx, codePos );
instructions.add( new WasmConstInstruction( (int)byteCode.readByte(), ValueType.i32, codePos ) ); addConstInstruction( (int)byteCode.readByte(), ValueType.i32, codePos );
instructions.add( new WasmNumericInstruction( NumericOperator.add, ValueType.i32, codePos) ); addNumericInstruction( NumericOperator.add, ValueType.i32, codePos);
instr = new WasmLoadStoreInstruction( false, idx, localVariables, codePos ); addLoadStoreInstruction( ValueType.i32, false, idx, codePos );
break; break;
case 133: // i2l case 133: // i2l
instr = new WasmConvertInstruction( ValueTypeConvertion.i2l, codePos ); addConvertInstruction( ValueTypeConvertion.i2l, codePos );
break; break;
case 134: // i2f case 134: // i2f
instr = new WasmConvertInstruction( ValueTypeConvertion.i2f, codePos ); addConvertInstruction( ValueTypeConvertion.i2f, codePos );
break; break;
case 135: // i2d case 135: // i2d
instr = new WasmConvertInstruction( ValueTypeConvertion.i2d, codePos ); addConvertInstruction( ValueTypeConvertion.i2d, codePos );
break; break;
case 136: // l2i case 136: // l2i
instr = new WasmConvertInstruction( ValueTypeConvertion.l2i, codePos ); addConvertInstruction( ValueTypeConvertion.l2i, codePos );
break; break;
case 137: // l2f case 137: // l2f
instr = new WasmConvertInstruction( ValueTypeConvertion.l2f, codePos ); addConvertInstruction( ValueTypeConvertion.l2f, codePos );
break; break;
case 138: // l2d case 138: // l2d
instr = new WasmConvertInstruction( ValueTypeConvertion.l2d, codePos ); addConvertInstruction( ValueTypeConvertion.l2d, codePos );
break; break;
case 139: // f2i case 139: // f2i
instr = new WasmConvertInstruction( ValueTypeConvertion.f2i, codePos ); addConvertInstruction( ValueTypeConvertion.f2i, codePos );
break; break;
case 140: // f2l case 140: // f2l
instr = new WasmConvertInstruction( ValueTypeConvertion.f2l, codePos ); addConvertInstruction( ValueTypeConvertion.f2l, codePos );
break; break;
case 141: // f2d case 141: // f2d
instr = new WasmConvertInstruction( ValueTypeConvertion.f2d, codePos ); addConvertInstruction( ValueTypeConvertion.f2d, codePos );
break; break;
case 142: // d2i case 142: // d2i
instr = new WasmConvertInstruction( ValueTypeConvertion.d2i, codePos ); addConvertInstruction( ValueTypeConvertion.d2i, codePos );
break; break;
case 143: // d2l case 143: // d2l
instr = new WasmConvertInstruction( ValueTypeConvertion.d2l, codePos ); addConvertInstruction( ValueTypeConvertion.d2l, codePos );
break; break;
case 144: // d2f case 144: // d2f
instr = new WasmConvertInstruction( ValueTypeConvertion.d2f, codePos ); addConvertInstruction( ValueTypeConvertion.d2f, codePos );
break; break;
case 145: // i2b case 145: // i2b
instr = new WasmConvertInstruction( ValueTypeConvertion.i2b, codePos ); addConvertInstruction( ValueTypeConvertion.i2b, codePos );
break; break;
case 146: // i2c case 146: // i2c
instructions.add( new WasmConstInstruction( 0xFFFF, ValueType.i32, codePos ) ); addConstInstruction( 0xFFFF, ValueType.i32, codePos );
instr = new WasmNumericInstruction( NumericOperator.and, ValueType.i32, codePos ); addNumericInstruction( NumericOperator.and, ValueType.i32, codePos );
break; break;
case 147: // i2s case 147: // i2s
instr = new WasmConvertInstruction( ValueTypeConvertion.i2s, codePos ); addConvertInstruction( ValueTypeConvertion.i2s, codePos );
break; break;
case 148: // lcmp case 148: // lcmp
opCompare( ValueType.i64, byteCode, codePos ); opCompare( ValueType.i64, byteCode, codePos );
@ -471,7 +448,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 167: // goto case 167: // goto
int offset = byteCode.readShort(); int offset = byteCode.readShort();
branchManager.addGotoOperator( codePos, offset, byteCode.getLineNumber() ); branchManager.addGotoOperator( codePos, offset, byteCode.getLineNumber() );
instr = new WasmNopInstruction( codePos ); // marker of the line number for the branch manager addNopInstruction( codePos ); // marker of the line number for the branch manager
break; break;
//TODO case 168: // jsr //TODO case 168: // jsr
//TODO case 169: // ret //TODO case 169: // ret
@ -503,16 +480,16 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
type = ValueType.anyref; type = ValueType.anyref;
break; break;
} }
instr = new WasmBlockInstruction( WasmBlockOperator.RETURN, type, codePos ); addBlockInstruction( WasmBlockOperator.RETURN, type, codePos );
endWithReturn = true; endWithReturn = true;
break; break;
case 178: // getstatic case 178: // getstatic
ConstantRef ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() ); ConstantRef ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
instr = new WasmGlobalInstruction( true, ref, codePos ); addGlobalInstruction( true, ref, codePos );
break; break;
case 179: // putstatic case 179: // putstatic
ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() ); ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
instr = new WasmGlobalInstruction( false, ref, codePos ); addGlobalInstruction( false, ref, codePos );
break; break;
//TODO case 180: // getfield //TODO case 180: // getfield
//TODO case 181: // putfield //TODO case 181: // putfield
@ -521,7 +498,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 184: // invokestatic case 184: // invokestatic
idx = byteCode.readUnsignedShort(); idx = byteCode.readUnsignedShort();
ref = (ConstantRef)constantPool.get( idx ); ref = (ConstantRef)constantPool.get( idx );
instr = new WasmCallInstruction( ref, codePos ); addCallInstruction( ref, codePos );
break; break;
//TODO case 185: // invokeinterface //TODO case 185: // invokeinterface
//TODO case 186: // invokedynamic //TODO case 186: // invokedynamic
@ -543,9 +520,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
default: default:
throw new WasmException( "Unimplemented Java byte code operation: " + op, byteCode.getLineNumber() ); throw new WasmException( "Unimplemented Java byte code operation: " + op, byteCode.getLineNumber() );
} }
if( instr != null ) {
instructions.add( instr );
}
} }
branchManager.calculate(); branchManager.calculate();
branchManager.handle( byteCode ); // add branch operations branchManager.handle( byteCode ); // add branch operations
@ -553,7 +527,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
// if a method ends with a loop without a break then code after the loop is no reachable // if a method ends with a loop without a break then code after the loop is no reachable
// Java does not need a return byte code in this case // Java does not need a return byte code in this case
// But WebAssembly need the dead code to validate // But WebAssembly need the dead code to validate
instructions.add( new WasmBlockInstruction( WasmBlockOperator.UNREACHABLE, null, byteCode.getCodePosition() ) ); addBlockInstruction( WasmBlockOperator.UNREACHABLE, null, byteCode.getCodePosition() );
} }
} catch( WasmException ex ) { } catch( WasmException ex ) {
throw ex; throw ex;
@ -594,11 +568,11 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
keys[i] = byteCode.readInt(); keys[i] = byteCode.readInt();
positions[i] = startPosition + byteCode.readInt(); positions[i] = startPosition + byteCode.readInt();
} }
int tempI32 = localVariables.getTempI32(); int tempI32 = -1;
int block = 0; int block = 0;
int defaultBlock = -1; int defaultBlock = -1;
int currentPos = -1; int currentPos = -1;
instructions.add( new WasmLoadStoreInstruction( false, tempI32, localVariables, codePos ) ); addLoadStoreInstruction( ValueType.i32, false, tempI32, codePos );
do { do {
int nextPos = findNext( currentPos, positions ); int nextPos = findNext( currentPos, positions );
if( nextPos == currentPos ) { if( nextPos == currentPos ) {
@ -615,10 +589,10 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
} }
for( int i = 0; i < positions.length; i++ ) { for( int i = 0; i < positions.length; i++ ) {
if( positions[i] == currentPos ) { if( positions[i] == currentPos ) {
instructions.add( new WasmLoadStoreInstruction( true, tempI32, localVariables, codePos ) ); addLoadStoreInstruction( ValueType.i32, true, tempI32, codePos );
instructions.add( new WasmConstInstruction( keys[i], ValueType.i32, codePos ) ); addConstInstruction( keys[i], ValueType.i32, codePos );
instructions.add( new WasmNumericInstruction( NumericOperator.eq, ValueType.i32, codePos ) ); addNumericInstruction( NumericOperator.eq, ValueType.i32, codePos );
instructions.add( new WasmBlockInstruction( WasmBlockOperator.BR_IF, block, codePos ) ); addBlockInstruction( WasmBlockOperator.BR_IF, block, codePos );
} }
} }
block++; block++;
@ -626,7 +600,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
if( defaultBlock < 0 ) { if( defaultBlock < 0 ) {
defaultBlock = block; defaultBlock = block;
} }
instructions.add( new WasmBlockInstruction( WasmBlockOperator.BR, defaultBlock, codePos ) ); addBlockInstruction( WasmBlockOperator.BR, defaultBlock, codePos );
} else { } else {
int low = byteCode.readInt(); int low = byteCode.readInt();
keys = null; keys = null;
@ -636,8 +610,8 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
positions[i] = startPosition + byteCode.readInt(); positions[i] = startPosition + byteCode.readInt();
} }
if( low != 0 ) { // the br_table starts ever with the value 0. That we need to subtract the start value if it different if( low != 0 ) { // the br_table starts ever with the value 0. That we need to subtract the start value if it different
instructions.add( new WasmConstInstruction( low, ValueType.i32, codePos ) ); addConstInstruction( low, ValueType.i32, codePos );
instructions.add( new WasmNumericInstruction( NumericOperator.sub, ValueType.i32, codePos ) ); addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos );
} }
} }
branchManager.addSwitchOperator( switchValuestartPosition, 0, lineNumber, keys, positions, defaultPosition ); branchManager.addSwitchOperator( switchValuestartPosition, 0, lineNumber, keys, positions, defaultPosition );
@ -651,6 +625,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
*/ */
private int findPreviousPushCodePosition() { private int findPreviousPushCodePosition() {
int valueCount = 0; int valueCount = 0;
List<WasmInstruction> instructions = getInstructions();
for( int i = instructions.size() - 1; i >= 0; i-- ) { for( int i = instructions.size() - 1; i >= 0; i-- ) {
WasmInstruction instr = instructions.get( i ); WasmInstruction instr = instructions.get( i );
ValueType valueType = instr.getPushValueType(); ValueType valueType = instr.getPushValueType();
@ -686,25 +661,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
return find ? next : current; return find ? next : current;
} }
/**
* Create a WasmLoadStoreInstruction.
*
* @param valueType
* the value type
* @param load
* true: if load
* @param idx
* the memory/slot idx of the variable
* @param codePos
* the code position/offset in the Java method
* @return the WasmLoadStoreInstruction
*/
@Nonnull
private WasmLoadStoreInstruction loadStore( ValueType valueType, boolean load, @Nonnegative int idx, int codePos ) {
localVariables.use( valueType, idx );
return new WasmLoadStoreInstruction( load, idx, localVariables, codePos );
}
/** /**
* Handle the if<condition> of the Java byte code. This Java instruction compare the first stack value with value 0. * Handle the if<condition> of the Java byte code. This Java instruction compare the first stack value with value 0.
* Important: In the Java IF expression the condition for the jump to the else block is saved. In WebAssembler we * Important: In the Java IF expression the condition for the jump to the else block is saved. In WebAssembler we
@ -720,7 +676,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
* if any I/O errors occur. * if any I/O errors occur.
*/ */
private void opIfCondition( NumericOperator compareOp, CodeInputStream byteCode, int codePos ) throws IOException { private void opIfCondition( NumericOperator compareOp, CodeInputStream byteCode, int codePos ) throws IOException {
instructions.add( new WasmConstInstruction( 0, ValueType.i32, codePos ) ); addConstInstruction( 0, ValueType.i32, codePos );
opIfCompareCondition( compareOp, byteCode, codePos ); opIfCompareCondition( compareOp, byteCode, codePos );
} }
@ -742,7 +698,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
int offset = byteCode.readShort(); int offset = byteCode.readShort();
WasmNumericInstruction compare = new WasmNumericInstruction( compareOp, ValueType.i32, codePos ); WasmNumericInstruction compare = new WasmNumericInstruction( compareOp, ValueType.i32, codePos );
branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare ); branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare );
instructions.add( compare ); getInstructions().add( compare );
} }
/** /**
@ -787,7 +743,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
int offset = byteCode.readShort(); int offset = byteCode.readShort();
WasmNumericInstruction compare = new WasmNumericInstruction( numOp, valueType, codePos ); WasmNumericInstruction compare = new WasmNumericInstruction( numOp, valueType, codePos );
branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare ); branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare );
instructions.add( compare ); getInstructions().add( compare );
} }
} }

View File

@ -73,6 +73,10 @@ class LocaleVariableManager {
* the memory/slot index of the local variable * the memory/slot index of the local variable
*/ */
void use( ValueType valueType, int slot ) { void use( ValueType valueType, int slot ) {
if( slot < 0 ) {
needTempI32 = true;
return;
}
ensureCapacity( slot ); ensureCapacity( slot );
size = Math.max( size, slot + 1 ); size = Math.max( size, slot + 1 );
LocaleVariable var = variables[slot]; LocaleVariable var = variables[slot];

View File

@ -15,8 +15,15 @@
*/ */
package de.inetsoftware.jwebassembly.module; package de.inetsoftware.jwebassembly.module;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.classparser.ConstantRef;
/** /**
* Base class for Code Building. * Base class for Code Building.
* *
@ -24,12 +31,18 @@ import java.util.List;
*/ */
public abstract class WasmCodeBuilder { public abstract class WasmCodeBuilder {
private final LocaleVariableManager localVariables = new LocaleVariableManager();
private final List<WasmInstruction> instructions = new ArrayList<>();
/** /**
* Get the list of instructions * Get the list of instructions
* *
* @return the list * @return the list
*/ */
abstract List<WasmInstruction> getInstructions(); List<WasmInstruction> getInstructions() {
return instructions;
}
/** /**
* Get the data types of the local variables. The value is only valid until the next call. * Get the data types of the local variables. The value is only valid until the next call.
@ -38,6 +51,143 @@ public abstract class WasmCodeBuilder {
* the count of method parameter which should be exclude * the count of method parameter which should be exclude
* @return the reused list with fresh values * @return the reused list with fresh values
*/ */
abstract List<ValueType> getLocalTypes( int paramCount ); List<ValueType> getLocalTypes( int paramCount ) {
return localVariables.getLocalTypes( paramCount );
}
/**
* Reset the code builder.
*/
protected void reset() {
instructions.clear();
localVariables.reset();
}
/**
* Calculate the index of the variables
*/
protected void calculateVariables() {
localVariables.calculate();
}
/**
* Create a WasmLoadStoreInstruction.
*
* @param valueType
* the value type
* @param load
* true: if load
* @param idx
* the memory/slot idx of the variable
* @param javaCodePos
* the code position/offset in the Java method
*/
@Nonnull
protected void addLoadStoreInstruction( ValueType valueType, boolean load, @Nonnegative int idx, int javaCodePos ) {
localVariables.use( valueType, idx );
instructions.add( new WasmLoadStoreInstruction( load, idx, localVariables, javaCodePos ) );
}
/**
* Add a global instruction
*
* @param load
* true: if load
* @param ref
* reference to a static field
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addGlobalInstruction( boolean load, ConstantRef ref, int javaCodePos ) {
instructions.add( new WasmGlobalInstruction( load, ref, javaCodePos ) );
}
/**
* Add a constant instruction.
*
* @param value
* the value
* @param valueType
* the value type
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addConstInstruction( Number value, ValueType valueType, int javaCodePos ) {
instructions.add( new WasmConstInstruction( value, valueType, javaCodePos ) );
}
/**
* Add a constant instruction with unknown value type.
*
* @param value
* the value
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addConstInstruction( Number value, int javaCodePos ) {
instructions.add( new WasmConstInstruction( value, javaCodePos ) );
}
/**
* Add a numeric operation instruction
*
* @param numOp
* the operation
* @param valueType
* the value type
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addNumericInstruction( @Nullable NumericOperator numOp, @Nullable ValueType valueType, int javaCodePos ) {
instructions.add( new WasmNumericInstruction( numOp, valueType, javaCodePos ) );
}
/**
* Add a value convert/cast instruction.
*
* @param conversion
* the conversion
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addConvertInstruction( ValueTypeConvertion conversion, int javaCodePos ) {
instructions.add( new WasmConvertInstruction( conversion, javaCodePos ) );
}
/**
* Add a static function call.
*
* @param method
* reference to a static method
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addCallInstruction( ConstantRef method, int javaCodePos ) {
instructions.add( new WasmCallInstruction( method, javaCodePos ) );
}
/**
* Add a block operation.
*
* @param op
* the operation
* @param data
* extra data for some operations
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addBlockInstruction( WasmBlockOperator op, @Nullable Object data, int javaCodePos ) {
instructions.add( new WasmBlockInstruction( op, data, javaCodePos ) );
}
/**
* Add a no operation to the instruction list as marker on the code position. This instruction will not be write to
* the output.
*
* @param javaCodePos
* the code position/offset in the Java method
*/
protected void addNopInstruction( int javaCodePos ) {
instructions.add( new WasmNopInstruction( javaCodePos ) );
}
} }