2017-03-31 20:43:57 +02:00
/ *
2018-03-27 20:04:35 +02:00
* Copyright 2017 - 2018 Volker Berlin ( i - net software )
2017-03-31 20:43:57 +02:00
*
* 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 ;
2017-03-31 20:43:57 +02:00
import javax.annotation.Nonnegative ;
import javax.annotation.Nonnull ;
2017-04-09 18:46:27 +02:00
import javax.annotation.Nullable ;
2017-03-31 20:43:57 +02:00
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 ;
2017-03-31 20:43:57 +02:00
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-03-31 20:43:57 +02:00
/ * *
2017-04-17 12:10:56 +02:00
* Prepare the content of the class .
2017-03-31 20:43:57 +02:00
*
* @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-03-31 20:43:57 +02:00
}
2017-04-17 12:10:56 +02:00
} catch ( IOException ioex ) {
throw WasmException . create ( ioex , sourceFile , - 1 ) ;
2017-03-31 20:43:57 +02:00
}
}
2017-04-17 12:10:56 +02:00
/ * *
* Prepare the method .
*
* @param method
* the method
* @throws WasmException
* if some Java code can ' t converted
* /
2018-03-24 12:33:56 +01:00
protected void prepareMethod ( MethodInfo method ) throws WasmException {
// Nothing
2017-04-17 12:10:56 +02:00
}
2017-03-31 20:43:57 +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
2018-03-24 12:33:56 +01:00
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-03-31 20:43:57 +02:00
}
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-03-31 20:43:57 +02:00
}
2017-04-17 12:10:56 +02:00
} catch ( IOException ioex ) {
throw WasmException . create ( ioex , sourceFile , - 1 ) ;
2017-03-31 20:43:57 +02:00
}
}
/ * *
* Look for a Export annotation and if there write an export directive .
2017-04-17 12:10:56 +02:00
*
2018-03-24 12:33:56 +01:00
* @param signatureName
* the full name with signature
2017-03-31 20:43:57 +02:00
* @param methodName
2017-04-17 12:10:56 +02:00
* the normalized method name
2017-03-31 20:43:57 +02:00
* @param method
2017-04-17 12:10:56 +02:00
* the moethod
2018-03-24 12:33:56 +01:00
*
2017-03-31 20:43:57 +02:00
* @throws IOException
2017-04-17 12:10:56 +02:00
* if any IOException occur
2017-03-31 20:43:57 +02:00
* /
2018-03-24 12:33:56 +01:00
private void writeExport ( String signatureName , String methodName , MethodInfo method ) throws IOException {
2017-03-31 20:43:57 +02:00
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 ) {
2018-03-24 12:33:56 +01:00
exportName = method . getName ( ) ; // TODO naming conversion rule if no name was set
2017-03-31 20:43:57 +02:00
}
2018-03-24 12:33:56 +01:00
writeExport ( signatureName , methodName , exportName ) ;
2017-03-31 20:43:57 +02:00
}
}
}
/ * *
* Write an export directive
2018-03-24 12:33:56 +01:00
* @param signatureName
* the full name with signature
2017-03-31 20:43:57 +02:00
* @param methodName
* the method name
* @param exportName
* the export name , if null then the same like the method name
2018-03-24 12:33:56 +01:00
*
2017-03-31 20:43:57 +02:00
* @throws IOException
* if any I / O error occur
* /
2018-03-24 12:33:56 +01:00
protected abstract void writeExport ( String signatureName , String methodName , String exportName ) throws IOException ;
2017-03-31 20:43:57 +02:00
/ * *
* Write the method header .
2018-03-24 12:33:56 +01:00
* @param signatureName
* the full name with signature
2017-03-31 20:43:57 +02:00
* @param name
* the method name
2018-03-24 12:33:56 +01:00
*
2017-03-31 20:43:57 +02:00
* @throws IOException
* if any I / O error occur
* /
2018-03-24 12:33:56 +01:00
protected abstract void writeMethodStart ( String signatureName , String name ) throws IOException ;
2017-03-31 20:43:57 +02:00
/ * *
* 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
2018-03-31 19:34:27 +02:00
case 'S' : // short
case 'I' : // int
writeMethodParam ( kind , ValueType . i32 ) ;
continue ;
2017-03-31 20:43:57 +02:00
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 ;
2018-03-24 12:46:47 +01:00
kind = " result " ;
2017-03-31 20:43:57 +02:00
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 ) ;
2017-03-31 20:43:57 +02:00
}
}
/ * *
* Write a method parameter .
*
* @param kind
2018-03-24 12:46:47 +01:00
* " param " , " result " or " local "
2017-03-31 20:43:57 +02:00
* @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 ;
2018-03-31 19:34:27 +02:00
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 ;
2018-03-31 19:34:27 +02:00
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 ) ;
}
}
2017-03-31 20:43:57 +02:00
/ * *
* 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
2017-03-31 20:43:57 +02:00
* @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 {
2017-03-31 20:43:57 +02:00
try {
while ( byteCode . available ( ) > 0 ) {
2018-03-25 12:57:04 +02:00
branchManager . handle ( byteCode , this ) ;
2017-03-31 20:43:57 +02:00
int op = byteCode . readUnsignedByte ( ) ;
switch ( op ) {
2018-03-31 19:34:27 +02:00
case 0 : // nop
return ;
2018-04-02 11:53:12 +02:00
//TODO case 1: // aconst_null
2017-04-11 17:59:06 +02:00
case 2 : // iconst_m1
case 3 : // iconst_0
2017-03-31 20:43:57 +02:00
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 ) ;
2017-03-31 20:43:57 +02:00
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 ;
2018-03-31 19:34:27 +02:00
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 ;
2018-03-31 19:34:27 +02:00
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
2017-03-31 20:43:57 +02:00
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 ;
2018-03-31 19:34:27 +02:00
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
2017-03-31 20:43:57 +02:00
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 ;
2018-04-02 11:53:12 +02:00
case 87 : // pop
case 88 : // pop2
writeBlockCode ( BlockOperator . DROP ) ;
break ;
case 89 : // dup: duplicate the value on top of the stack
case 90 : // dup_x1
case 91 : // dup_x2
case 92 : // dup2
case 93 : // dup2_x1
case 94 : // dup2_x2
case 95 : // swap
// 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 , sourceFile , lineNumber ) ;
2017-03-31 20:43:57 +02:00
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 ) ;
2017-03-31 20:43:57 +02:00
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
2017-04-16 23:24:37 +02:00
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
2017-04-16 23:24:37 +02:00
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
2017-04-16 23:24:37 +02:00
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 ;
2018-03-31 19:34:27 +02:00
case 133 : // i2l
writeCast ( ValueTypeConvertion . i2l ) ;
break ;
2018-04-02 10:48:24 +02:00
case 134 : // i2f
writeCast ( ValueTypeConvertion . i2f ) ;
break ;
case 135 : // i2d
writeCast ( ValueTypeConvertion . i2d ) ;
break ;
2017-04-11 17:47:21 +02:00
case 136 : // l2i
writeCast ( ValueTypeConvertion . l2i ) ;
break ;
2018-04-02 10:48:24 +02:00
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 ;
2018-03-31 19:34:27 +02:00
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
2017-03-31 20:43:57 +02:00
case 177 : // return void
2018-04-02 11:53:12 +02:00
writeBlockCode ( BlockOperator . RETURN ) ;
2017-03-31 20:43:57 +02:00
break ;
2018-03-24 12:33:56 +01:00
case 184 : // invokestatic
idx = byteCode . readUnsignedShort ( ) ;
ConstantRef method = ( ConstantRef ) constantPool . get ( idx ) ;
writeFunctionCall ( method . getConstantClass ( ) . getName ( ) + '.' + method . getName ( ) + method . getType ( ) ) ;
break ;
2017-03-31 20:43:57 +02:00
default :
2018-03-31 19:34:27 +02:00
throw new WasmException ( " Unimplemented Java byte code operation: " + op , sourceFile , lineNumber ) ;
2017-03-31 20:43:57 +02:00
}
}
2018-04-02 11:53:12 +02:00
} catch ( WasmException ex ) {
throw ex ;
2017-03-31 20:43:57 +02:00
} catch ( Exception ex ) {
2017-04-09 18:18:53 +02:00
throw WasmException . create ( ex , sourceFile , lineNumber ) ;
2017-03-31 20:43:57 +02:00
}
}
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
}
}
2017-03-31 20:43:57 +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
* /
2017-03-31 20:43:57 +02:00
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
2017-03-31 20:43:57 +02:00
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 ) ;
2017-03-31 20:43:57 +02:00
}
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
2018-03-24 12:33:56 +01:00
*
* @param numOp
* the numeric operation
2017-04-09 18:46:27 +02:00
* @param valueType
* the type of the parameters
*
2017-03-31 20:43:57 +02:00
* @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 ;
2017-03-31 20:43:57 +02:00
2017-04-11 17:47:21 +02:00
/ * *
* 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 ;
2018-03-24 12:33:56 +01:00
/ * *
* 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 ;
2017-03-31 20:43:57 +02:00
}