JWebAssembly/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java

971 lines
50 KiB
Java
Raw Normal View History

/*
* Copyright 2018 - 2021 Volker Berlin (i-net software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.inetsoftware.jwebassembly.module;
import java.io.IOException;
2021-02-14 16:27:10 +01:00
import java.util.List;
import javax.annotation.Nonnull;
2020-01-25 21:17:42 +01:00
import de.inetsoftware.classparser.BootstrapMethod;
import de.inetsoftware.classparser.ClassFile;
import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.CodeInputStream;
2018-12-05 22:14:26 +01:00
import de.inetsoftware.classparser.ConstantClass;
2020-01-25 21:17:42 +01:00
import de.inetsoftware.classparser.ConstantInvokeDynamic;
import de.inetsoftware.classparser.ConstantPool;
import de.inetsoftware.classparser.ConstantRef;
import de.inetsoftware.classparser.MethodInfo;
import de.inetsoftware.jwebassembly.WasmException;
2021-02-14 16:27:10 +01:00
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
2019-08-21 20:52:12 +02:00
import de.inetsoftware.jwebassembly.wasm.ArrayType;
import de.inetsoftware.jwebassembly.wasm.NamedStorageType;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.NumericOperator;
2018-12-05 22:14:26 +01:00
import de.inetsoftware.jwebassembly.wasm.StructOperator;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.ValueType;
2021-02-14 16:27:10 +01:00
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
/**
* Convert Java Byte Code to a list of WasmInstruction.
*
* @author Volker Berlin
*/
class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
private BranchManger branchManager;
/**
* {@inheritDoc}
*/
@Override
void init( WasmOptions options, ClassFileLoader classFileLoader ) {
super.init( options, classFileLoader );
this.branchManager = new BranchManger( options, getInstructions(), getLocalVariables() );
}
/**
* Build the wasm instructions
*
* @param code
* the Java method code
* @param method
* the method with signature as fallback for a missing variable table
* @throws WasmException
* if some Java code can't converted
*/
void buildCode( @Nonnull Code code, MethodInfo method ) {
CodeInputStream byteCode = null;
try {
reset( code.getLocalVariableTable(), method, null );
branchManager.reset( code );
byteCode = code.getByteCode();
2021-02-14 16:27:10 +01:00
AnyType returnType = new ValueTypeParser( method.getType().substring( method.getType().lastIndexOf( ')' ) + 1), getTypeManager() ).next();
writeCode( byteCode, code.getConstantPool(), method.getDeclaringClassFile(), returnType );
calculateVariables();
} catch( Exception ioex ) {
int lineNumber = byteCode == null ? -1 : byteCode.getLineNumber();
throw WasmException.create( ioex, lineNumber );
}
}
/**
* Write the byte code of a method.
*
* @param byteCode
* a stream of byte code
* @param constantPool
* the constant pool of the the current class
2020-01-25 21:17:42 +01:00
* @param classFile
* the declaring class file
2021-02-14 16:27:10 +01:00
* @param returnType
* the return type of the method
* @throws WasmException
* if some Java code can't converted
*/
2021-02-14 16:27:10 +01:00
private void writeCode( CodeInputStream byteCode, ConstantPool constantPool, ClassFile classFile, AnyType returnType ) throws WasmException {
boolean nullConstants = false;
int lineNumber = -1;
try {
boolean wide = false;
while( byteCode.available() > 0 ) {
int codePos = byteCode.getCodePosition();
lineNumber = byteCode.getLineNumber();
int op = byteCode.readUnsignedByte();
2019-01-23 20:24:47 +01:00
switch( op ) {
case 0: // nop
break;
2018-12-14 20:50:08 +01:00
case 1: // aconst_null
addStructInstruction( StructOperator.NULL, "java/lang/Object", null, codePos, lineNumber );
2021-02-14 16:27:10 +01:00
nullConstants = true;
2018-12-14 20:50:08 +01:00
break;
case 2: // iconst_m1
case 3: // iconst_0
case 4: // iconst_1
case 5: // iconst_2
case 6: // iconst_3
case 7: // iconst_4
case 8: // iconst_5
addConstInstruction( Integer.valueOf( op - 3 ), ValueType.i32, codePos, lineNumber );
break;
case 9: // lconst_0
case 10: // lconst_1
addConstInstruction( Long.valueOf( op - 9 ), ValueType.i64, codePos, lineNumber );
break;
case 11: // fconst_0
case 12: // fconst_1
case 13: // fconst_2
addConstInstruction( Float.valueOf( op - 11 ), ValueType.f32, codePos, lineNumber );
break;
case 14: // dconst_0
case 15: // dconst_1
addConstInstruction( Double.valueOf( op - 14 ), ValueType.f64, codePos, lineNumber );
break;
case 16: // bipush
addConstInstruction( Integer.valueOf( byteCode.readByte() ), ValueType.i32, codePos, lineNumber );
break;
case 17: // sipush
addConstInstruction( Integer.valueOf( byteCode.readShort() ), ValueType.i32, codePos, lineNumber );
break;
case 18: // ldc
addConstInstruction( constantPool.get( byteCode.readUnsignedByte() ), codePos, lineNumber );
break;
case 19: // ldc_w
case 20: // ldc2_w
addConstInstruction( constantPool.get( byteCode.readUnsignedShort() ), codePos, lineNumber );
break;
case 21: // iload
addLoadStoreInstruction( ValueType.i32, true, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 22: // lload
addLoadStoreInstruction( ValueType.i64, true, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 23: // fload
addLoadStoreInstruction( ValueType.f32, true, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 24: // dload
addLoadStoreInstruction( ValueType.f64, true, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
2018-12-02 19:54:59 +01:00
case 25: // aload
addLoadStoreInstruction( ValueType.externref, true, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
case 26: // iload_0
case 27: // iload_1
case 28: // iload_2
case 29: // iload_3
addLoadStoreInstruction( ValueType.i32, true, op - 26, codePos, lineNumber );
break;
case 30: // lload_0
case 31: // lload_1
case 32: // lload_2
case 33: // lload_3
addLoadStoreInstruction( ValueType.i64, true, op - 30, codePos, lineNumber );
break;
case 34: // fload_0
case 35: // fload_1
case 36: // fload_2
case 37: // fload_3
addLoadStoreInstruction( ValueType.f32, true, op - 34, codePos, lineNumber );
break;
case 38: // dload_0
case 39: // dload_1
case 40: // dload_2
case 41: // dload_3
addLoadStoreInstruction( ValueType.f64, true, op - 38, codePos, lineNumber );
break;
2018-12-02 19:54:59 +01:00
case 42: //aload_0
case 43: //aload_1
case 44: //aload_2
case 45: //aload_3
addLoadStoreInstruction( ValueType.externref, true, op - 42, codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
case 46: // iaload
addArrayInstruction( ArrayOperator.GET, ValueType.i32, codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
2019-01-13 14:42:24 +01:00
case 47: // laload
addArrayInstruction( ArrayOperator.GET, ValueType.i64, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 48: // faload
addArrayInstruction( ArrayOperator.GET, ValueType.f32, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 49: // daload
addArrayInstruction( ArrayOperator.GET, ValueType.f64, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 50: // aaload
AnyType storeType = findValueTypeFromStack( 2, codePos );
addArrayInstruction( ArrayOperator.GET, ((ArrayType)storeType).getArrayType(), codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 51: // baload
addArrayInstruction( ArrayOperator.GET_S, ValueType.i8, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 52: // caload
addArrayInstruction( ArrayOperator.GET_U, ValueType.i16, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 53: // saload
addArrayInstruction( ArrayOperator.GET_S, ValueType.i16, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 54: // istore
addLoadStoreInstruction( ValueType.i32, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 55: // lstore
addLoadStoreInstruction( ValueType.i64, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 56: // fstore
addLoadStoreInstruction( ValueType.f32, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
case 57: // dstore
addLoadStoreInstruction( ValueType.f64, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break;
2018-12-02 19:54:59 +01:00
case 58: // astore
2021-02-27 20:40:30 +01:00
storeType = branchManager.getCatchType( codePos );
if( storeType != null ) {
// for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
} else {
storeType = findValueTypeFromStack( 1, codePos );
}
addLoadStoreInstruction( storeType, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
case 59: // istore_0
case 60: // istore_1
case 61: // istore_2
case 62: // istore_3
addLoadStoreInstruction( ValueType.i32, false, op - 59, codePos, lineNumber );
break;
case 63: // lstore_0
case 64: // lstore_1
case 65: // lstore_2
case 66: // lstore_3
addLoadStoreInstruction( ValueType.i64, false, op - 63, codePos, lineNumber );
break;
case 67: // fstore_0
case 68: // fstore_1
case 69: // fstore_2
case 70: // fstore_3
addLoadStoreInstruction( ValueType.f32, false, op - 67, codePos, lineNumber );
break;
case 71: // dstore_0
case 72: // dstore_1
case 73: // dstore_2
case 74: // dstore_3
addLoadStoreInstruction( ValueType.f64, false, op - 71, codePos, lineNumber );
break;
case 75: // astore_0
case 76: // astore_1
case 77: // astore_2
case 78: // astore_3
2021-02-27 20:40:30 +01:00
storeType = branchManager.getCatchType( codePos );
if( storeType != null ) {
// for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
2019-03-02 21:54:27 +01:00
} else {
storeType = findValueTypeFromStack( 1, codePos );
2019-03-02 21:54:27 +01:00
}
addLoadStoreInstruction( storeType, false, op - 75, codePos, lineNumber );
break;
2018-12-02 19:54:59 +01:00
case 79: // iastore
addArrayInstruction( ArrayOperator.SET, ValueType.i32, codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
2019-01-13 14:42:24 +01:00
case 80: // lastore
addArrayInstruction( ArrayOperator.SET, ValueType.i64, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 81: // fastore
addArrayInstruction( ArrayOperator.SET, ValueType.f32, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 82: // dastore
addArrayInstruction( ArrayOperator.SET, ValueType.f64, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 83: // aastore
storeType = findValueTypeFromStack( 3, codePos );
addArrayInstruction( ArrayOperator.SET, ((ArrayType)storeType).getArrayType(), codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 84: // bastore
addArrayInstruction( ArrayOperator.SET, ValueType.i8, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 85: // castore
addArrayInstruction( ArrayOperator.SET, ValueType.i16, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 86: // sastore
addArrayInstruction( ArrayOperator.SET, ValueType.i16, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
case 87: // pop
case 88: // pop2
addBlockInstruction( WasmBlockOperator.DROP, null, codePos, lineNumber );
break;
case 89: // dup: duplicate the value on top of the stack
2018-11-26 20:35:50 +01:00
case 92: // dup2
addDupInstruction( codePos, lineNumber );
break;
case 90: // dup_x1
case 93: // dup2_x1
2020-04-25 19:31:30 +02:00
addDupX1Instruction( codePos, lineNumber );
break;
case 91: // dup_x2
case 94: // dup2_x2
2020-04-25 22:22:18 +02:00
addDupX2Instruction( codePos, lineNumber );
break;
case 95: // swap
2020-04-25 22:22:18 +02:00
throw new WasmException( "Stack swap is not supported in current WASM. try to save immediate values in a local variable: "
+ op, lineNumber );
case 96: // iadd
addNumericInstruction( NumericOperator.add, ValueType.i32, codePos, lineNumber );
break;
case 97: // ladd
addNumericInstruction( NumericOperator.add, ValueType.i64, codePos, lineNumber );
break;
case 98: // fadd
addNumericInstruction( NumericOperator.add, ValueType.f32, codePos, lineNumber );
break;
case 99: // dadd
addNumericInstruction( NumericOperator.add, ValueType.f64, codePos, lineNumber );
break;
case 100: // isub
addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos, lineNumber );
break;
case 101: // lsub
addNumericInstruction( NumericOperator.sub, ValueType.i64, codePos, lineNumber );
break;
case 102: // fsub
addNumericInstruction( NumericOperator.sub, ValueType.f32, codePos, lineNumber );
break;
case 103: // dsub
addNumericInstruction( NumericOperator.sub, ValueType.f64, codePos, lineNumber );
break;
case 104: // imul;
addNumericInstruction( NumericOperator.mul, ValueType.i32, codePos, lineNumber );
break;
case 105: // lmul
addNumericInstruction( NumericOperator.mul, ValueType.i64, codePos, lineNumber );
break;
case 106: // fmul
addNumericInstruction( NumericOperator.mul, ValueType.f32, codePos, lineNumber );
break;
case 107: // dmul
addNumericInstruction( NumericOperator.mul, ValueType.f64, codePos, lineNumber );
break;
case 108: // idiv
if( getOptions().useEH() ) {
addCallInstruction( new FunctionName( "de/inetsoftware/jwebassembly/module/WasmEmbbeddedCode", "idiv", "(II)I" ), false, codePos, lineNumber );
} else {
addNumericInstruction( NumericOperator.div, ValueType.i32, codePos, lineNumber );
}
break;
case 109: // ldiv
if( getOptions().useEH() ) {
addCallInstruction( new FunctionName( "de/inetsoftware/jwebassembly/module/WasmEmbbeddedCode", "ldiv", "(JJ)J" ), false, codePos, lineNumber );
} else {
addNumericInstruction( NumericOperator.div, ValueType.i64, codePos, lineNumber );
}
break;
case 110: // fdiv
addNumericInstruction( NumericOperator.div, ValueType.f32, codePos, lineNumber );
break;
case 111: // ddiv
addNumericInstruction( NumericOperator.div, ValueType.f64, codePos, lineNumber );
break;
case 112: // irem
addNumericInstruction( NumericOperator.rem, ValueType.i32, codePos, lineNumber );
break;
case 113: // lrem
addNumericInstruction( NumericOperator.rem, ValueType.i64, codePos, lineNumber );
break;
case 114: // frem
2019-03-12 21:27:23 +01:00
//helper function like: (a - (int)(a / b) * (float)b)
addCallInstruction( new WatCodeSyntheticFunctionName( "frem", "local.get 0 local.get 0 local.get 1 f32.div i32.trunc_sat_f32_s f32.convert_i32_s local.get 1 f32.mul f32.sub return", ValueType.f32, ValueType.f32, null, ValueType.f32 ), false, codePos, lineNumber );
2019-03-12 21:27:23 +01:00
break;
case 115: // drem
2019-03-12 21:27:23 +01:00
//helper function like: (a - (long)(a / b) * (double)b)
addCallInstruction( new WatCodeSyntheticFunctionName( "drem", "local.get 0 local.get 0 local.get 1 f64.div i64.trunc_sat_f64_s f64.convert_i64_s local.get 1 f64.mul f64.sub return", ValueType.f64, ValueType.f64, null, ValueType.f64 ), false, codePos, lineNumber );
2019-03-12 21:27:23 +01:00
break;
case 116: // ineg
addConstInstruction( -1, ValueType.i32, codePos, lineNumber );
addNumericInstruction( NumericOperator.mul, ValueType.i32, codePos, lineNumber );
break;
case 117: // lneg
addConstInstruction( (long)-1, ValueType.i64, codePos, lineNumber );
addNumericInstruction( NumericOperator.mul, ValueType.i64, codePos, lineNumber );
break;
case 118: // fneg
addNumericInstruction( NumericOperator.neg, ValueType.f32, codePos, lineNumber );
break;
case 119: // dneg
addNumericInstruction( NumericOperator.neg, ValueType.f64, codePos, lineNumber );
break;
case 120: // ishl
addNumericInstruction( NumericOperator.shl, ValueType.i32, codePos, lineNumber );
break;
case 121: // lshl
addConvertInstruction( ValueTypeConvertion.i2l, codePos, lineNumber ); // the shift parameter must be of type long!!!
addNumericInstruction( NumericOperator.shl, ValueType.i64, codePos, lineNumber );
break;
case 122: // ishr
addNumericInstruction( NumericOperator.shr_s, ValueType.i32, codePos, lineNumber );
break;
case 123: // lshr
addConvertInstruction( ValueTypeConvertion.i2l, codePos, lineNumber ); // the shift parameter must be of type long!!!
addNumericInstruction( NumericOperator.shr_s, ValueType.i64, codePos, lineNumber );
break;
case 124: // iushr
addNumericInstruction( NumericOperator.shr_u, ValueType.i32, codePos, lineNumber );
break;
case 125: // lushr
addConvertInstruction( ValueTypeConvertion.i2l, codePos, lineNumber ); // the shift parameter must be of type long!!!
addNumericInstruction( NumericOperator.shr_u, ValueType.i64, codePos, lineNumber );
break;
case 126: // iand
addNumericInstruction( NumericOperator.and, ValueType.i32, codePos, lineNumber );
break;
case 127: // land
addNumericInstruction( NumericOperator.and, ValueType.i64, codePos, lineNumber );
break;
case 128: // ior
addNumericInstruction( NumericOperator.or, ValueType.i32, codePos, lineNumber );
break;
case 129: // lor
addNumericInstruction( NumericOperator.or, ValueType.i64, codePos, lineNumber );
break;
case 130: // ixor
addNumericInstruction( NumericOperator.xor, ValueType.i32, codePos, lineNumber );
break;
case 131: // lxor
addNumericInstruction( NumericOperator.xor, ValueType.i64, codePos, lineNumber );
break;
case 132: // iinc
int idx = byteCode.readUnsignedIndex( wide );
addLoadStoreInstruction( ValueType.i32, true, idx, codePos, lineNumber );
addConstInstruction( (int)(wide ? byteCode.readShort() : byteCode.readByte()), ValueType.i32, codePos, lineNumber );
addNumericInstruction( NumericOperator.add, ValueType.i32, codePos, lineNumber );
addLoadStoreInstruction( ValueType.i32, false, idx, codePos, lineNumber );
break;
case 133: // i2l
addConvertInstruction( ValueTypeConvertion.i2l, codePos, lineNumber );
break;
case 134: // i2f
addConvertInstruction( ValueTypeConvertion.i2f, codePos, lineNumber );
break;
case 135: // i2d
addConvertInstruction( ValueTypeConvertion.i2d, codePos, lineNumber );
break;
case 136: // l2i
addConvertInstruction( ValueTypeConvertion.l2i, codePos, lineNumber );
break;
case 137: // l2f
addConvertInstruction( ValueTypeConvertion.l2f, codePos, lineNumber );
break;
case 138: // l2d
addConvertInstruction( ValueTypeConvertion.l2d, codePos, lineNumber );
break;
case 139: // f2i
addConvertInstruction( ValueTypeConvertion.f2i, codePos, lineNumber );
break;
case 140: // f2l
addConvertInstruction( ValueTypeConvertion.f2l, codePos, lineNumber );
break;
case 141: // f2d
addConvertInstruction( ValueTypeConvertion.f2d, codePos, lineNumber );
break;
case 142: // d2i
addConvertInstruction( ValueTypeConvertion.d2i, codePos, lineNumber );
break;
case 143: // d2l
addConvertInstruction( ValueTypeConvertion.d2l, codePos, lineNumber );
break;
case 144: // d2f
addConvertInstruction( ValueTypeConvertion.d2f, codePos, lineNumber );
break;
case 145: // i2b
addConvertInstruction( ValueTypeConvertion.i2b, codePos, lineNumber );
break;
case 146: // i2c
addConstInstruction( 0xFFFF, ValueType.i32, codePos, lineNumber );
addNumericInstruction( NumericOperator.and, ValueType.i32, codePos, lineNumber );
break;
case 147: // i2s
addConvertInstruction( ValueTypeConvertion.i2s, codePos, lineNumber );
break;
case 148: // lcmp
opCompare( ValueType.i64, byteCode, codePos, lineNumber );
break;
case 149: // fcmpl
case 150: // fcmpg
opCompare( ValueType.f32, byteCode, codePos, lineNumber );
break;
case 151: // dcmpl
case 152: // dcmpg
opCompare( ValueType.f64, byteCode, codePos, lineNumber );
break;
case 153: // ifeq
opIfCondition( NumericOperator.eq, byteCode, codePos, lineNumber );
break;
case 154: // ifne
opIfCondition( NumericOperator.ne, byteCode, codePos, lineNumber );
break;
case 155: // iflt
opIfCondition( NumericOperator.lt, byteCode, codePos, lineNumber );
break;
case 156: // ifge
opIfCondition( NumericOperator.ge, byteCode, codePos, lineNumber );
break;
case 157: // ifgt
opIfCondition( NumericOperator.gt, byteCode, codePos, lineNumber );
break;
case 158: // ifle
opIfCondition( NumericOperator.le, byteCode, codePos, lineNumber );
break;
case 159: // if_icmpeq
opIfCompareCondition( NumericOperator.eq, byteCode, codePos, lineNumber );
break;
case 160: // if_icmpne
opIfCompareCondition( NumericOperator.ne, byteCode, codePos, lineNumber );
break;
case 161: // if_icmplt
opIfCompareCondition( NumericOperator.lt, byteCode, codePos, lineNumber );
break;
case 162: // if_icmpge
opIfCompareCondition( NumericOperator.ge, byteCode, codePos, lineNumber );
break;
case 163: // if_icmpgt
opIfCompareCondition( NumericOperator.gt, byteCode, codePos, lineNumber );
break;
case 164: // if_icmple
opIfCompareCondition( NumericOperator.le, byteCode, codePos, lineNumber );
break;
case 165: // if_acmpeq
opIfCompareCondition( NumericOperator.ref_eq, byteCode, codePos, lineNumber );
break;
case 166: // if_acmpne
opIfCompareCondition( NumericOperator.ref_ne, byteCode, codePos, lineNumber );
break;
case 167: // goto
int offset = byteCode.readShort();
branchManager.addGotoOperator( codePos, offset, byteCode.getCodePosition(), lineNumber );
addJumpPlaceholder( codePos + offset, 0, null, codePos, lineNumber ); // marker of the line number for the branch manager
break;
case 168: // jsr
case 169: // ret
case 201: // jsr_w
throw new WasmException( "Finally block of Java 5 or older is not supported. Compile the sources with a Java SE 6 or newer: "
+ op, lineNumber );
case 170: // tableswitch
case 171: // lookupswitch
writeSwitchCode( byteCode, op == 171 );
break;
case 172: // ireturn
case 173: // lreturn
case 174: // freturn
case 175: // dreturn
case 176: // areturn
case 177: // return void
2019-08-21 20:52:12 +02:00
AnyType type = null;
switch( op ) {
case 172: // ireturn
type = ValueType.i32;
break;
case 173: // lreturn
type = ValueType.i64;
break;
case 174: // freturn
type = ValueType.f32;
break;
case 175: // dreturn
type = ValueType.f64;
break;
case 176: // areturn
2021-02-14 16:27:10 +01:00
type = getOptions().useGC() ? returnType : ValueType.externref;
break;
}
addBlockInstruction( WasmBlockOperator.RETURN, type, codePos, lineNumber );
break;
case 178: // getstatic
ConstantRef ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
addGlobalInstruction( true, ref, codePos, lineNumber );
break;
case 179: // putstatic
ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
addGlobalInstruction( false, ref, codePos, lineNumber );
break;
2019-01-13 11:36:07 +01:00
case 180: // getfield
ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
addStructInstruction( StructOperator.GET, ref.getClassName(), new NamedStorageType( ref, getTypeManager() ), codePos, lineNumber );
2019-01-13 11:36:07 +01:00
break;
case 181: // putfield
ref = (ConstantRef)constantPool.get( byteCode.readUnsignedShort() );
addStructInstruction( StructOperator.SET, ref.getClassName(), new NamedStorageType( ref, getTypeManager() ), codePos, lineNumber );
2019-01-13 11:36:07 +01:00
break;
2019-03-02 21:54:27 +01:00
case 182: // invokevirtual
2018-12-14 19:59:49 +01:00
case 183: // invokespecial, invoke a constructor
case 184: // invokestatic
2020-01-25 23:42:22 +01:00
case 185: // invokeinterface
idx = byteCode.readUnsignedShort();
ref = (ConstantRef)constantPool.get( idx );
FunctionName funcName = new FunctionName( ref );
switch( op ) {
case 182:
addCallVirtualInstruction( funcName, codePos, lineNumber );
break;
case 183:
addCallInstruction( funcName, true, codePos, lineNumber );
break;
case 184:
addCallInstruction( funcName, false, codePos, lineNumber );
break;
2020-01-25 23:42:22 +01:00
case 185:
addCallInterfaceInstruction( funcName, codePos, lineNumber );
2020-05-08 22:19:08 +02:00
byteCode.read(); // count
byteCode.read(); // 0
2020-01-25 23:42:22 +01:00
break;
}
break;
2020-01-25 21:17:42 +01:00
case 186: // invokedynamic
idx = byteCode.readUnsignedShort();
ConstantInvokeDynamic dynamic = (ConstantInvokeDynamic)constantPool.get( idx );
idx = byteCode.readUnsignedShort(); // ever zero
idx = dynamic.getBootstrapMethodIndex();
BootstrapMethod method = classFile.getBootstrapMethod( idx );
2021-01-23 22:16:16 +01:00
String name = dynamic.getType();
addInvokeDynamic( method, name, dynamic.getName(), codePos, lineNumber );
break;
2018-12-05 22:14:26 +01:00
case 187: // new
2021-01-23 22:16:16 +01:00
name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName();
addStructInstruction( StructOperator.NEW_DEFAULT, name, null, codePos, lineNumber );
2018-12-05 22:14:26 +01:00
break;
2018-12-02 19:54:59 +01:00
case 188: // newarray
int typeValue = byteCode.readByte();
switch( typeValue ) {
2019-08-21 20:52:12 +02:00
case 4: // boolean
type = ValueType.bool;
2019-08-21 20:52:12 +02:00
break;
case 5: // char
type = ValueType.u16;
2018-12-02 19:54:59 +01:00
break;
case 6: //float
type = ValueType.f32;
break;
2019-08-21 20:52:12 +02:00
case 7: //double
2018-12-02 19:54:59 +01:00
type = ValueType.f64;
break;
2019-08-21 20:52:12 +02:00
case 8: //byte
type = ValueType.i8;
break;
case 9: //short
type = ValueType.i16;
break;
case 10: //int
2018-12-02 19:54:59 +01:00
type = ValueType.i32;
break;
2019-08-21 20:52:12 +02:00
case 11: //long
2018-12-02 19:54:59 +01:00
type = ValueType.i64;
break;
default:
throw new WasmException( "Invalid Java byte code newarray: " + typeValue, lineNumber );
2018-12-02 19:54:59 +01:00
}
addArrayInstruction( ArrayOperator.NEW, type, codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
2019-01-13 14:42:24 +01:00
case 189: // anewarray
name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName();
type = getTypeManager().valueOf( name );
addArrayInstruction( ArrayOperator.NEW, type, codePos, lineNumber );
2019-01-13 14:42:24 +01:00
break;
2018-12-02 19:54:59 +01:00
case 190: // arraylength
type = ((ArrayType)findValueTypeFromStack( 1, codePos )).getArrayType();
2019-08-21 20:52:12 +02:00
addArrayInstruction( ArrayOperator.LEN, type, codePos, lineNumber );
2018-12-02 19:54:59 +01:00
break;
2019-03-02 21:54:27 +01:00
case 191: // athrow
addBlockInstruction( WasmBlockOperator.THROW, null, codePos, lineNumber );
2019-03-02 21:54:27 +01:00
break;
2020-02-01 20:29:29 +01:00
case 192: // checkcast
name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName();
addStructInstruction( StructOperator.CAST, name, null, codePos, lineNumber );
break;
2020-02-01 16:49:52 +01:00
case 193: // instanceof
2020-02-01 20:29:29 +01:00
name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName();
2020-02-01 16:49:52 +01:00
addStructInstruction( StructOperator.INSTANCEOF, name, null, codePos, lineNumber );
break;
case 194: // monitorenter
addBlockInstruction( WasmBlockOperator.MONITOR_ENTER, null, codePos, lineNumber );
break;
case 195: // monitorexit
addBlockInstruction( WasmBlockOperator.MONITOR_EXIT, null, codePos, lineNumber );
break;
case 196: // wide
// https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.wide
wide = true;
continue;
case 197: // multianewarray
name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName();
idx = byteCode.readUnsignedByte();
addMultiNewArrayInstruction( idx, name, codePos, lineNumber );
break;
2018-12-15 22:33:25 +01:00
case 198: // ifnull
opIfCompareCondition( NumericOperator.ifnull, byteCode, codePos, lineNumber );
2018-12-15 22:33:25 +01:00
break;
case 199: // ifnonnull
opIfCompareCondition( NumericOperator.ifnonnull, byteCode, codePos, lineNumber );
2018-12-15 22:33:25 +01:00
break;
case 200: // goto_w
offset = byteCode.readInt();
branchManager.addGotoOperator( codePos, offset, byteCode.getCodePosition(), lineNumber );
addNopInstruction( codePos, lineNumber ); // marker of the line number for the branch manager
break;
default:
throw new WasmException( "Unimplemented Java byte code operation: " + op, lineNumber );
}
wide = false;
}
branchManager.calculate();
branchManager.handle( byteCode ); // add branch operations
2021-02-14 16:27:10 +01:00
if( returnType != null && !isEndsWithReturn() ) {
// if a method ends with a loop or block without a break then code after the loop is no reachable
// Java does not need a return byte code in this case
// But WebAssembly need the dead code to validate
addBlockInstruction( WasmBlockOperator.UNREACHABLE, null, byteCode.getCodePosition(), byteCode.getLineNumber() );
}
2021-02-14 16:27:10 +01:00
if( nullConstants && getOptions().useGC() ) {
patchTypeOfNullConst();
}
} catch( Exception ex ) {
throw WasmException.create( ex, lineNumber );
}
}
/**
* Write the both switch operation codes
*
* @param byteCode
* the current stream with a position after the operation code
* @param isLookupSwitch
* true, if the operation was a loopupswitch; false, if the operation was a tableswitch
* @throws IOException
* if any I/O error occur
*/
private void writeSwitchCode( CodeInputStream byteCode, boolean isLookupSwitch ) throws IOException {
int lineNumber = byteCode.getLineNumber();
int codePos = byteCode.getCodePosition();
int startPosition = codePos;
int padding = startPosition % 4;
if( padding > 0 ) {
byteCode.skip( 4 - padding );
}
startPosition--;
int switchValuestartPosition = findBlockStartCodePosition( 1 );
int defaultPosition = startPosition + byteCode.readInt();
int[] keys;
int[] positions;
if( isLookupSwitch ) { // lookupswitch
int count = byteCode.readInt();
keys = new int[count];
positions = new int[count];
for( int i = 0; i < count; i++ ) {
keys[i] = byteCode.readInt();
positions[i] = startPosition + byteCode.readInt();
}
int tempI32 = getTempVariable( ValueType.i32, codePos, Integer.MAX_VALUE );
int block = 0;
int defaultBlock = -1;
int currentPos = -1;
addLoadStoreInstruction( ValueType.i32, false, tempI32, codePos, lineNumber );
do {
int nextPos = findNext( currentPos, positions );
if( nextPos == currentPos ) {
break;
}
currentPos = nextPos;
if( defaultBlock < 0 ) {
if( defaultPosition <= currentPos ) {
defaultBlock = block;
if( defaultPosition < currentPos ) {
block++;
}
}
}
for( int i = 0; i < positions.length; i++ ) {
if( positions[i] == currentPos ) {
addLoadStoreInstruction( ValueType.i32, true, tempI32, codePos, lineNumber );
addConstInstruction( keys[i], ValueType.i32, codePos, lineNumber );
addNumericInstruction( NumericOperator.eq, ValueType.i32, codePos, lineNumber );
addBlockInstruction( WasmBlockOperator.BR_IF, block, codePos, lineNumber );
}
}
block++;
} while( true );
if( defaultBlock < 0 ) {
defaultBlock = block;
}
addBlockInstruction( WasmBlockOperator.BR, defaultBlock, codePos, lineNumber );
} else {
int low = byteCode.readInt();
keys = null;
int count = byteCode.readInt() - low + 1;
positions = new int[count];
for( int i = 0; i < count; i++ ) {
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
addConstInstruction( low, ValueType.i32, codePos, lineNumber );
addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos, lineNumber );
}
}
branchManager.addSwitchOperator( switchValuestartPosition, 0, lineNumber, keys, positions, defaultPosition );
}
/**
* Find the next higher value.
*
* @param current
* the current value
* @param values
* the unordered list of values
* @return the next value or current value if not found.
*/
private static int findNext( int current, int[] values ) {
boolean find = false;
int next = Integer.MAX_VALUE;
for( int val : values ) {
if( val > current && val <= next ) {
next = val;
find = true;
}
}
return find ? next : current;
}
/**
* 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
* need to use condition for the if block. The caller of the method must already negate this
*
* @param compareOp
* The condition for the continue of a loop.
* @param byteCode
* current byte code stream to read the target offset.
* @param codePos
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
* @throws IOException
* if any I/O errors occur.
*/
private void opIfCondition( NumericOperator compareOp, CodeInputStream byteCode, int codePos, int lineNumber ) throws IOException {
addConstInstruction( 0, ValueType.i32, codePos, lineNumber );
opIfCompareCondition( compareOp, byteCode, codePos, lineNumber );
}
/**
* Handle the if<condition> of the Java byte code. This Java instruction compare 2 values from stack. Important: In
* the Java IF expression the condition for the jump to the else block is saved. In WebAssembler we need to use
* condition for the if block. The caller of the method must already negate this.
*
* @param compareOp
* The condition for the continue of a loop.
* @param byteCode
* current byte code stream to read the target offset.
* @param codePos
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
* @throws IOException
* if any I/O errors occur.
*/
private void opIfCompareCondition( NumericOperator compareOp, CodeInputStream byteCode, int codePos, int lineNumber ) throws IOException {
int offset = byteCode.readShort();
2019-09-08 21:45:28 +02:00
WasmNumericInstruction compare = addNumericInstruction( compareOp, ValueType.i32, codePos, lineNumber );
branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare );
}
/**
* Handle the different compare operator. The compare operator returns the integer values -1, 0 or 1. There is no
* equivalent in WebAssembly. That we need to read the next operation to find an equivalent.
*
* @param valueType
* the value type of the compared
* @param byteCode
* current byte code stream to read the next operation.
* @param codePos
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
* @throws IOException
* if any I/O errors occur.
*/
private void opCompare( ValueType valueType, CodeInputStream byteCode, int codePos, int lineNumber ) throws IOException {
codePos = byteCode.getCodePosition();
NumericOperator numOp;
int nextOp = byteCode.read();
switch( nextOp ) {
case 153: // ifeq
numOp = NumericOperator.eq;
break;
case 154: // ifne
numOp = NumericOperator.ne;
break;
case 155: // iflt
numOp = NumericOperator.lt;
break;
case 156: // ifge
numOp = NumericOperator.ge;
break;
case 157: // ifgt
numOp = NumericOperator.gt;
break;
case 158: // ifle
numOp = NumericOperator.le;
break;
default:
throw new WasmException( "Unexpected compare sub operation: " + nextOp, -1 );
}
int offset = byteCode.readShort();
WasmNumericInstruction compare = addNumericInstruction( numOp, valueType, codePos, lineNumber );
branchManager.addIfOperator( codePos, offset, byteCode.getLineNumber(), compare );
}
2021-02-14 16:27:10 +01:00
/**
* NULL const has no type in Java. In WebAssembly currently.
*/
private void patchTypeOfNullConst() {
List<WasmInstruction> instructions = getInstructions();
int size = instructions.size();
for( int i = 0; i < size; i++ ) {
WasmInstruction instr = instructions.get( i );
if( instr.getType() == Type.Struct ) {
WasmStructInstruction structInst = (WasmStructInstruction)instr;
if( structInst.getOperator() == StructOperator.NULL ) {
int count = 0;
for( int s = i + 1; s < size; s++ ) {
WasmInstruction nextInstr = instructions.get( s );
count -= nextInstr.getPopCount();
if( count < 0 ) {
AnyType[] popValueTypes = nextInstr.getPopValueTypes();
structInst.setStructType( (StructType)popValueTypes[-1 - count] );
break;
}
if( nextInstr.getPushValueType() != null ) {
count++;
}
}
}
}
}
}
}