862 lines
33 KiB
Java
Raw Normal View History

/*
2018-03-27 20:04:35 +02:00
* Copyright 2017 - 2018 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.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
2017-04-17 12:10:56 +02:00
import java.util.function.Consumer;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
2017-04-09 18:46:27 +02:00
import javax.annotation.Nullable;
import de.inetsoftware.classparser.Annotations;
import de.inetsoftware.classparser.ClassFile;
import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.CodeInputStream;
2017-04-08 18:48:45 +02:00
import de.inetsoftware.classparser.ConstantPool;
2017-04-17 12:10:56 +02:00
import de.inetsoftware.classparser.ConstantRef;
2017-04-09 22:45:52 +02:00
import de.inetsoftware.classparser.LocalVariableTable;
import de.inetsoftware.classparser.MethodInfo;
import de.inetsoftware.jwebassembly.WasmException;
/**
* Module Writer for text format with S-expressions.
*
* @author Volker Berlin
*/
public abstract class ModuleWriter implements Closeable {
private int paramCount;
private ArrayList<ValueType> locals = new ArrayList<>();
2017-04-09 22:45:52 +02:00
private LocalVariableTable localTable;
2017-04-09 18:18:53 +02:00
private String sourceFile;
2018-03-25 12:57:04 +02:00
private BranchManger branchManager = new BranchManger();
/**
2017-04-17 12:10:56 +02:00
* Prepare the content of the class.
*
* @param classFile
* the class file
* @throws WasmException
* if some Java code can't converted
*/
2017-04-17 12:10:56 +02:00
public void prepare( ClassFile classFile ) {
iterateMethods( classFile, m -> prepareMethod( m ) );
}
/**
* Write the content of the class to the writer.
*
* @param classFile
* the class file
* @throws WasmException
* if some Java code can't converted
*/
public void write( ClassFile classFile ) throws WasmException {
iterateMethods( classFile, m -> writeMethod( m ) );
}
private void iterateMethods( ClassFile classFile, Consumer<MethodInfo> handler ) throws WasmException {
sourceFile = null; // clear previous value for the case an IO exception occur
try {
sourceFile = classFile.getSourceFile();
if( sourceFile == null ) {
sourceFile = classFile.getThisClass().getName();
}
MethodInfo[] methods = classFile.getMethods();
for( MethodInfo method : methods ) {
Code code = method.getCode();
if( method.getName().equals( "<init>" ) && method.getDescription().equals( "()V" )
&& code.isSuperInitReturn( classFile.getSuperClass() ) ) {
continue; //default constructor
}
handler.accept( method );
}
2017-04-17 12:10:56 +02:00
} catch( IOException ioex ) {
throw WasmException.create( ioex, sourceFile, -1 );
}
}
2017-04-17 12:10:56 +02:00
/**
* Prepare the method.
*
* @param method
* the method
* @throws WasmException
* if some Java code can't converted
*/
protected void prepareMethod( MethodInfo method ) throws WasmException {
// Nothing
2017-04-17 12:10:56 +02:00
}
/**
* Write the content of a method.
*
* @param method
* the method
* @throws WasmException
* if some Java code can't converted
*/
2017-04-17 12:10:56 +02:00
private void writeMethod( MethodInfo method ) throws WasmException {
try {
Code code = method.getCode();
if( code != null ) { // abstract methods and interface methods does not have code
String methodName = method.getName();
String className = method.getDeclaringClassFile().getThisClass().getName();
String fullName = className + '.' + methodName;
String signatureName = fullName + method.getDescription();
writeExport( signatureName, fullName, method );
writeMethodStart( signatureName, fullName );
2017-04-17 12:10:56 +02:00
writeMethodSignature( method );
locals.clear();
localTable = code.getLocalVariableTable();
2018-03-27 20:04:35 +02:00
branchManager.reset();
for( CodeInputStream byteCode : code.getByteCodes() ) {
prepareBranchManager( byteCode, byteCode.getLineNumber() );
}
branchManager.calculate();
for( CodeInputStream byteCode : code.getByteCodes() ) {
writeCodeChunk( byteCode, byteCode.getLineNumber(), method.getConstantPool() );
}
2017-04-17 12:10:56 +02:00
for( int i = Math.min( paramCount, locals.size() ); i > 0; i-- ) {
locals.remove( 0 );
}
writeMethodFinish( locals );
}
2017-04-17 12:10:56 +02:00
} catch( IOException ioex ) {
throw WasmException.create( ioex, sourceFile, -1 );
}
}
/**
* Look for a Export annotation and if there write an export directive.
2017-04-17 12:10:56 +02:00
*
* @param signatureName
* the full name with signature
* @param methodName
2017-04-17 12:10:56 +02:00
* the normalized method name
* @param method
2017-04-17 12:10:56 +02:00
* the moethod
*
* @throws IOException
2017-04-17 12:10:56 +02:00
* if any IOException occur
*/
private void writeExport( String signatureName, String methodName, MethodInfo method ) throws IOException {
Annotations annotations = method.getRuntimeInvisibleAnnotations();
if( annotations != null ) {
Map<String,Object> export = annotations.get( "org.webassembly.annotation.Export" );
if( export != null ) {
String exportName = (String)export.get( "name" );
if( exportName == null ) {
exportName = method.getName(); // TODO naming conversion rule if no name was set
}
writeExport( signatureName, methodName, exportName );
}
}
}
/**
* Write an export directive
* @param signatureName
* the full name with signature
* @param methodName
* the method name
* @param exportName
* the export name, if null then the same like the method name
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeExport( String signatureName, String methodName, String exportName ) throws IOException;
/**
* Write the method header.
* @param signatureName
* the full name with signature
* @param name
* the method name
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeMethodStart( String signatureName, String name ) throws IOException;
/**
* Write the parameter and return signatures
*
* @param method
* the method
* @throws IOException
* if any I/O error occur
* @throws WasmException
* if some Java code can't converted
*/
private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException {
String signature = method.getDescription();
String kind = "param";
int paramCount = 0;
for( int i = 1; i < signature.length(); i++ ) {
paramCount++;
String javaType;
switch( signature.charAt( i ) ) {
case '[': // array
javaType = "array";
break;
case 'L':
javaType = "object";
break;
case 'B': // byte
case 'C': // char
case 'S': // short
case 'I': // int
writeMethodParam( kind, ValueType.i32 );
continue;
case 'D': // double
writeMethodParam( kind, ValueType.f64 );
continue;
case 'F': // float
writeMethodParam( kind, ValueType.f32 );
continue;
case 'J': // long
writeMethodParam( kind, ValueType.i64 );
continue;
case 'V': // void
continue;
case ')':
this.paramCount = paramCount - 1;
kind = "result";
continue;
default:
javaType = signature.substring( i, i + 1 );
}
int lineNumber = method.getCode().getFirstLineNr();
2017-04-09 18:18:53 +02:00
throw new WasmException( "Not supported Java data type in method signature: " + javaType, sourceFile, lineNumber );
}
}
/**
* Write a method parameter.
*
* @param kind
* "param", "result" or "local"
* @param valueType
* the data type of the parameter
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeMethodParam( String kind, ValueType valueType ) throws IOException;
/**
* Complete the method
*
* @param locals
* a list with types of local variables
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeMethodFinish( List<ValueType> locals ) throws IOException;
2018-03-27 20:04:35 +02:00
/**
* Write a chunk of byte code.
*
* @param byteCode
* a stream of byte code
* @param lineNumber
* the current line number
* @throws WasmException
* if some Java code can't converted
*/
private void prepareBranchManager( CodeInputStream byteCode, int lineNumber ) throws WasmException {
try {
while( byteCode.available() > 0 ) {
int op = byteCode.readUnsignedByte();
switch( op ) {
case 16: // bipush
case 18: // ldc
case 21: //iload
case 22: //lload
case 23: //fload
case 24: //dload
case 25: //aload
case 54: // istore
case 55: // lstore
case 56: // fstore
case 57: // dstore
case 58: // astore
case 179: // putstatic
case 181: // putfield
byteCode.skip(1);
break;
case 17: // sipush
2018-04-01 11:33:15 +02:00
case 19: // ldc_w
2018-03-27 20:04:35 +02:00
case 20: // ldc2_w
case 132: // iinc
case 184: // invokestatic
byteCode.skip( 2);
break;
case 153: // ifeq
case 154: // ifne
case 155: // iflt
case 156: // ifge
case 157: // ifgt
case 158: // ifle
case 159: // if_icmpeq
case 160: // if_icmpne
case 161: // if_icmplt
case 162: // if_icmpge
case 163: // if_icmpgt
case 164: // if_icmple
case 165: // if_acmpeq
case 166: // if_acmpne
int startPosition = byteCode.getCodePosition() + 2;
int offset = byteCode.readShort();
2018-03-27 20:04:35 +02:00
branchManager.start( BlockOperator.IF, startPosition, offset - 3 );
break;
case 167: // goto
startPosition = byteCode.getCodePosition() - 1;
offset = byteCode.readShort();
2018-03-27 20:04:35 +02:00
branchManager.start( BlockOperator.GOTO, startPosition, offset );
break;
}
}
} catch( Exception ex ) {
throw WasmException.create( ex, sourceFile, lineNumber );
}
}
/**
* Write a chunk of byte code.
*
* @param byteCode
* a stream of byte code
* @param lineNumber
* the current line number
2017-04-09 18:18:53 +02:00
* @param constantPool
* the constant pool of the the current class
* @throws WasmException
* if some Java code can't converted
*/
2017-04-08 18:48:45 +02:00
private void writeCodeChunk( CodeInputStream byteCode, int lineNumber, ConstantPool constantPool ) throws WasmException {
try {
while( byteCode.available() > 0 ) {
2018-03-25 12:57:04 +02:00
branchManager.handle( byteCode, this );
int op = byteCode.readUnsignedByte();
switch( op ) {
case 0: // nop
return;
2017-04-11 17:59:06 +02:00
case 2: // iconst_m1
case 3: // iconst_0
case 4: // iconst_1
2017-04-11 17:59:06 +02:00
case 5: // iconst_2
case 6: // iconst_3
case 7: // iconst_4
case 8: // iconst_5
writeConstInt( op - 3 );
break;
2017-04-11 17:59:06 +02:00
case 9: // lconst_0
case 10: // lconst_1
writeConstLong( op - 9 );
break;
2017-04-14 16:31:35 +02:00
case 11: // fconst_0
case 12: // fconst_1
case 13: // fconst_2
writeConstFloat( op - 11 );
break;
case 14: // dconst_0
case 15: // dconst_1
writeConstDouble( op - 14 );
break;
2017-04-11 17:59:06 +02:00
case 16: // bipush
2017-04-02 19:40:42 +02:00
writeConstInt( byteCode.readByte() );
break;
2017-04-14 16:31:35 +02:00
case 17: // sipush
writeConstInt( byteCode.readShort() );
break;
2017-04-11 17:59:06 +02:00
case 18: // ldc
2017-04-09 12:44:01 +02:00
writeConst( constantPool.get( byteCode.readUnsignedByte() ) );
break;
case 19: // ldc_w
2017-04-11 17:59:06 +02:00
case 20: // ldc2_w
2017-04-09 12:44:01 +02:00
writeConst( constantPool.get( byteCode.readUnsignedShort() ) );
2017-04-08 18:48:45 +02:00
break;
case 21: // iload
writeLoadStore( true, ValueType.i32, byteCode.readUnsignedByte() );
break;
case 22: // lload
writeLoadStore( true, ValueType.i64, byteCode.readUnsignedByte() );
break;
case 23: // fload
writeLoadStore( true, ValueType.f32, byteCode.readUnsignedByte() );
break;
case 24: // dload
writeLoadStore( true, ValueType.f64, byteCode.readUnsignedByte() );
break;
//TODO case 25: // aload
case 26: // iload_0
case 27: // iload_1
case 28: // iload_2
case 29: // iload_3
writeLoadStore( true, ValueType.i32, op - 26 );
break;
2017-04-09 18:46:27 +02:00
case 30: // lload_0
case 31: // lload_1
case 32: // lload_2
case 33: // lload_3
writeLoadStore( true, ValueType.i64, op - 30 );
break;
case 34: // fload_0
case 35: // fload_1
case 36: // fload_2
case 37: // fload_3
writeLoadStore( true, ValueType.f32, op - 34 );
break;
case 38: // dload_0
case 39: // dload_1
case 40: // dload_2
case 41: // dload_3
writeLoadStore( true, ValueType.f64, op - 38 );
break;
case 54: // istore
writeLoadStore( false, ValueType.i32, byteCode.readUnsignedByte() );
break;
case 55: // lstore
writeLoadStore( false, ValueType.i64, byteCode.readUnsignedByte() );
break;
case 56: // fstore
writeLoadStore( false, ValueType.f32, byteCode.readUnsignedByte() );
break;
case 57: // dstore
writeLoadStore( false, ValueType.f64, byteCode.readUnsignedByte() );
break;
//TODO case 58: // astore
case 59: // istore_0
case 60: // istore_1
case 61: // istore_2
case 62: // istore_3
writeLoadStore( false, ValueType.i32, op - 59 );
break;
2017-04-11 17:59:06 +02:00
case 63: // lstore_0
case 64: // lstore_1
case 65: // lstore_2
case 66: // lstore_3
writeLoadStore( false, ValueType.i64, op - 63 );
break;
2017-04-14 16:31:35 +02:00
case 67: // fstore_0
case 68: // fstore_1
case 69: // fstore_2
case 70: // fstore_3
writeLoadStore( false, ValueType.f32, op - 67 );
break;
case 71: // dstore_0
case 72: // dstore_1
case 73: // dstore_2
case 74: // dstore_3
writeLoadStore( false, ValueType.f64, op - 71 );
break;
case 96: // iadd
2017-04-11 21:12:27 +02:00
writeNumericOperator( NumericOperator.add, ValueType.i32);
2017-04-09 18:46:27 +02:00
break;
2017-04-09 20:45:23 +02:00
case 97: // ladd
2017-04-11 21:12:27 +02:00
writeNumericOperator( NumericOperator.add, ValueType.i64 );
2017-04-09 20:45:23 +02:00
break;
2017-04-09 18:46:27 +02:00
case 98: // fadd
2017-04-11 21:12:27 +02:00
writeNumericOperator( NumericOperator.add, ValueType.f32 );
break;
2017-04-09 20:45:23 +02:00
case 99: // dadd
2017-04-11 21:12:27 +02:00
writeNumericOperator( NumericOperator.add, ValueType.f64 );
break;
case 100: // isub
writeNumericOperator( NumericOperator.sub, ValueType.i32 );
break;
case 101: // lsub
writeNumericOperator( NumericOperator.sub, ValueType.i64 );
break;
case 102: // fsub
writeNumericOperator( NumericOperator.sub, ValueType.f32 );
break;
case 103: // dsub
writeNumericOperator( NumericOperator.sub, ValueType.f64 );
2017-04-09 20:45:23 +02:00
break;
2017-04-14 16:31:35 +02:00
case 104: // imul;
writeNumericOperator( NumericOperator.mul, ValueType.i32 );
break;
case 105: // lmul
writeNumericOperator( NumericOperator.mul, ValueType.i64 );
break;
case 106: // fmul
writeNumericOperator( NumericOperator.mul, ValueType.f32 );
break;
case 107: // dmul
writeNumericOperator( NumericOperator.mul, ValueType.f64 );
break;
case 108: // idiv
writeNumericOperator( NumericOperator.div, ValueType.i32 );
break;
case 109: // ldiv
writeNumericOperator( NumericOperator.div, ValueType.i64 );
break;
case 110: // fdiv
writeNumericOperator( NumericOperator.div, ValueType.f32 );
break;
case 111: // ddiv
writeNumericOperator( NumericOperator.div, ValueType.f64 );
break;
case 112: // irem
writeNumericOperator( NumericOperator.rem, ValueType.i32 );
break;
case 113: // lrem
writeNumericOperator( NumericOperator.rem, ValueType.i64 );
break;
case 114: // frem
case 115: // drem
throw new WasmException( "Modulo/Remainder for floating numbers is not supported in WASM. Use int or long data types." + op, sourceFile, lineNumber );
2017-04-16 12:20:53 +02:00
case 120: // ishl
writeNumericOperator( NumericOperator.shl, ValueType.i32 );
break;
case 121: // lshl
writeCast( ValueTypeConvertion.i2l ); // the shift parameter must be of type long!!!
2017-04-16 12:20:53 +02:00
writeNumericOperator( NumericOperator.shl, ValueType.i64 );
break;
case 122: // ishr
writeNumericOperator( NumericOperator.shr_s, ValueType.i32 );
break;
case 123: // lshr
writeCast( ValueTypeConvertion.i2l ); // the shift parameter must be of type long!!!
2017-04-16 12:20:53 +02:00
writeNumericOperator( NumericOperator.shr_s, ValueType.i64 );
break;
case 124: // iushr
writeNumericOperator( NumericOperator.shr_u, ValueType.i32 );
break;
case 125: // lushr
writeCast( ValueTypeConvertion.i2l ); // the shift parameter must be of type long!!!
2017-04-16 12:20:53 +02:00
writeNumericOperator( NumericOperator.shr_u, ValueType.i64 );
break;
case 126: // iand
writeNumericOperator( NumericOperator.and, ValueType.i32 );
break;
case 127: // land
writeNumericOperator( NumericOperator.and, ValueType.i64 );
break;
case 128: // ior
writeNumericOperator( NumericOperator.or, ValueType.i32 );
break;
case 129: // lor
writeNumericOperator( NumericOperator.or, ValueType.i64 );
break;
case 130: // ixor
writeNumericOperator( NumericOperator.xor, ValueType.i32 );
break;
case 131: // lxor
writeNumericOperator( NumericOperator.xor, ValueType.i64 );
break;
2017-04-16 11:28:11 +02:00
case 132: // iinc
int idx = byteCode.readUnsignedByte();
writeLoadStore( true, ValueType.i32, idx );
writeConstInt( byteCode.readUnsignedByte() );
writeNumericOperator( NumericOperator.add, ValueType.i32);
writeLoadStore( false, ValueType.i32, idx );
break;
case 133: // i2l
writeCast( ValueTypeConvertion.i2l );
break;
case 134: // i2f
writeCast( ValueTypeConvertion.i2f );
break;
case 135: // i2d
writeCast( ValueTypeConvertion.i2d );
break;
case 136: // l2i
writeCast( ValueTypeConvertion.l2i );
break;
case 137: // l2f
writeCast( ValueTypeConvertion.l2f );
break;
case 138: // l2d
writeCast( ValueTypeConvertion.l2d );
break;
case 139: // f2i
writeCast( ValueTypeConvertion.f2i );
break;
case 140: // f2l
writeCast( ValueTypeConvertion.f2l );
break;
case 141: // f2d
writeCast( ValueTypeConvertion.f2d );
break;
case 142: // d2i
writeCast( ValueTypeConvertion.d2i );
break;
case 143: // d2l
writeCast( ValueTypeConvertion.d2l );
break;
case 144: // d2f
writeCast( ValueTypeConvertion.d2f );
break;
case 145: // i2b
writeConstInt( 24 );
writeNumericOperator( NumericOperator.shl, ValueType.i32 );
writeConstInt( 24 );
writeNumericOperator( NumericOperator.shr_s, ValueType.i32 );
break;
case 146: // i2c
writeConstInt( 0xFFFF );
writeNumericOperator( NumericOperator.and, ValueType.i32 );
break;
case 147: // i2s
writeConstInt( 16 );
writeNumericOperator( NumericOperator.shl, ValueType.i32 );
writeConstInt( 16 );
writeNumericOperator( NumericOperator.shr_s, ValueType.i32 );
break;
2018-03-25 12:57:04 +02:00
case 153: // ifeq
opIfCondition( NumericOperator.ne, byteCode );
break;
2018-03-25 21:06:18 +02:00
case 154: // ifne
opIfCondition( NumericOperator.eq, byteCode );
break;
2018-03-27 20:04:35 +02:00
case 155: // iflt
opIfCondition( NumericOperator.gt, byteCode );
break;
2018-03-28 20:07:51 +02:00
case 156: // ifge
opIfCondition( NumericOperator.le_s, byteCode );
break;
case 157: // ifgt
opIfCondition( NumericOperator.lt_s, byteCode );
break;
case 158: // ifle
opIfCondition( NumericOperator.ge_s, byteCode );
break;
2018-03-25 12:57:04 +02:00
case 167: // goto
2018-03-27 20:04:35 +02:00
byteCode.skip(2);
2018-03-25 12:57:04 +02:00
break;
2017-04-02 19:40:42 +02:00
case 172: // ireturn
2017-04-08 18:48:45 +02:00
case 173: // lreturn
2017-04-09 12:44:01 +02:00
case 174: // freturn
case 175: // dreturn
case 177: // return void
writeReturn();
break;
case 184: // invokestatic
idx = byteCode.readUnsignedShort();
ConstantRef method = (ConstantRef)constantPool.get( idx );
writeFunctionCall( method.getConstantClass().getName() + '.' + method.getName() + method.getType() );
break;
default:
throw new WasmException( "Unimplemented Java byte code operation: " + op, sourceFile, lineNumber );
}
}
} catch( Exception ex ) {
2017-04-09 18:18:53 +02:00
throw WasmException.create( ex, sourceFile, lineNumber );
}
}
2018-03-25 12:57:04 +02:00
/**
* Handle the if<condition> of the Java byte code. This Java instruction compare the first stack value with value 0.
* Important: In Java 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 numOp
* The condition for the if block.
* @param byteCode
* current byte code stream to read the taget offset.
* @throws IOException
* if any I/O errors occur.
*/
private void opIfCondition( NumericOperator numOp, CodeInputStream byteCode ) throws IOException {
2018-03-27 20:04:35 +02:00
byteCode.skip(2);
2018-03-25 12:57:04 +02:00
writeConstInt( 0 );
writeNumericOperator( numOp, ValueType.i32 );
2018-03-27 20:04:35 +02:00
//writeBlockCode( BlockOperator.IF );
2018-03-25 12:57:04 +02:00
}
2017-04-09 18:18:53 +02:00
/**
* Write a constant value.
*
* @param value
* the value
* @throws IOException
* if any I/O error occur
* @throws WasmException
* if the value type is not supported
*/
2017-04-09 12:44:01 +02:00
private void writeConst( Object value ) throws IOException, WasmException {
Class<?> clazz = value.getClass();
if( clazz == Integer.class ) {
writeConstInt( ((Integer)value).intValue() );
} else if( clazz == Long.class ) {
writeConstLong( ((Long)value).longValue() );
} else if( clazz == Float.class ) {
writeConstFloat( ((Float)value).floatValue() );
} else if( clazz == Double.class ) {
writeConstDouble( ((Double)value).doubleValue() );
} else {
2017-04-09 18:18:53 +02:00
throw new WasmException( "Not supported constant type: " + clazz, sourceFile, -1 );
2017-04-09 12:44:01 +02:00
}
}
/**
* Write a constant integer value
*
* @param value
* the value
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeConstInt( int value ) throws IOException;
2017-04-08 18:48:45 +02:00
/**
* Write a constant long value
*
* @param value
* the value
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeConstLong( long value ) throws IOException;
2017-04-09 12:44:01 +02:00
/**
* Write a constant float value
*
* @param value
* the value
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeConstFloat( float value ) throws IOException;
/**
* Write a constant double value
*
* @param value
* the value
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeConstDouble( double value ) throws IOException;
2017-04-08 18:48:45 +02:00
/**
* Write or Load a local variable.
*
* @param load
* true: if load
* @param valueType
* the type of the variable
* @param idx
2017-04-09 22:45:52 +02:00
* the memory/slot idx of the variable
2017-04-08 18:48:45 +02:00
* @throws WasmException
* occur a if a variable was used for a different type
* @throws IOException
* if any I/O error occur
*/
private void writeLoadStore( boolean load, @Nonnull ValueType valueType, @Nonnegative int idx ) throws WasmException, IOException {
2017-04-09 22:45:52 +02:00
idx = localTable.get( idx ).getPosition(); // translate slot index to position index
while( locals.size() <= idx ) {
locals.add( null );
}
ValueType oldType = locals.get( idx );
if( oldType != null && oldType != valueType ) {
2017-04-09 18:18:53 +02:00
throw new WasmException( "Redefine local variable type from " + oldType + " to " + valueType, sourceFile, -1 );
}
locals.set( idx, valueType );
if( load ) {
writeLoad( idx );
} else {
writeStore( idx );
}
}
/**
* Write a variable load.
*
* @param idx
* the index of the parameter variable
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeLoad( int idx ) throws IOException;
/**
* Write a variable store.
*
* @param idx
* the index of the parameter variable
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeStore( int idx ) throws IOException;
/**
* Write a add operator
*
* @param numOp
* the numeric operation
2017-04-09 18:46:27 +02:00
* @param valueType
* the type of the parameters
*
* @throws IOException
* if any I/O error occur
*/
2017-04-11 21:12:27 +02:00
protected abstract void writeNumericOperator( NumericOperator numOp, @Nullable ValueType valueType ) throws IOException;
/**
* Cast a value from one type to another
*
* @param cast
* the operator
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeCast( ValueTypeConvertion cast ) throws IOException;
/**
* Write a return
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeReturn() throws IOException;
/**
* Write a call to a function.
*
* @param name
* the full qualified method name
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeFunctionCall( String name ) throws IOException;
2018-03-25 12:57:04 +02:00
/**
* Write a block/branch code
*
* @param op
* the operation
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeBlockCode( BlockOperator op ) throws IOException;
}