2018-11-04 20:28:42 +01:00
/ *
2020-01-11 20:31:05 +01:00
* Copyright 2018 - 2020 Volker Berlin ( i - net software )
2018-11-04 20:28:42 +01: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 ;
2019-08-11 13:11:22 +02:00
import java.io.IOException ;
2018-11-09 20:14:30 +01:00
import java.util.ArrayList ;
2020-03-27 21:10:02 +01:00
import java.util.Iterator ;
2018-11-04 20:28:42 +01:00
import java.util.List ;
2019-10-27 20:11:47 +01:00
import java.util.function.Function ;
2018-11-04 20:28:42 +01:00
2018-11-09 20:14:30 +01:00
import javax.annotation.Nonnegative ;
import javax.annotation.Nonnull ;
import javax.annotation.Nullable ;
2019-08-11 13:11:22 +02:00
import de.inetsoftware.classparser.ClassFile ;
2020-03-15 15:49:52 +01:00
import de.inetsoftware.classparser.ConstantClass ;
2019-04-06 17:51:29 +02:00
import de.inetsoftware.classparser.LocalVariableTable ;
2018-11-18 12:17:08 +01:00
import de.inetsoftware.classparser.Member ;
2019-08-11 13:11:22 +02:00
import de.inetsoftware.classparser.MethodInfo ;
import de.inetsoftware.jwebassembly.WasmException ;
2020-02-09 18:05:31 +01:00
import de.inetsoftware.jwebassembly.javascript.JavaScriptNewMultiArrayFunctionName ;
2019-08-11 13:11:22 +02:00
import de.inetsoftware.jwebassembly.javascript.NonGC ;
2020-03-29 18:35:01 +02:00
import de.inetsoftware.jwebassembly.module.StackInspector.StackValue ;
2019-03-03 21:33:09 +01:00
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 ;
2020-02-09 18:05:31 +01:00
import de.inetsoftware.jwebassembly.wasm.ArrayType ;
2019-11-18 21:32:35 +01:00
import de.inetsoftware.jwebassembly.wasm.MemoryOperator ;
2019-04-21 21:33:22 +02:00
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 ;
2019-04-27 21:14:55 +02:00
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser ;
2019-10-05 16:15:18 +02:00
import de.inetsoftware.jwebassembly.wasm.VariableOperator ;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator ;
2018-11-09 20:14:30 +01:00
2018-11-04 20:28:42 +01:00
/ * *
* Base class for Code Building .
*
* @author Volker Berlin
* /
public abstract class WasmCodeBuilder {
2018-11-09 20:14:30 +01:00
private final LocaleVariableManager localVariables = new LocaleVariableManager ( ) ;
private final List < WasmInstruction > instructions = new ArrayList < > ( ) ;
2019-01-23 20:27:57 +01:00
private TypeManager types ;
2019-09-08 13:55:22 +02:00
private FunctionManager functions ;
2019-09-09 21:07:45 +02:00
private WasmOptions options ;
2019-08-08 19:28:27 +02:00
2019-11-10 12:57:58 +01:00
private StringManager strings ;
2019-11-03 13:52:07 +01:00
2020-01-02 15:15:21 +01:00
private ClassFileLoader classFileLoader ;
2019-10-02 18:56:35 +02:00
/ * *
* Initialize the code builder ;
*
* @param options
* compiler properties
2020-02-24 21:08:29 +01:00
* @param classFileLoader
* for loading the class files
2019-10-02 18:56:35 +02:00
* /
2020-02-24 21:08:29 +01:00
void init ( WasmOptions options , ClassFileLoader classFileLoader ) {
this . localVariables . init ( options . types ) ;
this . types = options . types ;
this . functions = options . functions ;
this . strings = options . strings ;
2019-10-02 18:56:35 +02:00
this . options = options ;
2020-01-02 15:15:21 +01:00
this . classFileLoader = classFileLoader ;
2019-10-02 18:56:35 +02:00
}
2018-11-04 20:28:42 +01:00
/ * *
* Get the list of instructions
*
* @return the list
* /
2020-04-13 16:05:41 +02:00
@Nonnull
2018-11-09 20:14:30 +01:00
List < WasmInstruction > getInstructions ( ) {
return instructions ;
}
2018-11-04 20:28:42 +01:00
2020-04-13 16:05:41 +02:00
/ * *
* Get the manager of local variables
* @return the manager
* /
@Nonnull
LocaleVariableManager getLocalVariables ( ) {
return localVariables ;
}
2019-03-03 21:33:09 +01:00
/ * *
* Check if the last instruction is a return instruction
*
* @return true , if a return
* /
boolean isEndsWithReturn ( ) {
WasmInstruction instr = instructions . get ( instructions . size ( ) - 1 ) ;
if ( instr . getType ( ) = = Type . Block ) {
return ( ( WasmBlockInstruction ) instr ) . getOperation ( ) = = WasmBlockOperator . RETURN ;
}
return false ;
}
2019-01-23 20:27:57 +01:00
/ * *
2019-10-02 18:56:35 +02:00
* We need one value from the stack inside of a block . We need to find the WasmInstruction on which the block can
* start . If this a function call or numeric expression this can be complex to find the right point .
2019-01-23 20:27:57 +01:00
*
2019-10-02 18:56:35 +02:00
* @param count
* the count of values on the stack back . 1 means the last value . 2 means the penultimate value .
* @return the code position that push the last instruction
2019-01-23 20:27:57 +01:00
* /
2020-03-01 19:02:49 +01:00
int findBlockStartCodePosition ( int count ) {
return findBlockStart ( count , true ) ;
2019-10-05 16:15:18 +02:00
}
/ * *
* We need one value from the stack inside of a block . We need to find the WasmInstruction on which the block can
* start . If this a function call or numeric expression this can be complex to find the right point .
*
* @param count
* the count of values on the stack back . 1 means the last value . 2 means the penultimate value .
* @param codePosition
* true , get the code position ; false , get the index in the instructions
* @return the code position that push the last instruction
* /
2020-03-01 19:02:49 +01:00
private int findBlockStart ( int count , boolean codePosition ) {
2019-10-02 18:56:35 +02:00
int valueCount = 0 ;
List < WasmInstruction > instructions = this . instructions ;
for ( int i = instructions . size ( ) - 1 ; i > = 0 ; i - - ) {
WasmInstruction instr = instructions . get ( i ) ;
AnyType valueType = instr . getPushValueType ( ) ;
if ( valueType ! = null ) {
valueCount + + ;
}
valueCount - = instr . getPopCount ( ) ;
if ( valueCount = = count ) {
2019-10-05 16:15:18 +02:00
return codePosition ? instr . getCodePosition ( ) : i ;
2019-10-02 18:56:35 +02:00
}
}
throw new WasmException ( " Start position not found " , - 1 ) ; // should never occur
}
/ * *
* We need the value type from the stack .
*
* @param count
* the count of values on the stack back . 1 means the last value . 2 means the penultimate value .
2020-03-29 14:31:58 +02:00
* @param javaCodePos
* current code position for which the stack is inspected
2019-10-02 18:56:35 +02:00
* @return the type of the last push value
* /
@Nonnull
2020-03-29 14:31:58 +02:00
AnyType findValueTypeFromStack ( int count , int javaCodePos ) {
return StackInspector . findInstructionThatPushValue ( instructions , count , javaCodePos ) . instr . getPushValueType ( ) ;
2020-03-01 19:02:49 +01:00
}
/ * *
* Find the instruction that push the x - th value to the stack .
*
* @param count
* the count of values on the stack back . 1 means the last value . 2 means the penultimate value .
2020-03-29 14:31:58 +02:00
* @param javaCodePos
* current code position for which the stack is inspected
2020-03-01 19:02:49 +01:00
* @return the instruction
* /
2020-03-29 14:31:58 +02:00
private WasmInstruction findInstructionThatPushValue ( int count , int javaCodePos ) {
return StackInspector . findInstructionThatPushValue ( instructions , count , javaCodePos ) . instr ;
2019-01-23 20:27:57 +01:00
}
2018-11-04 20:28:42 +01:00
/ * *
* Get the data types of the local variables . The value is only valid until the next call .
*
* @param paramCount
* the count of method parameter which should be exclude
* @return the reused list with fresh values
* /
2019-01-20 19:58:23 +01:00
List < AnyType > getLocalTypes ( int paramCount ) {
2018-11-09 20:14:30 +01:00
return localVariables . getLocalTypes ( paramCount ) ;
}
2019-04-14 14:29:09 +02:00
/ * *
* Get the name of the variable or null if no name available
*
* @param idx
* the wasm variable index
* @return the name
* /
String getLocalName ( int idx ) {
return localVariables . getLocalName ( idx ) ;
}
/ * *
* Get the slot of the temporary variable .
*
* @param valueType
* the valueType for the variable
* @param startCodePosition
* the start of the Java code position
* @param endCodePosition
* the end of the Java code position
* @return the slot
* /
int getTempVariable ( AnyType valueType , int startCodePosition , int endCodePosition ) {
return localVariables . getTempVariable ( valueType , startCodePosition , endCodePosition ) ;
}
2019-04-26 17:28:57 +02:00
/ * *
* Get the type manager .
*
* @return the type manager
* /
protected TypeManager getTypeManager ( ) {
return types ;
}
2018-11-09 20:14:30 +01:00
/ * *
* Reset the code builder .
2019-04-06 17:51:29 +02:00
*
* @param variableTable
* variable table of the Java method .
2019-09-23 20:33:21 +02:00
* @param method
2020-03-27 21:10:02 +01:00
* the method with signature as fallback for a missing variable table . If null signature is used and the method must be static .
* @param signature
* alternative for method signature , can be null if method is set
2018-11-09 20:14:30 +01:00
* /
2020-03-27 21:10:02 +01:00
protected void reset ( LocalVariableTable variableTable , MethodInfo method , Iterator < AnyType > signature ) {
2018-11-09 20:14:30 +01:00
instructions . clear ( ) ;
2020-03-27 21:10:02 +01:00
localVariables . reset ( variableTable , method , signature ) ;
2018-11-09 20:14:30 +01:00
}
/ * *
* Calculate the index of the variables
* /
protected void calculateVariables ( ) {
localVariables . calculate ( ) ;
}
2018-11-04 20:28:42 +01:00
2018-11-09 20:14:30 +01:00
/ * *
* Create a WasmLoadStoreInstruction .
*
* @param valueType
* the value type
* @param load
* true : if load
2018-11-10 10:46:36 +01:00
* @param javaIdx
* the memory / slot index of the variable in Java byte code
2018-11-09 20:14:30 +01:00
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addLoadStoreInstruction ( AnyType valueType , boolean load , @Nonnegative int javaIdx , int javaCodePos , int lineNumber ) {
2019-04-06 17:51:29 +02:00
localVariables . use ( valueType , javaIdx , javaCodePos ) ;
2020-03-29 21:24:09 +02:00
instructions . add ( new WasmLoadStoreInstruction ( load ? VariableOperator . get : VariableOperator . set , javaIdx , localVariables , javaCodePos , lineNumber ) ) ;
2018-11-10 10:46:36 +01:00
}
/ * *
2019-11-03 19:00:49 +01:00
* Create a WasmLoadStoreInstruction local . get / local . set .
2018-11-10 10:46:36 +01:00
*
2019-11-09 18:48:28 +01:00
* @param op
* the operation
2018-11-10 10:46:36 +01:00
* @param wasmIdx
* the index of the variable
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-10 10:46:36 +01:00
* /
2019-11-09 18:48:28 +01:00
protected void addLocalInstruction ( VariableOperator op , @Nonnegative int wasmIdx , int javaCodePos , int lineNumber ) {
2019-11-12 20:47:57 +01:00
switch ( op ) {
case set :
case tee :
2020-03-29 14:31:58 +02:00
AnyType valueType = findValueTypeFromStack ( 1 , javaCodePos ) ;
2019-11-12 20:47:57 +01:00
localVariables . useIndex ( valueType , wasmIdx ) ;
}
2020-03-01 19:02:49 +01:00
instructions . add ( new WasmLocalInstruction ( op , wasmIdx , localVariables , javaCodePos , lineNumber ) ) ;
2018-11-09 20:14:30 +01:00
}
2019-11-16 19:17:22 +01:00
/ * *
* Create a WasmDupInstruction .
*
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
protected void addDupInstruction ( int javaCodePos , int lineNumber ) {
2020-03-29 14:31:58 +02:00
WasmInstruction instr = findInstructionThatPushValue ( 1 , javaCodePos ) ;
2020-03-01 19:02:49 +01:00
AnyType type = instr . getPushValueType ( ) ;
int varIndex = - 1 ;
// if it is a GET to a local variable then we can use it
if ( instr . getType ( ) = = Type . Local ) {
WasmLocalInstruction local1 = ( WasmLocalInstruction ) instr ;
if ( local1 . getOperator ( ) = = VariableOperator . get ) {
varIndex = local1 . getIndex ( ) ;
}
}
//alternate we need to create a new locale variable
if ( varIndex < 0 ) {
varIndex = getTempVariable ( type , javaCodePos , javaCodePos + 1 ) ;
2020-03-29 21:24:09 +02:00
instructions . add ( new WasmLoadStoreInstruction ( VariableOperator . tee , varIndex , localVariables , javaCodePos , lineNumber ) ) ;
instructions . add ( new WasmLoadStoreInstruction ( VariableOperator . get , varIndex , localVariables , javaCodePos , lineNumber ) ) ;
2020-03-01 19:02:49 +01:00
// an alternative solution can be a function call with multiple return values but this seems to be slower
// new SyntheticFunctionName( "dup" + storeType, "local.get 0 local.get 0 return", storeType, null, storeType, storeType ), codePos, lineNumber )
} else {
instructions . add ( new WasmLocalInstruction ( VariableOperator . get , varIndex , localVariables , javaCodePos , lineNumber ) ) ;
}
2019-11-16 19:17:22 +01:00
}
2018-11-09 20:14:30 +01:00
/ * *
* 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
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addGlobalInstruction ( boolean load , Member ref , int javaCodePos , int lineNumber ) {
2019-04-27 21:14:55 +02:00
FunctionName name = new FunctionName ( ref ) ;
AnyType type = new ValueTypeParser ( ref . getType ( ) , types ) . next ( ) ;
instructions . add ( new WasmGlobalInstruction ( load , name , type , javaCodePos , lineNumber ) ) ;
2020-04-05 21:03:13 +02:00
functions . markClassAsUsed ( name . className ) ;
2018-11-09 20:14:30 +01:00
}
2019-11-03 19:00:49 +01:00
/ * *
* Add a WasmTableInstruction table . get / table . set .
*
* @param load
* true : if load
* @param idx
* the index of the table
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
@Nonnull
protected void addTableInstruction ( boolean load , @Nonnegative int idx , int javaCodePos , int lineNumber ) {
instructions . add ( new WasmTableInstruction ( load , idx , javaCodePos , lineNumber ) ) ;
}
2018-11-09 20:14:30 +01:00
/ * *
* Add a constant instruction .
*
* @param value
* the value
* @param valueType
* the value type
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addConstInstruction ( Number value , ValueType valueType , int javaCodePos , int lineNumber ) {
instructions . add ( new WasmConstInstruction ( value , valueType , javaCodePos , lineNumber ) ) ;
2018-11-09 20:14:30 +01:00
}
/ * *
* Add a constant instruction with unknown value type .
*
* @param value
* the value
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-11-03 13:52:07 +01:00
protected void addConstInstruction ( Object value , int javaCodePos , int lineNumber ) {
if ( value . getClass ( ) = = String . class ) {
Integer id = strings . get ( value ) ;
2019-11-24 14:44:56 +01:00
FunctionName name = strings . getStringConstantFunction ( ) ;
2019-11-03 19:00:49 +01:00
instructions . add ( new WasmConstInstruction ( id , ValueType . i32 , javaCodePos , lineNumber ) ) ;
2020-03-22 12:36:01 +01:00
instructions . add ( new WasmCallInstruction ( name , javaCodePos , lineNumber , types , false , ( String ) value ) ) ;
2020-02-23 17:51:32 +01:00
} else if ( value instanceof Number ) {
2019-11-03 13:52:07 +01:00
instructions . add ( new WasmConstInstruction ( ( Number ) value , javaCodePos , lineNumber ) ) ;
2020-03-15 15:49:52 +01:00
} else if ( value instanceof ConstantClass ) {
String className = ( ( ConstantClass ) value ) . getName ( ) ;
Integer id = types . valueOf ( className ) . getClassIndex ( ) ;
FunctionName name = types . getClassConstantFunction ( ) ;
instructions . add ( new WasmConstInstruction ( id , ValueType . i32 , javaCodePos , lineNumber ) ) ;
addCallInstruction ( name , javaCodePos , lineNumber ) ;
2020-02-23 17:51:32 +01:00
} else {
//TODO There can be ConstantClass, MethodType and MethodHandle
throw new WasmException ( " Class constants are not supported. : " + value , lineNumber ) ;
2019-11-03 13:52:07 +01:00
}
2018-11-09 20:14:30 +01:00
}
/ * *
* Add a numeric operation instruction
*
* @param numOp
* the operation
* @param valueType
* the value type
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2019-09-08 21:45:28 +02:00
* @return the added instruction
2018-11-09 20:14:30 +01:00
* /
2019-09-08 21:45:28 +02:00
protected WasmNumericInstruction addNumericInstruction ( @Nullable NumericOperator numOp , @Nullable ValueType valueType , int javaCodePos , int lineNumber ) {
WasmNumericInstruction numeric = new WasmNumericInstruction ( numOp , valueType , javaCodePos , lineNumber ) ;
instructions . add ( numeric ) ;
2020-02-23 20:18:57 +01:00
if ( ! options . useGC ( ) & & options . ref_eq = = null & & ( numOp = = NumericOperator . ref_eq | | numOp = = NumericOperator . ref_ne ) ) {
2020-01-11 20:31:05 +01:00
functions . markAsNeeded ( options . ref_eq = getNonGC ( " ref_eq " , lineNumber ) ) ;
2019-09-08 21:45:28 +02:00
}
return numeric ;
2018-11-09 20:14:30 +01:00
}
/ * *
* Add a value convert / cast instruction .
*
* @param conversion
* the conversion
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addConvertInstruction ( ValueTypeConvertion conversion , int javaCodePos , int lineNumber ) {
instructions . add ( new WasmConvertInstruction ( conversion , javaCodePos , lineNumber ) ) ;
2018-11-09 20:14:30 +01:00
}
/ * *
* Add a static function call .
*
2019-01-20 10:41:33 +01:00
* @param name
* the function name that should be called
2018-11-09 20:14:30 +01:00
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addCallInstruction ( FunctionName name , int javaCodePos , int lineNumber ) {
2020-02-29 16:14:32 +01:00
name = functions . markAsNeeded ( name ) ;
2020-01-11 21:48:04 +01:00
boolean needThisParameter = functions . needThisParameter ( name ) ;
WasmCallInstruction instruction = new WasmCallInstruction ( name , javaCodePos , lineNumber , types , needThisParameter ) ;
2019-10-27 20:11:47 +01:00
if ( " <init> " . equals ( name . methodName ) ) {
2019-11-17 17:28:31 +01:00
// check if there a factory for the constructor in JavaScript then we need to do some more complex patching
2019-10-27 20:11:47 +01:00
Function < String , Object > importAnannotation = functions . getImportAnannotation ( name ) ;
2019-11-17 17:28:31 +01:00
FunctionName factoryName = null ;
if ( importAnannotation ! = null ) { // JavaScript replacement for a constructor via import
// The new signature need a return value. The <init> of Java has ever a void return value
String signature = name . signature ;
signature = signature . substring ( 0 , signature . length ( ) - 1 ) + " Ljava/lang/Object; " ;
factoryName = new ImportSyntheticFunctionName ( " String " , " init " , signature , importAnannotation ) ;
} else {
MethodInfo replace = functions . replace ( name , null ) ;
if ( replace ! = null & & ! " <init> " . equals ( replace . getName ( ) ) ) {
// the constructor was replaced with a factory method. Typical this method called then a JavaScript replacement
factoryName = new FunctionName ( replace ) ;
}
}
if ( factoryName ! = null ) {
// the constructor was replaced we need also replace the create instance instruction
2019-10-27 20:11:47 +01:00
List < WasmInstruction > instructions = this . instructions ;
for ( int i = instructions . size ( ) - 1 ; i > = 0 ; i - - ) {
WasmInstruction instr = instructions . get ( i ) ;
if ( instr . getType ( ) = = Type . Struct ) {
WasmStructInstruction struct = ( WasmStructInstruction ) instr ;
if ( struct . getOperator ( ) = = StructOperator . NEW_DEFAULT ) {
2019-11-16 19:17:22 +01:00
instructions . set ( i , new WasmNopInstruction ( struct . getCodePosition ( ) , struct . getLineNumber ( ) ) ) ; // replace NEW_DEFAULT with Nop, Nop because the code position can be needed for the branch manager
instr = instructions . get ( + + i ) ;
2020-03-29 21:24:09 +02:00
// if( instr.getType() == Type.Dup ) {
// instructions.remove( i ); // dup of the instance reference if it is later assign, missing if the object after the constructor is never assign
// }
2019-10-27 20:11:47 +01:00
break ;
}
}
}
2019-11-17 17:28:31 +01:00
// the new instruction
2020-01-11 21:48:04 +01:00
instruction = new WasmCallInstruction ( factoryName , javaCodePos , lineNumber , types , false ) ;
2019-10-27 20:11:47 +01:00
}
}
instructions . add ( instruction ) ;
2020-04-05 21:03:13 +02:00
functions . markClassAsUsed ( name . className ) ;
2018-11-09 20:14:30 +01:00
}
2019-05-14 21:47:49 +02:00
/ * *
2020-02-24 18:16:36 +01:00
* Add indirect call to the instruction .
2019-05-14 21:47:49 +02:00
*
2020-02-24 18:16:36 +01:00
* @param indirectCall
* the instruction
2019-05-14 21:47:49 +02:00
* /
2020-02-24 18:16:36 +01:00
private void addCallIndirectInstruction ( WasmCallIndirectInstruction indirectCall ) {
// For access to the vtable the THIS parameter must be duplicated on stack before the function parameters
2020-03-29 18:35:01 +02:00
// find the instruction that THIS push on the stack
2020-02-24 18:16:36 +01:00
int count = indirectCall . getPopCount ( ) ;
2020-03-29 18:35:01 +02:00
int javaCodePos = indirectCall . getCodePosition ( ) ;
StackValue stackValue = StackInspector . findInstructionThatPushValue ( instructions , count , javaCodePos ) ;
WasmInstruction instr = stackValue . instr ;
2020-02-24 18:16:36 +01:00
int varIndex = - 1 ;
// if it is a GET to a local variable then we can use it
2019-10-05 16:15:18 +02:00
if ( instr . getType ( ) = = Type . Local ) {
WasmLocalInstruction local1 = ( WasmLocalInstruction ) instr ;
if ( local1 . getOperator ( ) = = VariableOperator . get ) {
varIndex = local1 . getIndex ( ) ;
}
}
2020-02-24 18:16:36 +01:00
//alternate we need to create a new locale variable
2019-10-05 16:15:18 +02:00
if ( varIndex < 0 ) {
2020-02-24 18:16:36 +01:00
varIndex = getTempVariable ( indirectCall . getThisType ( ) , instr . getCodePosition ( ) , javaCodePos + 1 ) ;
2020-03-29 18:35:01 +02:00
int idx = count = = 1 ? instructions . size ( ) : stackValue . idx + 1 ;
instructions . add ( idx , new DupThis ( indirectCall , varIndex , instr . getCodePosition ( ) + 1 ) ) ;
2019-10-05 16:15:18 +02:00
}
2020-02-24 18:16:36 +01:00
indirectCall . setVariableIndexOfThis ( varIndex ) ;
instructions . add ( indirectCall ) ;
2020-02-26 18:02:59 +01:00
options . registerGet_i32 ( ) ; // for later access of the vtable
2019-05-14 21:47:49 +02:00
}
2020-02-24 18:16:36 +01:00
/ * *
* Add a virtual / method function call .
*
* @param name
* the function name that should be called
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
protected void addCallVirtualInstruction ( FunctionName name , int javaCodePos , int lineNumber ) {
2020-02-29 16:14:32 +01:00
name = functions . markAsNeeded ( name ) ;
2020-02-24 18:16:36 +01:00
addCallIndirectInstruction ( new WasmCallVirtualInstruction ( name , javaCodePos , lineNumber , types , options ) ) ;
2020-02-25 18:05:12 +01:00
options . getCallVirtual ( ) ; // mark the function as needed
2020-04-05 21:03:13 +02:00
functions . markClassAsUsed ( name . className ) ;
2020-02-24 18:16:36 +01:00
}
2020-01-25 23:42:22 +01:00
/ * *
* Add interface function call
* @param name
* the function name that should be called
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
protected void addCallInterfaceInstruction ( FunctionName name , int javaCodePos , int lineNumber ) {
2020-02-29 16:14:32 +01:00
//TODO name = functions.markAsNeeded( name );
2020-02-24 18:16:36 +01:00
addCallIndirectInstruction ( new WasmCallInterfaceInstruction ( name , javaCodePos , lineNumber , types , options ) ) ;
2020-04-05 21:03:13 +02:00
functions . markClassAsUsed ( name . className ) ;
2020-01-25 23:42:22 +01:00
}
2018-11-09 20:14:30 +01:00
/ * *
* 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
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addBlockInstruction ( WasmBlockOperator op , @Nullable Object data , int javaCodePos , int lineNumber ) {
instructions . add ( new WasmBlockInstruction ( op , data , javaCodePos , lineNumber ) ) ;
2018-11-09 20:14:30 +01:00
}
/ * *
* 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
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-11-09 20:14:30 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addNopInstruction ( int javaCodePos , int lineNumber ) {
instructions . add ( new WasmNopInstruction ( javaCodePos , lineNumber ) ) ;
2018-11-09 20:14:30 +01:00
}
2018-12-02 19:54:59 +01:00
2020-03-26 18:21:50 +01:00
/ * *
* Add a Jump instruction for later stack inspection
*
* @param jumpPos
* the position of the jump
* @param popCount
* the the count of values that are removed from the stack .
2020-04-12 10:44:53 +02:00
* @param pushValueType
* optional type of a push value
2020-03-26 18:21:50 +01:00
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
2020-04-12 10:44:53 +02:00
protected void addJumpPlaceholder ( int jumpPos , int popCount , AnyType pushValueType , int javaCodePos , int lineNumber ) {
instructions . add ( new JumpInstruction ( jumpPos , popCount , pushValueType , javaCodePos , lineNumber ) ) ;
2020-03-26 18:21:50 +01:00
}
2019-09-08 21:45:28 +02:00
/ * *
* Get a non GC polyfill function .
* @param name the function name
* @param lineNumber the line number for a possible error
* @return the function name
* /
private FunctionName getNonGC ( String name , int lineNumber ) {
try {
2020-01-02 15:15:21 +01:00
ClassFile classFile = classFileLoader . get ( NonGC . class . getName ( ) . replace ( '.' , '/' ) ) ;
2019-09-08 21:45:28 +02:00
for ( MethodInfo method : classFile . getMethods ( ) ) {
if ( name . equals ( method . getName ( ) ) ) {
return new FunctionName ( method ) ;
}
}
} catch ( IOException ex ) {
throw WasmException . create ( ex , lineNumber ) ;
}
throw new WasmException ( " Not implemented NonGC polyfill function: " + name , lineNumber ) ;
}
2018-12-02 19:54:59 +01:00
/ * *
* Add an array operation to the instruction list as marker on the code position .
*
* @param op
* the operation
* @param type
* the array type
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-12-02 19:54:59 +01:00
* /
2019-03-31 11:23:45 +02:00
protected void addArrayInstruction ( ArrayOperator op , AnyType type , int javaCodePos , int lineNumber ) {
2019-09-09 21:07:45 +02:00
if ( options . useGC ( ) ) {
2019-08-27 20:41:00 +02:00
instructions . add ( new WasmArrayInstruction ( op , type , types , javaCodePos , lineNumber ) ) ;
2019-08-11 13:11:22 +02:00
} else {
2020-04-10 12:15:53 +02:00
if ( type . getCode ( ) > = 0 | | type . getCode ( ) = = ValueType . anyref . getCode ( ) ) {
type = ValueType . anyref ; // handle all not native types as anyref
2019-08-11 13:11:22 +02:00
}
2019-09-08 21:45:28 +02:00
String api = " array_ " + op . toString ( ) . toLowerCase ( ) + " _ " + type ;
FunctionName name = getNonGC ( api , lineNumber ) ;
addCallInstruction ( name , javaCodePos , lineNumber ) ;
2020-02-09 18:05:31 +01:00
}
}
/ * *
* Add a new multi dimensional array instruction
*
* @param dim
2020-02-27 11:25:41 +01:00
* the dimension of the array & gt ; = 2
2020-02-09 18:05:31 +01:00
* @param typeName
* the full type name
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
protected void addMultiNewArrayInstruction ( int dim , String typeName , int javaCodePos , int lineNumber ) {
ArrayType type = ( ArrayType ) new ValueTypeParser ( typeName , types ) . next ( ) ;
if ( options . useGC ( ) ) {
throw new WasmException ( " multi new array is not supported " , lineNumber ) ;
} else {
FunctionName name = new JavaScriptNewMultiArrayFunctionName ( dim , type ) ;
addCallInstruction ( name , javaCodePos , lineNumber ) ;
2019-08-11 13:11:22 +02:00
}
2018-12-02 19:54:59 +01:00
}
2018-12-05 22:14:26 +01:00
/ * *
2020-02-25 18:05:12 +01:00
* Add a struct / object operation to the instruction list .
2018-12-05 22:14:26 +01:00
*
* @param op
* the operation
2019-01-06 16:29:26 +01:00
* @param typeName
* the type name
2019-01-13 11:36:07 +01:00
* @param fieldName
* the name of field if needed for the operation
2018-12-05 22:14:26 +01:00
* @param javaCodePos
* the code position / offset in the Java method
2019-03-31 11:23:45 +02:00
* @param lineNumber
* the line number in the Java source code
2018-12-05 22:14:26 +01:00
* /
2019-04-24 19:54:30 +02:00
protected void addStructInstruction ( StructOperator op , @Nonnull String typeName , @Nullable NamedStorageType fieldName , int javaCodePos , int lineNumber ) {
2020-02-28 18:04:28 +01:00
switch ( op ) {
case INSTANCEOF :
instructions . add ( new WasmConstInstruction ( types . valueOf ( typeName ) . getClassIndex ( ) , javaCodePos , lineNumber ) ) ;
FunctionName name = options . getInstanceOf ( ) ;
instructions . add ( new WasmCallInstruction ( name , javaCodePos , lineNumber , types , false ) ) ;
return ;
case CAST :
instructions . add ( new WasmConstInstruction ( types . valueOf ( typeName ) . getClassIndex ( ) , javaCodePos , lineNumber ) ) ;
name = options . getCast ( ) ;
instructions . add ( new WasmCallInstruction ( name , javaCodePos , lineNumber , types , false ) ) ;
return ;
2020-02-26 18:02:59 +01:00
}
2019-09-08 13:55:22 +02:00
WasmStructInstruction structInst = new WasmStructInstruction ( op , typeName , fieldName , javaCodePos , lineNumber , types ) ;
instructions . add ( structInst ) ;
2019-09-09 21:07:45 +02:00
if ( ! options . useGC ( ) ) {
2019-09-08 13:55:22 +02:00
SyntheticFunctionName name = structInst . createNonGcFunction ( ) ;
if ( name ! = null ) {
2020-01-11 20:31:05 +01:00
functions . markAsNeeded ( name ) ;
2019-09-08 13:55:22 +02:00
functions . markAsImport ( name , name . getAnnotation ( ) ) ;
}
}
2018-12-05 22:14:26 +01:00
}
2019-11-18 21:32:35 +01:00
/ * *
* Create an instance of a load / store to the linear memory instruction
*
* @param op
* the operation
* @param type
* the type of the static field
* @param offset
* the base offset which will be added to the offset value on the stack
* @param alignment
* the alignment of the value on the linear memory ( 0 : 8 Bit ; 1 : 16 Bit ; 2 : 32 Bit )
* @param javaCodePos
* the code position / offset in the Java method
* @param lineNumber
* the line number in the Java source code
* /
protected void addMemoryInstruction ( MemoryOperator op , ValueType type , int offset , int alignment , int javaCodePos , int lineNumber ) {
instructions . add ( new WasmMemoryInstruction ( op , type , offset , alignment , javaCodePos , lineNumber ) ) ;
}
2018-11-04 20:28:42 +01:00
}