2017-03-31 20:43:57 +02:00
/ *
2021-01-02 16:44:38 +01:00
* Copyright 2017 - 2021 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.text ;
import java.io.IOException ;
2019-03-14 22:10:35 +01:00
import java.util.ArrayList ;
2018-10-08 22:02:19 +02:00
import java.util.HashMap ;
2018-08-14 15:11:21 +02:00
import java.util.HashSet ;
2019-06-01 18:42:24 +02:00
import java.util.LinkedHashMap ;
import java.util.Map ;
2019-09-08 13:55:22 +02:00
import java.util.Map.Entry ;
2019-07-15 21:16:47 +02:00
import java.util.Set ;
2017-03-31 20:43:57 +02:00
2019-11-03 19:00:49 +01:00
import javax.annotation.Nonnegative ;
2018-05-04 20:52:54 +02:00
import javax.annotation.Nonnull ;
2017-04-09 18:46:27 +02:00
import javax.annotation.Nullable ;
2019-04-27 21:14:55 +02:00
import de.inetsoftware.jwebassembly.WasmException ;
2018-05-21 14:29:32 +02:00
import de.inetsoftware.jwebassembly.module.FunctionName ;
2017-03-31 20:43:57 +02:00
import de.inetsoftware.jwebassembly.module.ModuleWriter ;
2019-06-09 17:17:47 +02:00
import de.inetsoftware.jwebassembly.module.TypeManager.StructType ;
2020-09-30 20:17:15 +02:00
import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind ;
2017-04-11 17:47:21 +02:00
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion ;
2020-02-25 18:05:12 +01:00
import de.inetsoftware.jwebassembly.module.WasmOptions ;
2019-06-30 14:43:45 +02:00
import de.inetsoftware.jwebassembly.module.WasmTarget ;
2019-04-21 21:33:22 +02:00
import de.inetsoftware.jwebassembly.wasm.AnyType ;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.ArrayOperator ;
2020-08-09 19:19:58 +02:00
import de.inetsoftware.jwebassembly.wasm.ArrayType ;
2020-03-20 20:35:13 +01:00
import de.inetsoftware.jwebassembly.wasm.FunctionType ;
2019-11-18 20:08:18 +01:00
import de.inetsoftware.jwebassembly.wasm.MemoryOperator ;
2019-01-06 16:29:26 +01: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-02-20 21:42:52 +01:00
import de.inetsoftware.jwebassembly.wasm.VariableOperator ;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator ;
2017-03-31 20:43:57 +02:00
/ * *
* Module Writer for text format with S - expressions .
*
* @author Volker Berlin
*
* /
public class TextModuleWriter extends ModuleWriter {
2020-03-21 11:05:51 +01:00
private final WasmTarget target ;
2019-11-28 20:50:33 +01:00
2020-03-21 11:05:51 +01:00
private final StringBuilder output = new StringBuilder ( ) ;
2017-03-31 20:43:57 +02:00
2020-03-21 11:05:51 +01:00
private final ArrayList < String > methodParamNames = new ArrayList < > ( ) ;
2019-03-14 22:10:35 +01:00
2020-03-21 11:05:51 +01:00
private final StringBuilder typeOutput = new StringBuilder ( ) ;
2019-06-01 13:29:29 +02:00
2020-03-21 11:05:51 +01:00
private final ArrayList < String > types = new ArrayList < > ( ) ;
2019-06-01 13:29:29 +02:00
2020-03-21 11:05:51 +01:00
private StringBuilder methodOutput ;
2017-03-31 20:43:57 +02:00
2020-03-21 11:05:51 +01:00
private final StringBuilder imports = new StringBuilder ( ) ;
2019-06-09 22:39:53 +02:00
2020-03-21 11:05:51 +01:00
private final Map < String , Function > functions = new LinkedHashMap < > ( ) ;
2018-08-14 18:17:48 +02:00
2020-03-21 11:05:51 +01:00
private final Map < String , Function > abstracts = new HashMap < > ( ) ;
2019-07-15 21:16:47 +02:00
2020-03-21 11:05:51 +01:00
private final Set < String > functionNames = new HashSet < > ( ) ;
2017-03-31 20:43:57 +02:00
2020-03-21 11:05:51 +01:00
private int inset ;
2019-06-01 18:42:24 +02:00
2020-03-21 11:05:51 +01:00
private boolean isImport ;
2019-06-01 18:42:24 +02:00
2020-03-21 11:05:51 +01:00
private final HashMap < String , AnyType > globals = new HashMap < > ( ) ;
2019-02-27 21:55:55 +01:00
2020-03-21 11:05:51 +01:00
private boolean useExceptions ;
private boolean callIndirect ;
2019-05-05 17:25:43 +02:00
2021-01-03 17:14:55 +01:00
private boolean useTypeString ;
private boolean useTypeClass ;
2017-03-31 20:43:57 +02:00
/ * *
* Create a new instance .
*
2019-06-30 15:18:09 +02:00
* @param target
2017-03-31 20:43:57 +02:00
* target for the result
2019-09-10 17:49:05 +02:00
* @param options
2018-10-08 22:02:19 +02:00
* compiler properties
2017-03-31 20:43:57 +02:00
* @throws IOException
* if any I / O error occur
* /
2019-09-10 17:49:05 +02:00
public TextModuleWriter ( WasmTarget target , WasmOptions options ) throws IOException {
super ( options ) ;
2019-11-28 20:50:33 +01:00
this . target = target ;
2017-03-31 20:43:57 +02:00
inset + + ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void close ( ) throws IOException {
2019-11-28 20:50:33 +01:00
Appendable textOutput = target . getTextOutput ( ) ;
textOutput . append ( " (module " ) ;
2019-06-01 13:29:29 +02:00
for ( int i = 0 ; i < types . size ( ) ; i + + ) {
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
textOutput . append ( " (type $t " ) . append ( Integer . toString ( i ) ) . append ( " (func " ) . append ( types . get ( i ) ) . append ( " )) " ) ;
2019-06-01 13:29:29 +02:00
}
2019-11-28 20:50:33 +01:00
textOutput . append ( imports ) ;
2019-06-09 22:39:53 +02:00
2019-09-08 13:55:22 +02:00
for ( Entry < String , AnyType > entry : globals . entrySet ( ) ) {
2019-11-28 20:50:33 +01:00
textOutput . append ( " \ n " ) ;
textOutput . append ( " (global $ " ) . append ( entry . getKey ( ) ) . append ( " (mut " ) ;
writeTypeName ( textOutput , entry . getValue ( ) ) ;
textOutput . append ( ')' ) ;
writeDefaultValue ( textOutput , entry . getValue ( ) ) ;
textOutput . append ( ')' ) ;
2019-09-08 13:55:22 +02:00
}
2019-11-28 20:50:33 +01:00
textOutput . append ( output ) ;
2019-06-09 22:39:53 +02:00
for ( Function func : functions . values ( ) ) {
2019-11-28 20:50:33 +01:00
textOutput . append ( func . output ) ;
2019-06-09 22:39:53 +02:00
}
2019-05-01 17:11:14 +02:00
2019-05-05 17:25:43 +02:00
if ( callIndirect ) {
2019-06-04 18:56:53 +02:00
int count = functions . size ( ) ;
2019-05-05 17:25:43 +02:00
String countStr = Integer . toString ( count ) ;
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
2020-04-11 14:35:47 +02:00
textOutput . append ( " (table $functions " ) . append ( countStr ) . append ( " funcref) " ) ;
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
textOutput . append ( " (elem (i32.const 0) " ) ;
2019-05-05 17:25:43 +02:00
for ( int i = 0 ; i < count ; i + + ) {
2019-11-28 20:50:33 +01:00
textOutput . append ( Integer . toString ( i ) ) . append ( ' ' ) ;
2019-05-05 17:25:43 +02:00
}
2019-11-28 20:50:33 +01:00
textOutput . append ( ')' ) ;
2019-05-05 17:25:43 +02:00
}
2019-11-10 12:57:58 +01:00
// table for string constants
2020-03-11 20:03:00 +01:00
int stringCount = options . strings . size ( ) ;
2019-11-10 12:57:58 +01:00
if ( stringCount > 0 ) {
if ( ! callIndirect ) {
// we need to create a placeholder table with index 0 if not exists
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
2020-04-11 14:35:47 +02:00
textOutput . append ( " (table $functions 0 funcref) " ) ;
2019-11-10 12:57:58 +01:00
}
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
2021-01-03 17:14:55 +01:00
String tableTypeName = options . useGC ( ) & & useTypeString ? " (ref null $java/lang/String) " : " externref " ;
2021-01-03 15:43:40 +01:00
textOutput . append ( " (table $strings " ) . append ( Integer . toString ( stringCount ) ) . append ( ' ' ) . append ( tableTypeName ) . append ( ')' ) ;
2019-11-10 12:57:58 +01:00
}
2020-03-11 19:55:59 +01:00
// table with classes
int typeCount = options . types . size ( ) ;
if ( typeCount > 0 ) {
newline ( textOutput ) ;
2021-01-03 17:14:55 +01:00
String tableTypeName = options . useGC ( ) & & useTypeClass ? " (ref null $java/lang/Class) " : " externref " ;
2021-01-03 15:43:40 +01:00
textOutput . append ( " (table $classes " ) . append ( Integer . toString ( typeCount ) ) . append ( ' ' ) . append ( tableTypeName ) . append ( ')' ) ;
2020-03-11 19:55:59 +01:00
}
2019-05-01 17:11:14 +02:00
int dataSize = dataStream . size ( ) ;
if ( dataSize > 0 ) {
int pages = ( dataSize + 0xFFFF ) / 0x10000 ;
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
2019-05-01 17:11:14 +02:00
String pagesStr = Integer . toString ( pages ) ;
2020-03-11 19:34:49 +01:00
textOutput . append ( " (memory " ) . append ( pagesStr ) . append ( ')' ) ;
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
textOutput . append ( " (data (i32.const 0) \" " ) ;
2019-05-01 17:11:14 +02:00
byte [ ] data = dataStream . toByteArray ( ) ;
for ( byte b : data ) {
2019-12-23 21:28:26 +01:00
if ( b > = ' ' & & b < 0x7F & & b ! = '\"' & & b ! = '\\' ) {
textOutput . append ( ( char ) b ) ;
} else {
textOutput . append ( '\\' ) . append ( Character . forDigit ( ( b > > 4 ) & 0xF , 16 ) ) . append ( Character . forDigit ( b & 0xF , 16 ) ) ;
}
2019-05-01 17:11:14 +02:00
}
2019-11-28 20:50:33 +01:00
textOutput . append ( " \" ) " ) ;
2019-05-01 17:11:14 +02:00
}
2017-03-31 20:43:57 +02:00
inset - - ;
2019-11-28 20:50:33 +01:00
newline ( textOutput ) ;
textOutput . append ( ')' ) ;
2017-03-31 20:43:57 +02:00
}
2019-01-06 16:29:26 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2019-06-09 17:17:47 +02:00
protected int writeStructType ( StructType type ) throws IOException {
2020-06-01 11:17:47 +02:00
type . writeToStream ( dataStream , ( funcName ) - > getFunction ( funcName ) . id , options ) ;
2019-06-09 17:17:47 +02:00
2020-09-30 20:17:15 +02:00
if ( type . getKind ( ) = = StructTypeKind . primitive ) {
return - 9 ; // Should never use
}
2019-09-14 21:26:46 +02:00
if ( ! options . useGC ( ) ) {
2020-05-30 23:06:29 +02:00
return ValueType . externref . getCode ( ) ;
2019-09-14 21:26:46 +02:00
}
2019-01-06 16:29:26 +01:00
int oldInset = inset ;
inset = 1 ;
newline ( output ) ;
2019-06-09 17:17:47 +02:00
String typeName = normalizeName ( type . getName ( ) ) ;
2021-01-03 17:14:55 +01:00
switch ( typeName ) {
case " java/lang/String " :
useTypeString = true ;
break ;
case " java/lang/Class " :
useTypeClass = true ;
break ;
}
2021-02-13 15:05:40 +01:00
output . append ( " (type $ " ) . append ( typeName ) . append ( " ( " ) ;
if ( type . getKind ( ) = = StructTypeKind . array_native ) {
output . append ( " array (mut " ) ;
writeTypeName ( output , type . getFields ( ) . get ( 0 ) . getType ( ) ) ;
output . append ( " ) " ) ;
} else {
output . append ( " struct " ) ;
inset + + ;
for ( NamedStorageType field : type . getFields ( ) ) {
newline ( output ) ;
output . append ( " (field " ) ;
if ( options . debugNames ( ) & & field . getName ( ) ! = null ) {
output . append ( " $ " ) . append ( typeName ) . append ( '.' ) . append ( field . getName ( ) ) ;
}
output . append ( " (mut " ) ;
writeTypeName ( output , field . getType ( ) ) ;
output . append ( " )) " ) ;
2019-01-06 16:29:26 +01:00
}
2021-02-13 15:05:40 +01:00
inset - - ;
newline ( output ) ;
2019-01-06 16:29:26 +01:00
}
output . append ( " )) " ) ;
inset = oldInset ;
return 0 ;
}
2019-02-27 21:55:55 +01:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeException ( ) throws IOException {
if ( ! useExceptions ) {
useExceptions = true ;
int oldInset = inset ;
inset = 1 ;
newline ( output ) ;
2020-05-30 23:06:29 +02:00
output . append ( " (event (param externref)) " ) ;
2019-02-27 21:55:55 +01:00
inset = oldInset ;
2020-04-18 13:09:11 +02:00
options . setCatchType ( types . size ( ) ) ;
types . add ( options . getCatchType ( ) . toString ( ) ) ;
2019-02-27 21:55:55 +01:00
}
}
2019-06-04 18:09:34 +02:00
/ * *
* { @inheritDoc }
* /
@Override
protected void prepareFinish ( ) {
2019-11-28 20:22:23 +01:00
// nothing
2019-06-04 18:09:34 +02:00
}
2018-05-30 18:57:36 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2018-05-31 21:35:01 +02:00
protected void prepareImport ( FunctionName name , String importModule , String importName ) throws IOException {
2018-05-30 18:57:36 +02:00
if ( importName ! = null ) {
2019-06-09 22:39:53 +02:00
methodOutput = imports ;
2019-01-01 12:26:32 +01:00
newline ( methodOutput ) ;
2019-04-19 16:00:43 +02:00
methodOutput . append ( " (import \" " ) . append ( importModule ) . append ( " \" \" " ) . append ( importName ) . append ( " \" (func $ " ) . append ( normalizeName ( name ) ) ;
2018-08-14 18:17:48 +02:00
isImport = true ;
2018-05-30 18:57:36 +02:00
}
}
2019-04-19 16:00:43 +02:00
/ * *
* Normalize the function name for the text format
*
* @param name
* the name
* @return the normalized name
* /
@Nonnull
private String normalizeName ( FunctionName name ) {
2019-07-15 21:16:47 +02:00
Function function = getFunction ( name ) ;
if ( function . name = = null ) {
String base ;
String str = base = normalizeName ( name . fullName ) ;
for ( int i = 1 ; functionNames . contains ( str ) ; i + + ) {
str = base + '.' + i ;
}
functionNames . add ( str ) ;
function . name = str ;
}
return function . name ;
2019-04-19 20:29:20 +02:00
}
/ * *
2020-08-09 15:22:26 +02:00
* Normalize the function name for the text format of IDs .
* https : //webassembly.github.io/spec/core/text/values.html#text-id
2019-04-19 20:29:20 +02:00
*
* @param name
* the name
* @return the normalized name
* /
@Nonnull
private String normalizeName ( String name ) {
2020-09-24 20:30:53 +02:00
return name . replace ( '[' , '/' ) . replace ( " ; " , " " ) ;
2019-04-19 16:00:43 +02:00
}
2017-03-31 20:43:57 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2018-05-21 14:29:32 +02:00
protected void writeExport ( FunctionName name , String exportName ) throws IOException {
2017-03-31 20:43:57 +02:00
newline ( output ) ;
2019-04-19 16:00:43 +02:00
output . append ( " (export \" " ) . append ( exportName ) . append ( " \" (func $ " ) . append ( normalizeName ( name ) ) . append ( " )) " ) ;
2017-03-31 20:43:57 +02:00
}
2019-04-27 21:14:55 +02:00
/ * *
* Write the name of a type .
*
* @param output
* the target
* @param type
* the type
* @throws IOException
* if any I / O error occur
* /
private void writeTypeName ( Appendable output , AnyType type ) throws IOException {
2020-04-18 13:09:11 +02:00
if ( ! type . isRefType ( ) ) {
2021-02-13 15:05:40 +01:00
String name = type = = ValueType . u16 ? " i16 " : type = = ValueType . bool ? " i8 " : type . toString ( ) ;
2021-01-03 14:52:19 +01:00
output . append ( name ) ;
2019-09-10 17:49:05 +02:00
} else if ( options . useGC ( ) ) {
2020-09-17 21:07:09 +02:00
//output.append( ValueType.eqref.toString() );
output . append ( " (ref null " ) . append ( normalizeName ( type . toString ( ) ) ) . append ( ')' ) ;
2019-09-08 13:55:22 +02:00
} else {
2020-05-30 23:06:29 +02:00
output . append ( ValueType . externref . toString ( ) ) ;
2019-04-27 21:14:55 +02:00
}
}
2019-06-02 11:44:28 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2020-03-20 20:35:13 +01:00
protected void writeMethodParamStart ( @Nonnull FunctionName name , FunctionType funcType ) throws IOException {
2020-04-02 21:01:11 +02:00
switch ( funcType ) {
case Abstract :
abstracts . put ( name . signatureName , new Function ( ) ) ;
break ;
case Start :
newline ( imports ) ;
imports . append ( " (start $ " ) . append ( normalizeName ( name ) ) . append ( " ) " ) ;
break ;
2020-03-21 11:05:51 +01:00
}
2019-06-02 11:44:28 +02:00
typeOutput . setLength ( 0 ) ;
methodParamNames . clear ( ) ;
}
2017-03-31 20:43:57 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-01-14 20:09:00 +01:00
protected void writeMethodParam ( String kind , AnyType valueType , @Nullable String name ) throws IOException {
2019-06-01 13:29:29 +02:00
if ( kind ! = " local " ) {
typeOutput . append ( '(' ) . append ( kind ) . append ( ' ' ) ;
writeTypeName ( typeOutput , valueType ) ;
typeOutput . append ( ')' ) ;
}
2019-06-28 18:01:33 +02:00
if ( methodOutput = = null ) {
2019-06-04 18:09:34 +02:00
return ;
}
2021-01-24 11:08:54 +01:00
newline ( methodOutput ) ;
methodOutput . append ( " ( " ) . append ( kind ) ;
2019-09-10 17:49:05 +02:00
if ( options . debugNames ( ) ) {
2019-03-14 22:10:35 +01:00
if ( name ! = null ) {
methodOutput . append ( " $ " ) . append ( name ) ;
}
if ( kind ! = " result " ) {
methodParamNames . add ( name ) ;
}
2018-10-13 18:10:05 +02:00
}
2019-04-20 15:23:53 +02:00
methodOutput . append ( ' ' ) ;
2019-04-27 21:14:55 +02:00
writeTypeName ( methodOutput , valueType ) ;
2019-04-20 15:23:53 +02:00
methodOutput . append ( ')' ) ;
2017-03-31 20:43:57 +02:00
}
2018-06-01 19:44:40 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-06-02 11:44:28 +02:00
protected void writeMethodParamFinish ( @Nonnull FunctionName name ) throws IOException {
2019-06-01 13:29:29 +02:00
String typeStr = typeOutput . toString ( ) ;
int idx = types . indexOf ( typeStr ) ;
if ( idx < 0 ) {
idx = types . size ( ) ;
types . add ( typeStr ) ;
}
2019-06-09 22:39:53 +02:00
getFunction ( name ) . typeId = idx ;
2019-06-01 18:42:24 +02:00
2018-08-14 18:17:48 +02:00
if ( isImport ) {
isImport = false ;
2019-01-01 12:26:32 +01:00
methodOutput . append ( " )) " ) ;
2019-06-09 22:39:53 +02:00
methodOutput = null ;
2018-08-14 18:17:48 +02:00
}
}
2018-06-01 19:44:40 +02:00
2019-06-09 22:39:53 +02:00
private Function getFunction ( FunctionName name ) {
2020-03-21 11:05:51 +01:00
String signatureName = name . signatureName ;
Function func = functions . get ( signatureName ) ;
2019-06-09 22:39:53 +02:00
if ( func = = null ) {
2020-03-21 11:05:51 +01:00
func = abstracts . get ( signatureName ) ;
if ( func = = null ) {
func = new Function ( ) ;
func . id = functions . size ( ) ;
functions . put ( name . signatureName , func ) ;
}
2019-06-09 22:39:53 +02:00
}
return func ;
}
2019-06-02 11:44:28 +02:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeMethodStart ( FunctionName name , String sourceFile ) throws IOException {
2019-06-09 22:39:53 +02:00
methodOutput = getFunction ( name ) . output ;
2019-06-02 11:44:28 +02:00
newline ( methodOutput ) ;
methodOutput . append ( " (func $ " ) ;
methodOutput . append ( normalizeName ( name ) ) ;
inset + + ;
}
2019-03-26 18:21:20 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2019-03-31 13:29:40 +02:00
protected void markSourceLine ( int javaSourceLine ) {
2019-03-26 18:21:20 +01:00
// nothing
}
2017-03-31 20:43:57 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2018-08-14 22:09:01 +02:00
protected void writeMethodFinish ( ) throws IOException {
2017-03-31 20:43:57 +02:00
inset - - ;
2019-01-01 12:26:32 +01:00
newline ( methodOutput ) ;
methodOutput . append ( ')' ) ;
2019-06-09 22:39:53 +02:00
methodOutput = null ;
2017-03-31 20:43:57 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
2018-08-05 18:27:06 +02:00
protected void writeConst ( Number value , ValueType valueType ) throws IOException {
2017-03-31 20:43:57 +02:00
newline ( methodOutput ) ;
2019-04-19 17:39:49 +02:00
methodOutput . append ( valueType ) . append ( " .const " ) ;
switch ( valueType ) {
case f32 :
2019-08-03 20:49:45 +02:00
float floatValue = value . floatValue ( ) ;
if ( floatValue = = Double . POSITIVE_INFINITY ) {
methodOutput . append ( " inf " ) ;
2019-08-04 17:28:03 +02:00
} else if ( floatValue = = Double . NEGATIVE_INFINITY ) {
2019-08-03 20:49:45 +02:00
methodOutput . append ( " -inf " ) ;
} else {
methodOutput . append ( Float . toHexString ( floatValue ) . toLowerCase ( ) ) . append ( " ;; " ) . append ( value ) ;
}
2019-04-19 17:39:49 +02:00
break ;
case f64 :
2019-08-03 20:49:45 +02:00
double doubleValue = value . doubleValue ( ) ;
if ( doubleValue = = Double . POSITIVE_INFINITY ) {
methodOutput . append ( " inf " ) ;
2019-08-04 17:28:03 +02:00
} else if ( doubleValue = = Double . NEGATIVE_INFINITY ) {
2019-08-03 20:49:45 +02:00
methodOutput . append ( " -inf " ) ;
} else {
methodOutput . append ( Double . toHexString ( doubleValue ) . toLowerCase ( ) ) . append ( " ;; " ) . append ( value ) ;
}
2019-04-19 17:39:49 +02:00
break ;
default :
methodOutput . append ( value ) ;
break ;
}
2017-04-09 12:44:01 +02:00
}
2017-03-31 20:43:57 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-02-20 21:42:52 +01:00
protected void writeLocal ( VariableOperator op , int idx ) throws IOException {
2017-03-31 20:43:57 +02:00
newline ( methodOutput ) ;
2019-03-14 22:10:35 +01:00
methodOutput . append ( " local. " ) . append ( op ) . append ( ' ' ) ;
String name = idx < methodParamNames . size ( ) ? methodParamNames . get ( idx ) : null ;
if ( name = = null ) {
methodOutput . append ( Integer . toString ( idx ) ) ;
} else {
methodOutput . append ( '$' ) . append ( name ) ;
}
2017-03-31 20:43:57 +02:00
}
2018-08-14 15:11:21 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-04-27 21:14:55 +02:00
protected void writeGlobalAccess ( boolean load , FunctionName name , AnyType type ) throws IOException {
2019-07-15 21:16:47 +02:00
String fullName = normalizeName ( name . fullName ) ;
2019-09-08 13:55:22 +02:00
if ( ! globals . containsKey ( fullName ) ) {
2018-08-14 15:11:21 +02:00
// declare global variable if not already declared.
2019-09-08 13:55:22 +02:00
globals . put ( fullName , type ) ;
2018-08-14 15:11:21 +02:00
}
newline ( methodOutput ) ;
2019-04-19 16:00:43 +02:00
methodOutput . append ( load ? " global.get $ " : " global.set $ " ) . append ( fullName ) ;
2018-08-14 15:11:21 +02:00
}
2019-11-03 19:00:49 +01:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeTable ( boolean load , @Nonnegative int idx ) throws IOException {
newline ( methodOutput ) ;
methodOutput . append ( load ? " table.get " : " table.set " ) . append ( idx ) ;
}
2019-04-27 21:14:55 +02:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeDefaultValue ( AnyType type ) throws IOException {
newline ( methodOutput ) ;
writeDefaultValue ( methodOutput , type ) ;
}
/ * *
* Write the default / initial value for type .
*
* @param output
* the target
* @param type
* the type
* @throws IOException
* if an I / O error occurs .
* /
2020-09-17 21:07:09 +02:00
private void writeDefaultValue ( Appendable output , AnyType type ) throws IOException {
2019-09-07 19:52:10 +02:00
if ( type instanceof ValueType ) {
2019-04-27 21:14:55 +02:00
ValueType valueType = ( ValueType ) type ;
switch ( valueType ) {
case i32 :
case i64 :
case f32 :
case f64 :
2019-05-09 21:41:39 +02:00
output . append ( type . toString ( ) ) . append ( " .const 0 " ) ;
2019-04-27 21:14:55 +02:00
break ;
case i8 :
case i16 :
writeDefaultValue ( output , ValueType . i32 ) ;
break ;
2020-05-30 23:06:29 +02:00
case externref :
output . append ( " ref.null extern " ) ;
2019-04-27 21:14:55 +02:00
break ;
default :
throw new WasmException ( " Not supported storage type: " + type , - 1 ) ;
}
} else {
2020-09-17 21:07:09 +02:00
output . append ( " ref.null " ) . append ( options . useGC ( ) ? normalizeName ( type . toString ( ) ) : " extern " ) ;
2019-04-27 21:14:55 +02:00
}
}
2017-03-31 20:43:57 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2017-04-11 21:12:27 +02:00
protected void writeNumericOperator ( NumericOperator numOp , @Nullable ValueType valueType ) throws IOException {
2019-09-10 21:41:50 +02:00
boolean negate = false ;
String op = valueType . toString ( ) + '.' + numOp . toString ( ) ;
2018-08-14 18:24:50 +02:00
switch ( valueType ) {
case i32 :
case i64 :
switch ( numOp ) {
case div :
case rem :
2018-08-14 21:28:17 +02:00
case gt :
case lt :
case le :
case ge :
2018-08-14 18:24:50 +02:00
op + = " _s " ;
2018-12-15 22:33:25 +01:00
break ;
case ifnonnull :
2020-06-29 21:00:47 +02:00
op = " ref.is_null " ;
2019-09-10 21:41:50 +02:00
negate = true ;
break ;
2018-12-15 22:33:25 +01:00
case ifnull :
2020-06-29 21:00:47 +02:00
op = " ref.is_null " ;
2019-09-10 21:41:50 +02:00
break ;
2018-12-17 21:22:10 +01:00
case ref_ne :
2019-09-10 21:41:50 +02:00
op = options . useGC ( ) ? " ref.eq " : null ;
negate = true ;
break ;
2018-12-17 21:22:10 +01:00
case ref_eq :
2019-09-10 21:41:50 +02:00
op = options . useGC ( ) ? " ref.eq " : null ;
break ;
2019-01-13 11:36:07 +01:00
default :
2018-08-14 18:24:50 +02:00
}
2018-12-15 22:33:25 +01:00
break ;
2019-01-13 11:36:07 +01:00
default :
2018-08-14 18:24:50 +02:00
}
2019-09-10 21:41:50 +02:00
if ( op ! = null ) {
newline ( methodOutput ) ;
methodOutput . append ( op ) ;
} else {
2020-03-21 22:57:42 +01:00
writeFunctionCall ( options . ref_eq , null ) ;
2019-09-10 21:41:50 +02:00
}
if ( negate ) {
writeNumericOperator ( NumericOperator . eqz , ValueType . i32 ) ;
}
2017-03-31 20:43:57 +02:00
}
2017-04-11 17:47:21 +02:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeCast ( ValueTypeConvertion cast ) throws IOException {
String op ;
switch ( cast ) {
2018-04-02 10:48:24 +02:00
case i2l :
2019-02-22 23:19:10 +01:00
op = " i64.extend_i32_s " ;
2018-04-02 10:48:24 +02:00
break ;
case i2f :
2019-02-22 23:19:10 +01:00
op = " f32.convert_i32_s " ;
2018-04-02 10:48:24 +02:00
break ;
case i2d :
2019-02-22 23:19:10 +01:00
op = " f64.convert_i32_s " ;
2018-04-02 10:48:24 +02:00
break ;
2017-04-11 17:47:21 +02:00
case l2i :
2019-02-22 23:19:10 +01:00
op = " i32.wrap_i64 " ;
2017-04-11 17:47:21 +02:00
break ;
2018-04-02 10:48:24 +02:00
case l2f :
2019-02-22 23:19:10 +01:00
op = " f32.convert_i64_s " ;
2018-04-02 10:48:24 +02:00
break ;
case l2d :
2019-02-22 23:19:10 +01:00
op = " f64.convert_i64_s " ;
2018-04-02 10:48:24 +02:00
break ;
case f2i :
2019-02-22 23:19:10 +01:00
op = " i32.trunc_sat_f32_s " ;
2018-04-02 10:48:24 +02:00
break ;
case f2l :
2019-02-22 23:19:10 +01:00
op = " i64.trunc_sat_f32_s " ;
2018-04-02 10:48:24 +02:00
break ;
case f2d :
2019-02-22 23:19:10 +01:00
op = " f64.promote_f32 " ;
2018-04-02 10:48:24 +02:00
break ;
case d2i :
2019-02-22 23:19:10 +01:00
op = " i32.trunc_sat_f64_s " ;
2018-04-02 10:48:24 +02:00
break ;
case d2l :
2019-02-22 23:19:10 +01:00
op = " i64.trunc_sat_f64_s " ;
2018-04-02 10:48:24 +02:00
break ;
case d2f :
2019-02-22 23:19:10 +01:00
op = " f32.demote_f64 " ;
2017-04-16 23:24:37 +02:00
break ;
2018-08-03 21:49:42 +02:00
case i2b :
op = " i32.extend8_s " ;
break ;
case i2s :
op = " i32.extend16_s " ;
break ;
2019-07-14 12:41:40 +02:00
case f2i_re :
op = " i32.reinterpret_f32 " ;
break ;
case i2f_re :
op = " f32.reinterpret_i32 " ;
break ;
case d2l_re :
op = " i64.reinterpret_f64 " ;
break ;
case l2d_re :
op = " f64.reinterpret_i64 " ;
break ;
2017-04-11 17:47:21 +02:00
default :
2019-07-14 12:41:40 +02:00
throw new Error ( " Unknown cast/type conversion: " + cast ) ;
2017-04-11 17:47:21 +02:00
}
newline ( methodOutput ) ;
methodOutput . append ( op ) ;
}
2017-03-31 20:43:57 +02:00
/ * *
2019-01-13 11:36:07 +01:00
* Add a newline with the insets .
2017-03-31 20:43:57 +02:00
*
2019-01-13 11:36:07 +01:00
* @param output
* the target
2017-03-31 20:43:57 +02:00
* @throws IOException
* if any I / O error occur
* /
private void newline ( Appendable output ) throws IOException {
output . append ( '\n' ) ;
for ( int i = 0 ; i < inset ; i + + ) {
output . append ( ' ' ) ;
output . append ( ' ' ) ;
}
}
2018-03-24 12:33:56 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2020-03-22 12:36:01 +01:00
protected void writeFunctionCall ( FunctionName name , String comment ) throws IOException {
2018-03-24 12:33:56 +01:00
newline ( methodOutput ) ;
2019-04-19 16:00:43 +02:00
methodOutput . append ( " call $ " ) . append ( normalizeName ( name ) ) ;
2020-03-22 12:36:01 +01:00
if ( comment ! = null ) {
methodOutput . append ( " ;; \" " ) . append ( comment . replace ( " \ n " , " \\ n " ) . replace ( " \ r " , " \\ r " ) ) . append ( '"' ) ;
2020-03-21 22:57:42 +01:00
}
2018-03-24 12:33:56 +01:00
}
2018-03-25 12:57:04 +02:00
2019-05-05 17:25:43 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-09-13 20:04:03 +02:00
protected void writeVirtualFunctionCall ( FunctionName name , AnyType type ) throws IOException {
2019-05-05 17:25:43 +02:00
callIndirect = true ;
2019-06-09 21:09:05 +02:00
2019-09-13 20:34:44 +02:00
newline ( methodOutput ) ;
2020-03-21 22:57:42 +01:00
methodOutput . append ( " call_indirect (type $t " ) . append ( getFunction ( name ) . typeId ) . append ( " ) ;; " ) . append ( name . signatureName ) ;
2019-05-05 17:25:43 +02:00
}
2018-03-25 12:57:04 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2018-05-04 20:52:54 +02:00
protected void writeBlockCode ( @Nonnull WasmBlockOperator op , @Nullable Object data ) throws IOException {
2020-03-21 11:59:05 +01:00
CharSequence name ;
2018-04-02 11:53:12 +02:00
int insetAfter = 0 ;
2018-03-25 12:57:04 +02:00
switch ( op ) {
2018-04-02 11:53:12 +02:00
case RETURN :
name = " return " ;
break ;
2018-03-25 12:57:04 +02:00
case IF :
2020-04-18 13:09:11 +02:00
name = blockWithResult ( " if " , ( AnyType ) data ) ;
2018-04-02 11:53:12 +02:00
insetAfter + + ;
2018-03-25 12:57:04 +02:00
break ;
2018-03-27 20:04:35 +02:00
case ELSE :
inset - - ;
2018-04-02 11:53:12 +02:00
name = " else " ;
insetAfter + + ;
2018-03-27 20:04:35 +02:00
break ;
2018-03-25 12:57:04 +02:00
case END :
inset - - ;
2018-04-02 11:53:12 +02:00
name = " end " ;
break ;
case DROP :
name = " drop " ;
2018-03-25 12:57:04 +02:00
break ;
2018-05-03 22:57:44 +02:00
case BLOCK :
2020-04-18 13:09:11 +02:00
name = blockWithResult ( " block " , ( AnyType ) data ) ;
2018-05-03 22:57:44 +02:00
insetAfter + + ;
break ;
2018-05-04 20:52:54 +02:00
case BR :
name = " br " + data ;
break ;
2018-05-11 21:39:04 +02:00
case BR_IF :
name = " br_if " + data ;
break ;
2018-05-05 21:39:36 +02:00
case BR_TABLE :
StringBuilder builder = new StringBuilder ( " br_table " ) ;
for ( int i : ( int [ ] ) data ) {
builder . append ( ' ' ) . append ( i ) ;
}
name = builder . toString ( ) ;
break ;
2018-05-20 11:52:16 +02:00
case LOOP :
name = " loop " ;
insetAfter + + ;
break ;
2018-07-27 17:51:36 +02:00
case UNREACHABLE :
name = " unreachable " ;
break ;
2018-11-03 18:01:42 +01:00
case TRY :
2020-04-13 12:01:21 +02:00
name = options . useEH ( ) ? " try " : " block " ;
2018-11-03 18:01:42 +01:00
insetAfter + + ;
break ;
case CATCH :
inset - - ;
2020-04-13 12:01:21 +02:00
name = options . useEH ( ) ? " catch " : " br 0 " ;
2018-11-03 18:01:42 +01:00
insetAfter + + ;
break ;
2019-03-02 21:54:27 +01:00
case THROW :
2020-05-30 23:06:29 +02:00
name = options . useEH ( ) ? " throw 0 " : " unreachable " ; // currently there is only one event/exception with externref
2019-03-02 21:54:27 +01:00
break ;
case RETHROW :
name = " rethrow " ;
break ;
case BR_ON_EXN :
2020-05-30 23:06:29 +02:00
name = options . useEH ( ) ? " br_on_exn " + data + " 0 " : " unreachable " ; // br_on_exn, break depth, event; // currently there is only one event/exception with externref
2019-03-02 21:54:27 +01:00
break ;
2019-03-10 18:32:26 +01:00
case MONITOR_ENTER :
case MONITOR_EXIT :
name = " drop " ;
break ;
2018-03-25 12:57:04 +02:00
default :
throw new Error ( " Unknown block: " + op ) ;
}
2018-04-02 11:53:12 +02:00
newline ( methodOutput ) ;
methodOutput . append ( name ) ;
inset + = insetAfter ;
2018-03-25 12:57:04 +02:00
}
2018-12-02 19:54:59 +01:00
2020-04-18 13:09:11 +02:00
/ * *
* Create a the result type for a block instruction
*
* @param blockName
* the name of the block for example " if " or " block "
* @param result
* the result type of the block
* @return the block with result type
* @throws IOException
* if any I / O error occur
* /
@Nonnull
private CharSequence blockWithResult ( String blockName , AnyType result ) throws IOException {
if ( result = = null | | result = = ValueType . empty ) {
return blockName ;
} else {
StringBuilder builder = new StringBuilder ( blockName ) ;
if ( result . toString ( ) . contains ( " ( " ) ) {
builder . append ( result ) ;
} else {
builder . append ( " (result " ) ;
writeTypeName ( builder , result ) ;
builder . append ( " ) " ) ;
}
return builder ;
}
}
2018-12-05 22:14:26 +01:00
/ * *
* { @inheritDoc }
* /
2018-12-02 19:54:59 +01:00
@Override
2020-08-09 19:19:58 +02:00
protected void writeArrayOperator ( @Nonnull ArrayOperator op , ArrayType type ) throws IOException {
2018-12-02 19:54:59 +01:00
String operation ;
switch ( op ) {
case NEW :
2021-01-02 16:44:38 +01:00
newline ( methodOutput ) ;
methodOutput . append ( " rtt.canon " ) . append ( ' ' ) . append ( normalizeName ( type . getNativeArrayType ( ) . toString ( ) ) ) ;
operation = " new_default_with_rtt " ;
2018-12-02 19:54:59 +01:00
break ;
case GET :
operation = " get " ;
break ;
2021-01-02 21:43:02 +01:00
case GET_S :
operation = " get_s " ;
break ;
case GET_U :
operation = " get_u " ;
break ;
2018-12-02 19:54:59 +01:00
case SET :
operation = " set " ;
break ;
2019-08-11 13:06:31 +02:00
case LEN :
2018-12-02 19:54:59 +01:00
operation = " len " ;
break ;
2021-01-02 16:44:38 +01:00
case NEW_ARRAY_WITH_RTT :
operation = " new_default_with_rtt " ;
break ;
2018-12-02 19:54:59 +01:00
default :
throw new Error ( " Unknown operator: " + op ) ;
}
newline ( methodOutput ) ;
2021-01-02 16:44:38 +01:00
methodOutput . append ( " array. " ) . append ( operation ) . append ( ' ' ) . append ( normalizeName ( type . getNativeArrayType ( ) . toString ( ) ) ) ;
2018-12-02 19:54:59 +01:00
}
2018-12-05 22:14:26 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2019-04-22 15:56:11 +02:00
protected void writeStructOperator ( StructOperator op , AnyType type , NamedStorageType fieldName , int idx ) throws IOException {
2018-12-05 22:14:26 +01:00
String operation ;
switch ( op ) {
case NEW :
case NEW_DEFAULT :
2020-09-26 15:59:57 +02:00
newline ( methodOutput ) ;
methodOutput . append ( " rtt.canon " ) . append ( ' ' ) . append ( normalizeName ( type . toString ( ) ) ) ;
2021-01-02 16:44:38 +01:00
operation = " struct.new_default_with_rtt " ;
2018-12-05 22:14:26 +01:00
break ;
case GET :
2018-12-14 20:47:53 +01:00
operation = " struct.get " ;
2018-12-05 22:14:26 +01:00
break ;
case SET :
2018-12-14 20:47:53 +01:00
operation = " struct.set " ;
break ;
case NULL :
2020-09-24 21:46:02 +02:00
if ( options . useGC ( ) ) {
operation = " ref.null " ;
} else {
operation = " ref.null extern " ;
type = null ;
}
2018-12-05 22:14:26 +01:00
break ;
2021-01-02 16:44:38 +01:00
case RTT_CANON :
operation = " rtt.canon " ;
break ;
case NEW_WITH_RTT :
operation = " struct.new_with_rtt " ;
break ;
2021-02-13 20:56:42 +01:00
case CAST :
operation = " ref.cast $java/lang/Object " ;
break ;
2018-12-05 22:14:26 +01:00
default :
throw new Error ( " Unknown operator: " + op ) ;
}
newline ( methodOutput ) ;
2018-12-14 20:47:53 +01:00
methodOutput . append ( operation ) ;
if ( type ! = null ) {
2019-04-19 20:29:20 +02:00
methodOutput . append ( ' ' ) . append ( normalizeName ( type . toString ( ) ) ) ;
2019-01-13 11:36:07 +01:00
}
2021-01-02 16:44:38 +01:00
if ( idx > = 0 ) {
methodOutput . append ( ' ' ) . append ( idx ) ;
if ( fieldName ! = null ) {
methodOutput . append ( " ;; $ " ) . append ( normalizeName ( fieldName . getName ( ) ) ) ;
}
2018-12-14 20:47:53 +01:00
}
2018-12-05 22:14:26 +01:00
}
2019-09-13 20:04:03 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-11-18 20:08:18 +01:00
protected void writeMemoryOperator ( MemoryOperator memOp , ValueType valueType , int offset , int alignment ) throws IOException {
2019-09-13 20:04:03 +02:00
newline ( methodOutput ) ;
2019-11-18 20:08:18 +01:00
methodOutput . append ( valueType ) . append ( '.' ) . append ( memOp )
. append ( " offset= " ) . append ( offset )
2019-11-18 21:32:35 +01:00
. append ( " align= " ) . append ( 1 < < alignment ) ;
2019-09-13 20:04:03 +02:00
}
2017-03-31 20:43:57 +02:00
}