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.binary ;
import java.io.IOException ;
import java.util.ArrayList ;
2018-10-07 21:20:14 +02:00
import java.util.Collection ;
2020-03-20 20:35:13 +01:00
import java.util.HashMap ;
2017-03-31 20:43:57 +02:00
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
2018-10-13 15:29:34 +02:00
import java.util.Map.Entry ;
2020-04-05 21:42:40 +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-06-23 12:55:14 +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-03-30 22:23:36 +01:00
import de.inetsoftware.jwebassembly.module.WasmTarget ;
2019-03-31 10:39:59 +02:00
import de.inetsoftware.jwebassembly.sourcemap.SourceMapWriter ;
2019-03-31 13:29:40 +02:00
import de.inetsoftware.jwebassembly.sourcemap.SourceMapping ;
2019-03-30 22:23:36 +01: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 binary format . http : //webassembly.org/docs/binary-encoding/
*
* @author Volker Berlin
* /
public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcodes {
2018-05-30 21:19:01 +02:00
private static final byte [ ] WASM_BINARY_MAGIC = { 0 , 'a' , 's' , 'm' } ;
2017-03-31 20:43:57 +02:00
2018-05-30 21:19:01 +02:00
private static final int WASM_BINARY_VERSION = 1 ;
2017-03-31 20:43:57 +02:00
2019-03-30 22:23:36 +01:00
private WasmTarget target ;
2018-05-30 21:19:01 +02:00
private WasmOutputStream wasm ;
2017-03-31 20:43:57 +02:00
2019-03-28 18:26:25 +01:00
private final boolean createSourceMap ;
2020-04-18 13:09:11 +02:00
private WasmOutputStream codeStream = new WasmOutputStream ( options ) ;
2017-03-31 20:43:57 +02:00
2019-01-06 16:29:26 +01:00
private List < TypeEntry > functionTypes = new ArrayList < > ( ) ;
2017-03-31 20:43:57 +02:00
2018-05-30 21:19:01 +02:00
private Map < String , Function > functions = new LinkedHashMap < > ( ) ;
2017-03-31 20:43:57 +02:00
2019-02-27 21:55:55 +01:00
private List < AnyType > locals = new ArrayList < > ( ) ;
2018-08-14 22:09:01 +02:00
2018-08-13 17:39:14 +02:00
private Map < String , Global > globals = new LinkedHashMap < > ( ) ;
2019-11-02 18:59:09 +01:00
private List < ExportEntry > exports = new ArrayList < > ( ) ;
2017-03-31 20:43:57 +02:00
2018-05-30 21:19:01 +02:00
private Map < String , ImportFunction > imports = new LinkedHashMap < > ( ) ;
2017-03-31 20:43:57 +02:00
2020-03-20 20:35:13 +01:00
private Map < String , Function > abstracts = new HashMap < > ( ) ;
2018-05-30 21:19:01 +02:00
private Function function ;
2018-05-30 18:57:36 +02:00
2019-02-27 21:55:55 +01:00
private FunctionTypeEntry functionType ;
private int exceptionSignatureIndex = - 1 ;
2017-03-31 20:43:57 +02:00
2019-03-31 10:39:59 +02:00
private String javaSourceFile ;
2019-05-05 17:25:43 +02:00
private boolean callIndirect ;
2020-04-02 21:01:11 +02:00
private FunctionName startFunction ;
2017-03-31 20:43:57 +02:00
/ * *
* Create new instance .
*
2019-03-31 09:51:21 +02:00
* @param target
2017-03-31 20:43:57 +02:00
* the target for the module data .
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 BinaryModuleWriter ( WasmTarget target , WasmOptions options ) throws IOException {
super ( options ) ;
2019-03-30 22:23:36 +01:00
this . target = target ;
2019-03-28 18:26:25 +01:00
// for now we build the source map together with debug names
2019-09-10 17:49:05 +02:00
createSourceMap = options . debugNames ( ) ;
2017-03-31 20:43:57 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
public void close ( ) throws IOException {
2020-04-18 13:09:11 +02:00
wasm = new WasmOutputStream ( options , target . getWasmOutput ( ) ) ;
2017-03-31 20:43:57 +02:00
wasm . write ( WASM_BINARY_MAGIC ) ;
wasm . writeInt32 ( WASM_BINARY_VERSION ) ;
2018-10-07 21:20:14 +02:00
writeSection ( SectionType . Type , functionTypes ) ;
writeSection ( SectionType . Import , imports . values ( ) ) ;
writeSection ( SectionType . Function , functions . values ( ) ) ;
2019-05-05 17:25:43 +02:00
writeTableSection ( ) ;
2019-05-01 17:11:14 +02:00
writeMemorySection ( ) ;
2020-06-07 12:00:40 +02:00
writeEventSection ( ) ;
2020-02-12 21:34:00 +01:00
writeSection ( SectionType . Global , globals . values ( ) ) ;
2019-11-02 18:59:09 +01:00
writeSection ( SectionType . Export , exports ) ;
2020-04-02 21:01:11 +02:00
writeStartSection ( ) ;
2019-05-05 17:25:43 +02:00
writeElementSection ( ) ;
2017-03-31 20:43:57 +02:00
writeCodeSection ( ) ;
2019-05-01 17:11:14 +02:00
writeDataSection ( ) ;
2018-10-13 15:29:34 +02:00
writeDebugNames ( ) ;
2019-04-01 16:17:41 +02:00
writeSourceMappingUrl ( ) ;
2019-02-28 21:28:23 +01:00
writeProducersSection ( ) ;
2017-03-31 20:43:57 +02:00
wasm . close ( ) ;
}
/ * *
2018-10-07 21:20:14 +02:00
* Write a section with list format to the output .
2017-03-31 20:43:57 +02:00
*
2018-10-07 21:20:14 +02:00
* @param type
* the type of the section
* @param entries
* the entries of the section
2017-03-31 20:43:57 +02:00
* @throws IOException
* if any I / O error occur
* /
2018-10-07 21:20:14 +02:00
private void writeSection ( SectionType type , Collection < ? extends SectionEntry > entries ) throws IOException {
int count = entries . size ( ) ;
2017-03-31 20:43:57 +02:00
if ( count > 0 ) {
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2017-03-31 20:43:57 +02:00
stream . writeVaruint32 ( count ) ;
2018-10-07 21:20:14 +02:00
for ( SectionEntry entry : entries ) {
entry . writeSectionEntry ( stream ) ;
2017-03-31 20:43:57 +02:00
}
2018-10-13 15:29:34 +02:00
wasm . writeSection ( type , stream ) ;
2018-08-13 17:39:14 +02:00
}
}
2019-05-05 17:25:43 +02:00
/ * *
* Write the table section . It declare the space for the element section .
*
* @throws IOException
* if any I / O error occur
* /
private void writeTableSection ( ) throws IOException {
2020-03-11 20:03:00 +01:00
int stringCount = options . strings . size ( ) ;
2020-03-11 19:55:59 +01:00
int typeCount = options . types . size ( ) ;
2019-11-10 12:57:58 +01:00
if ( ! callIndirect & & stringCount = = 0 ) {
2019-05-05 17:25:43 +02:00
return ;
}
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2020-03-11 19:55:59 +01:00
int count = 1 ;
if ( stringCount > 0 ) {
count + + ;
}
if ( typeCount > 0 ) {
count + + ;
}
2019-05-05 17:25:43 +02:00
stream . writeVaruint32 ( count ) ; // count of tables
2019-11-10 12:57:58 +01:00
// indirect function table
2020-03-11 19:55:59 +01:00
int elemCount = callIndirect ? imports . size ( ) + functions . size ( ) : 0 ;
2019-11-10 12:57:58 +01:00
stream . writeValueType ( ValueType . funcref ) ; // the type of elements
stream . writeVaruint32 ( 0 ) ; // flags; 1-maximum is available, 0-no maximum value available
stream . writeVaruint32 ( elemCount ) ; // initial length
//stream.writeVaruint32( elemCount ); // maximum length
// string constants table
if ( count > = 2 ) {
2020-05-30 23:06:29 +02:00
stream . writeValueType ( ValueType . externref ) ; // the type of elements
2019-11-10 12:57:58 +01:00
stream . writeVaruint32 ( 0 ) ; // flags; 1-maximum is available, 0-no maximum value available
stream . writeVaruint32 ( stringCount ) ; // initial length
2019-05-05 17:25:43 +02:00
}
2020-03-11 19:55:59 +01:00
// table with classes
if ( count > = 3 ) {
2020-05-30 23:06:29 +02:00
stream . writeValueType ( ValueType . externref ) ; // the type of elements
2020-03-11 19:55:59 +01:00
stream . writeVaruint32 ( 0 ) ; // flags; 1-maximum is available, 0-no maximum value available
stream . writeVaruint32 ( typeCount ) ; // initial length
}
2019-05-05 17:25:43 +02:00
wasm . writeSection ( SectionType . Table , stream ) ;
}
2019-05-01 17:11:14 +02:00
/ * *
* Write the memory section .
*
* @throws IOException
* if any I / O error occur
* /
private void writeMemorySection ( ) throws IOException {
2019-11-02 19:11:18 +01:00
int dataSize = dataStream . size ( ) ;
if ( dataSize > 0 ) {
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-11-02 19:11:18 +01:00
int pages = ( dataSize + 0xFFFF ) / 0x10000 ; // a page is defined with a size of 64KiB
int count = 1 ;
stream . writeVaruint32 ( count ) ;
for ( int i = 0 ; i < count ; i + + ) {
stream . writeVaruint32 ( 0 ) ; // flags; 1-maximum is available, 0-no maximum value available
stream . writeVaruint32 ( pages ) ; // initial length
//stream.writeVaruint32( pages ); // maximum length
}
wasm . writeSection ( SectionType . Memory , stream ) ;
2019-05-01 17:11:14 +02:00
}
}
2019-02-27 21:55:55 +01:00
/ * *
* Write the event section if needed .
*
* @throws IOException
* if any I / O error occur
* /
private void writeEventSection ( ) throws IOException {
if ( exceptionSignatureIndex > = 0 ) {
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-02-27 21:55:55 +01:00
stream . writeVaruint32 ( 1 ) ;
// event declaration
stream . writeVaruint32 ( 0 ) ; // event type: exception = 0
stream . writeVaruint32 ( exceptionSignatureIndex ) ;
2019-03-03 22:06:14 +01:00
wasm . writeSection ( SectionType . Event , stream ) ;
2019-02-27 21:55:55 +01:00
}
}
2020-04-02 21:01:11 +02:00
/ * *
* Write a start section . The id of the function that should be automatically executed .
*
* @throws IOException
* if any I / O error occur
* /
private void writeStartSection ( ) throws IOException {
if ( startFunction = = null ) {
return ;
}
int id = getFunction ( startFunction ) . id ;
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2020-04-02 21:01:11 +02:00
stream . writeVaruint32 ( id ) ;
wasm . writeSection ( SectionType . Start , stream ) ;
}
2019-05-05 17:25:43 +02:00
/ * *
* Write element section . This section create a matching between direct and indirect function call IDs .
*
* @throws IOException
* if any I / O error occur
* /
private void writeElementSection ( ) throws IOException {
if ( ! callIndirect ) {
return ;
}
int elemCount = imports . size ( ) + functions . size ( ) ;
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-05-05 17:25:43 +02:00
stream . writeVaruint32 ( 1 ) ; // count of element segments to follow
// element_segment
stream . writeVaruint32 ( 0 ) ; // the table index (0 in the MVP)
stream . writeConst ( 0 , ValueType . i32 ) ; // the offset on which the elements start
stream . writeOpCode ( END ) ; // end of offset instruction
stream . writeVaruint32 ( elemCount ) ;
for ( int i = 0 ; i < elemCount ; i + + ) {
stream . writeVaruint32 ( i ) ; // we use a 1:1 matching between direct function call numbers and indrect function numbers because the most functions will be indirect
}
wasm . writeSection ( SectionType . Element , stream ) ;
}
2017-03-31 20:43:57 +02:00
/ * *
* Write the code section to the output . This section contains the byte code .
*
* @throws IOException
* if any I / O error occur
* /
private void writeCodeSection ( ) throws IOException {
int size = functions . size ( ) ;
if ( size = = 0 ) {
return ;
}
2019-03-31 13:29:40 +02:00
2019-04-01 20:33:12 +02:00
int start = wasm . size ( ) ;
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2017-03-31 20:43:57 +02:00
stream . writeVaruint32 ( size ) ;
2019-06-23 12:55:14 +02:00
for ( Entry < String , Function > entry : functions . entrySet ( ) ) {
try {
Function func = entry . getValue ( ) ;
func . addCodeOffset ( start + stream . size ( ) ) ;
func . functionsStream . writeTo ( stream ) ;
} catch ( RuntimeException ex ) {
throw WasmException . create ( entry . getKey ( ) , ex ) ;
}
2018-12-11 22:38:53 +01:00
}
2018-10-13 15:29:34 +02:00
wasm . writeSection ( SectionType . Code , stream ) ;
2019-04-01 20:33:12 +02:00
2019-12-31 14:06:30 +01:00
SourceMapWriter sourceMap = createSourceMap ? new SourceMapWriter ( options . getSourceMapBase ( ) ) : null ;
2019-03-31 13:29:40 +02:00
if ( sourceMap ! = null ) {
2019-04-01 20:33:12 +02:00
int offset = wasm . size ( ) - start - stream . size ( ) ;
for ( Function func : functions . values ( ) ) {
if ( func . sourceMappings ! = null ) {
func . addCodeOffset ( offset ) ;
for ( SourceMapping mapping : func . sourceMappings ) {
sourceMap . addMapping ( mapping ) ;
}
}
}
2019-03-31 10:39:59 +02:00
sourceMap . generate ( target . getSourceMapOutput ( ) ) ;
}
2018-10-13 15:29:34 +02:00
}
2019-05-01 17:11:14 +02:00
/ * *
* Write the data section
*
* @throws IOException
* if any I / O error occur
* /
private void writeDataSection ( ) throws IOException {
int size = dataStream . size ( ) ;
if ( size = = 0 ) {
return ;
}
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-05-01 17:11:14 +02:00
stream . writeVaruint32 ( 1 ) ; // count, we use one large segment
// one data segment
stream . writeVaruint32 ( 0 ) ; // index (0 in the MVP)
stream . writeConst ( 0 , ValueType . i32 ) ; // the offset on which the data start
stream . writeOpCode ( END ) ; // end of offset instruction
stream . writeVaruint32 ( size ) ;
dataStream . writeTo ( stream ) ;
wasm . writeSection ( SectionType . Data , stream ) ;
}
2018-10-13 15:29:34 +02:00
/ * *
2020-04-05 21:42:40 +02:00
* Write optional the debug names into the custom " name " section .
2018-10-13 15:29:34 +02:00
*
* @throws IOException
* if any I / O error occur
* /
private void writeDebugNames ( ) throws IOException {
2019-09-10 17:49:05 +02:00
if ( ! options . debugNames ( ) ) {
2018-10-13 15:29:34 +02:00
return ;
}
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2018-10-13 15:29:34 +02:00
stream . writeString ( " name " ) ; // Custom Section name "name", content is part of the section length
// write function names
stream . write ( 1 ) ; // 1 - Function name
2020-04-18 13:09:11 +02:00
WasmOutputStream section = new WasmOutputStream ( options ) ;
2020-04-05 21:42:40 +02:00
section . writeVaruint32 ( imports . size ( ) + functions . size ( ) ) ;
writeDebugFunctionNames ( imports . entrySet ( ) , section ) ;
writeDebugFunctionNames ( functions . entrySet ( ) , section ) ;
2018-10-13 15:29:34 +02:00
stream . writeVaruint32 ( section . size ( ) ) ;
section . writeTo ( stream ) ;
2018-10-13 18:10:05 +02:00
// write function parameter names
stream . write ( 2 ) ; // 2 - Local names
section . reset ( ) ;
2020-04-05 21:42:40 +02:00
section . writeVaruint32 ( imports . size ( ) + functions . size ( ) ) ;
writeDebugParameternNames ( imports . entrySet ( ) , section ) ;
writeDebugParameternNames ( functions . entrySet ( ) , section ) ;
stream . writeVaruint32 ( section . size ( ) ) ;
section . writeTo ( stream ) ;
wasm . writeSection ( SectionType . Custom , stream ) ;
}
/ * *
* Write function names to the custom " name " section .
*
* @param entries
* the functions
* @param section
* the target
* @throws IOException
* if any I / O error occur
* /
private void writeDebugFunctionNames ( Set < ? extends Entry < String , ? extends Function > > entries , WasmOutputStream section ) throws IOException {
for ( Entry < String , ? extends Function > entry : entries ) {
section . writeVaruint32 ( entry . getValue ( ) . id ) ; // function index
String functionName = entry . getKey ( ) ;
functionName = functionName . substring ( 0 , functionName . indexOf ( '(' ) ) ;
section . writeString ( functionName ) ;
}
}
/ * *
* Write parameter names to the custom " name " section .
*
* @param entries
* the functions
* @param section
* the target
* @throws IOException
* if any I / O error occur
* /
private void writeDebugParameternNames ( Set < ? extends Entry < String , ? extends Function > > entries , WasmOutputStream section ) throws IOException {
for ( Entry < String , ? extends Function > entry : entries ) {
2018-10-13 18:10:05 +02:00
Function func = entry . getValue ( ) ;
section . writeVaruint32 ( func . id ) ; // function index
List < String > paramNames = func . paramNames ;
int count = paramNames = = null ? 0 : paramNames . size ( ) ;
section . writeVaruint32 ( count ) ; // count of locals
for ( int i = 0 ; i < count ; i + + ) {
section . writeVaruint32 ( i ) ;
section . writeString ( paramNames . get ( i ) ) ;
}
}
2017-03-31 20:43:57 +02:00
}
2019-04-01 16:17:41 +02:00
/ * *
* Write the source mapping url
*
* @throws IOException
* if any I / O error occur
* /
private void writeSourceMappingUrl ( ) throws IOException {
if ( ! createSourceMap ) {
return ;
}
String url = target . getSourceMappingURL ( ) ;
if ( url = = null ) {
return ;
}
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-04-01 16:17:41 +02:00
stream . writeString ( " sourceMappingURL " ) ; // Custom Section name "sourceMappingURL", content is part of the section length
stream . writeString ( url ) ;
wasm . writeSection ( SectionType . Custom , stream ) ;
}
2019-02-28 21:28:23 +01:00
/ * *
* Write producer information to wasm
*
* @throws IOException
* if any I / O error occur
* /
private void writeProducersSection ( ) throws IOException {
Package pack = getClass ( ) . getPackage ( ) ;
String version = pack = = null ? null : pack . getImplementationVersion ( ) ;
2020-04-18 13:09:11 +02:00
WasmOutputStream stream = new WasmOutputStream ( options ) ;
2019-02-28 21:28:23 +01:00
stream . writeString ( " producers " ) ; // Custom Section name "producers", content is part of the section length
stream . writeVaruint32 ( 2 ) ; // field_count; number of fields that follow (language and processed-by)
// field source language list
stream . writeString ( " language " ) ;
stream . writeVaruint32 ( 1 ) ; // field_value_count; number of value strings that follow
stream . writeString ( " Java bytecode " ) ;
// field individual tool list
stream . writeString ( " processed-by " ) ;
if ( version = = null ) {
stream . writeVaruint32 ( 1 ) ; // field_value_count; number of value strings that follow
stream . writeString ( " JWebAssembly " ) ;
} else {
stream . writeVaruint32 ( 2 ) ; // field_value_count; number of value strings that follow
stream . writeString ( " JWebAssembly " ) ;
stream . writeString ( version ) ;
}
wasm . writeSection ( SectionType . Custom , stream ) ;
}
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 typeId = functionTypes . size ( ) ;
2021-01-02 18:18:28 +01:00
List < NamedStorageType > fields = type . getFields ( ) ;
functionTypes . add ( type . getKind ( ) = = StructTypeKind . array_native ? new ArrayTypeEntry ( fields ) : new StructTypeEntry ( fields ) ) ;
2019-01-06 16:29:26 +01:00
return typeId ;
}
2019-02-27 21:55:55 +01:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeException ( ) throws IOException {
if ( exceptionSignatureIndex < = 0 ) {
2020-04-18 13:09:11 +02:00
FunctionTypeEntry type = new FunctionTypeEntry ( ) ;
2020-05-30 23:06:29 +02:00
type . params . add ( ValueType . externref ) ;
2020-04-18 13:09:11 +02:00
exceptionSignatureIndex = functionTypes . indexOf ( type ) ;
2019-02-27 21:55:55 +01:00
if ( exceptionSignatureIndex < 0 ) {
exceptionSignatureIndex = functionTypes . size ( ) ;
2020-04-18 13:09:11 +02:00
functionTypes . add ( type ) ;
2019-02-27 21:55:55 +01:00
}
2020-04-18 13:09:11 +02:00
// result type of catch block for unboxing
type = new FunctionTypeEntry ( ) ;
type . params . add ( ValueType . exnref ) ;
2020-05-30 23:06:29 +02:00
type . results . add ( ValueType . externref ) ;
2020-04-18 13:09:11 +02:00
options . setCatchType ( functionTypes . size ( ) ) ;
functionTypes . add ( type ) ;
2019-02-27 21:55:55 +01:00
}
}
2018-03-24 12:33:56 +01:00
/ * *
* { @inheritDoc }
* /
2018-05-30 18:57:36 +02:00
@Override
2018-05-31 21:35:01 +02:00
protected void prepareImport ( FunctionName name , String importModule , String importName ) {
ImportFunction importFunction ;
function = importFunction = new ImportFunction ( importModule , importName ) ;
imports . put ( name . signatureName , importFunction ) ;
}
2018-05-30 18:57:36 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2019-06-04 18:09:34 +02:00
protected void prepareFinish ( ) {
2018-05-30 18:57:36 +02:00
// initialize the function index IDs
// https://github.com/WebAssembly/design/blob/master/Modules.md#function-index-space
int id = 0 ;
2018-05-30 21:19:01 +02:00
for ( ImportFunction entry : imports . values ( ) ) {
2018-05-30 18:57:36 +02:00
entry . id = id + + ;
}
for ( Function function : functions . values ( ) ) {
function . id = id + + ;
}
2018-03-24 12:33:56 +01: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 {
2019-11-02 18:59:09 +01:00
exports . add ( new ExportEntry ( exportName , ExternalKind . Function , getFunction ( name ) . id ) ) ;
2017-03-31 20:43:57 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
2020-03-20 20:35:13 +01:00
protected void writeMethodParamStart ( FunctionName name , FunctionType funcType ) throws IOException {
2020-04-02 21:01:11 +02:00
switch ( funcType ) {
case Abstract :
abstracts . put ( name . signatureName , function = new Function ( ) ) ;
break ;
case Start :
startFunction = name ;
//$FALL-THROUGH$
default :
function = getFunction ( name ) ;
2020-03-20 20:35:13 +01:00
}
2019-01-11 22:21:59 +01:00
functionType = new FunctionTypeEntry ( ) ;
2018-10-13 18:10:05 +02:00
locals . 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 {
2017-03-31 20:43:57 +02:00
switch ( kind ) {
case " param " :
functionType . params . add ( valueType ) ;
2018-10-13 18:10:05 +02:00
break ;
2018-03-24 12:46:47 +01:00
case " result " :
2018-11-25 17:03:54 +01:00
functionType . results . add ( valueType ) ;
2017-03-31 20:43:57 +02:00
return ;
2018-10-13 18:10:05 +02:00
case " local " :
locals . add ( valueType ) ;
break ;
}
2019-09-10 17:49:05 +02:00
if ( options . debugNames ( ) & & name ! = null ) {
2018-10-13 18:10:05 +02:00
if ( function . paramNames = = null ) {
function . paramNames = new ArrayList < > ( ) ;
}
function . paramNames . add ( name ) ;
2017-03-31 20:43:57 +02:00
}
}
/ * *
* { @inheritDoc }
* /
@Override
2019-06-02 11:44:28 +02:00
protected void writeMethodParamFinish ( FunctionName name ) throws IOException {
2018-05-30 21:19:01 +02:00
int typeId = functionTypes . indexOf ( functionType ) ;
if ( typeId < 0 ) {
typeId = functionTypes . size ( ) ;
functionTypes . add ( functionType ) ;
}
function . typeId = typeId ;
2018-05-31 21:35:01 +02:00
}
2017-03-31 20:43:57 +02:00
2019-03-26 18:21:20 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2019-06-02 11:44:28 +02:00
protected void writeMethodStart ( FunctionName name , String sourceFile ) throws IOException {
if ( createSourceMap ) {
int idx = name . className . lastIndexOf ( '/' ) ;
2019-12-31 14:06:30 +01:00
this . javaSourceFile = name . className . substring ( 0 , idx + 1 ) + sourceFile ;
2019-06-02 11:44:28 +02:00
}
codeStream . reset ( ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
2019-03-31 13:29:40 +02:00
protected void markSourceLine ( int javaSourceLine ) {
2019-03-28 18:26:25 +01:00
if ( createSourceMap ) {
2019-03-31 13:29:40 +02:00
function . markCodePosition ( codeStream . size ( ) , javaSourceLine , javaSourceFile ) ;
2019-03-28 18:26:25 +01:00
}
2019-03-26 18:21:20 +01:00
}
2018-05-31 21:35:01 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2018-08-14 22:09:01 +02:00
protected void writeMethodFinish ( ) throws IOException {
2019-06-15 12:29:59 +02:00
@SuppressWarnings ( " resource " )
2020-04-18 13:09:11 +02:00
WasmOutputStream localsTypeStream = new WasmOutputStream ( options ) ;
2019-06-15 12:29:59 +02:00
int localEntryCount = 0 ; // number of local entries in output
int varCount = locals . size ( ) ;
for ( int i = 0 ; i < varCount ; ) {
AnyType valueType = locals . get ( i + + ) ;
int count = 1 ; // number of local variables of the same type
while ( i < varCount & & locals . get ( i ) = = valueType ) {
count + + ;
i + + ;
}
localsTypeStream . writeVaruint32 ( count ) ;
localsTypeStream . writeRefValueType ( valueType ) ;
localEntryCount + + ;
2017-03-31 20:43:57 +02:00
}
2019-06-15 12:29:59 +02:00
@SuppressWarnings ( " resource " )
2020-04-18 13:09:11 +02:00
WasmOutputStream localsStream = new WasmOutputStream ( options ) ;
2019-06-15 12:29:59 +02:00
localsStream . writeVaruint32 ( localEntryCount ) ;
2020-04-18 13:09:11 +02:00
WasmOutputStream functionsStream = function . functionsStream = new WasmOutputStream ( options ) ;
2019-06-15 12:29:59 +02:00
functionsStream . writeVaruint32 ( localsStream . size ( ) + localsTypeStream . size ( ) + codeStream . size ( ) + 1 ) ;
2017-03-31 20:43:57 +02:00
localsStream . writeTo ( functionsStream ) ;
2019-06-15 12:29:59 +02:00
localsTypeStream . writeTo ( functionsStream ) ;
2019-04-01 20:33:12 +02:00
function . addCodeOffset ( functionsStream . size ( ) ) ;
2017-03-31 20:43:57 +02:00
codeStream . writeTo ( functionsStream ) ;
functionsStream . write ( END ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
2018-08-05 18:27:06 +02:00
protected void writeConst ( Number value , ValueType valueType ) throws IOException {
2018-10-07 21:20:14 +02:00
codeStream . writeConst ( value , valueType ) ;
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 {
int code ;
switch ( op ) {
case get :
code = LOCAL_GET ;
break ;
case set :
code = LOCAL_SET ;
break ;
default :
code = LOCAL_TEE ;
}
codeStream . writeOpCode ( code ) ;
2017-03-31 20:43:57 +02:00
codeStream . writeVaruint32 ( idx ) ;
}
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 {
2018-08-14 15:11:21 +02:00
Global var = globals . get ( name . fullName ) ;
if ( var = = null ) { // if not declared then create a definition in the global section
var = new Global ( ) ;
var . id = globals . size ( ) ;
2019-04-27 21:14:55 +02:00
var . type = type ;
2018-08-14 15:11:21 +02:00
var . mutability = true ;
globals . put ( name . fullName , var ) ;
}
2019-05-16 21:38:51 +02:00
int op = load ? GLOBAL_GET : GLOBAL_SET ;
2018-08-14 15:11:21 +02:00
codeStream . writeOpCode ( op ) ;
codeStream . writeVaruint32 ( var . id ) ;
}
2019-11-03 19:00:49 +01:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeTable ( boolean load , @Nonnegative int idx ) throws IOException {
codeStream . writeOpCode ( load ? TABLE_GET : TABLE_SET ) ;
codeStream . writeVaruint32 ( idx ) ;
}
2019-04-27 21:14:55 +02:00
/ * *
* { @inheritDoc }
* /
@Override
protected void writeDefaultValue ( AnyType type ) throws IOException {
codeStream . writeDefaultValue ( type ) ;
}
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 {
int op = 0 ;
switch ( numOp ) {
case add :
switch ( valueType ) {
case i32 :
op = I32_ADD ;
break ;
case i64 :
op = I64_ADD ;
break ;
case f32 :
op = F32_ADD ;
break ;
case f64 :
op = F64_ADD ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-11 21:12:27 +02:00
}
2017-04-09 18:46:27 +02:00
break ;
2017-04-11 21:12:27 +02:00
case sub :
switch ( valueType ) {
case i32 :
op = I32_SUB ;
break ;
case i64 :
op = I64_SUB ;
break ;
case f32 :
op = F32_SUB ;
break ;
case f64 :
op = F64_SUB ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-11 21:12:27 +02:00
}
2017-04-09 18:46:27 +02:00
break ;
2018-04-02 19:15:42 +02:00
case neg :
switch ( valueType ) {
case f32 :
op = F32_NEG ;
break ;
case f64 :
op = F64_NEG ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-04-02 19:15:42 +02:00
}
break ;
2017-04-14 16:31:35 +02:00
case mul :
switch ( valueType ) {
case i32 :
op = I32_MUL ;
break ;
case i64 :
op = I64_MUL ;
break ;
case f32 :
op = F32_MUL ;
break ;
case f64 :
op = F64_MUL ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-14 16:31:35 +02:00
}
break ;
case div :
switch ( valueType ) {
case i32 :
op = I32_DIV_S ;
break ;
case i64 :
op = I64_DIV_S ;
break ;
case f32 :
op = F32_DIV ;
break ;
case f64 :
op = F64_DIV ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-14 16:31:35 +02:00
}
break ;
case rem :
switch ( valueType ) {
case i32 :
op = I32_REM_S ;
break ;
case i64 :
op = I64_REM_S ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-14 16:31:35 +02:00
}
break ;
2017-04-16 12:20:53 +02:00
case and :
switch ( valueType ) {
case i32 :
op = I32_AND ;
break ;
case i64 :
op = I64_AND ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
case or :
switch ( valueType ) {
case i32 :
op = I32_OR ;
break ;
case i64 :
op = I64_OR ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
case xor :
switch ( valueType ) {
case i32 :
op = I32_XOR ;
break ;
case i64 :
op = I64_XOR ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
case shl :
switch ( valueType ) {
case i32 :
op = I32_SHL ;
break ;
case i64 :
op = I64_SHL ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
case shr_s :
switch ( valueType ) {
case i32 :
op = I32_SHR_S ;
break ;
case i64 :
op = I64_SHR_S ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
case shr_u :
switch ( valueType ) {
case i32 :
op = I32_SHR_U ;
break ;
case i64 :
op = I64_SHR_U ;
break ;
2019-03-31 10:26:08 +02:00
default :
2017-04-16 12:20:53 +02:00
}
break ;
2019-11-19 19:48:24 +01:00
case eqz :
switch ( valueType ) {
case i32 :
op = I32_EQZ ;
break ;
case i64 :
op = I64_EQZ ;
break ;
default :
}
break ;
2018-03-25 12:57:04 +02:00
case eq :
switch ( valueType ) {
case i32 :
op = I32_EQ ;
break ;
case i64 :
op = I64_EQ ;
break ;
case f32 :
op = F32_EQ ;
break ;
case f64 :
op = F64_EQ ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-25 12:57:04 +02:00
}
2018-03-25 21:06:18 +02:00
break ;
2018-03-25 12:57:04 +02:00
case ne :
switch ( valueType ) {
case i32 :
op = I32_NE ;
break ;
case i64 :
op = I64_NE ;
break ;
case f32 :
op = F32_NE ;
break ;
case f64 :
op = F64_NE ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-25 12:57:04 +02:00
}
2018-03-25 21:06:18 +02:00
break ;
2018-03-27 20:04:35 +02:00
case gt :
switch ( valueType ) {
case i32 :
op = I32_GT_S ;
break ;
case i64 :
2018-03-28 20:07:51 +02:00
op = I64_GT_S ;
2018-03-27 20:04:35 +02:00
break ;
case f32 :
2018-03-28 20:07:51 +02:00
op = F32_GT ;
2018-03-27 20:04:35 +02:00
break ;
case f64 :
2018-03-28 20:07:51 +02:00
op = F64_GT ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-28 20:07:51 +02:00
}
break ;
2018-08-14 21:28:17 +02:00
case lt :
2018-03-28 20:07:51 +02:00
switch ( valueType ) {
case i32 :
op = I32_LT_S ;
break ;
case i64 :
op = I64_LT_S ;
break ;
case f32 :
op = F32_LT ;
break ;
case f64 :
op = F64_LT ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-28 20:07:51 +02:00
}
break ;
2018-08-14 21:28:17 +02:00
case le :
2018-03-28 20:07:51 +02:00
switch ( valueType ) {
case i32 :
op = I32_LE_S ;
break ;
case i64 :
op = I64_LE_S ;
break ;
case f32 :
op = F32_LE ;
break ;
case f64 :
op = F64_LE ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-28 20:07:51 +02:00
}
break ;
2018-08-14 21:28:17 +02:00
case ge :
2018-03-28 20:07:51 +02:00
switch ( valueType ) {
case i32 :
op = I32_GE_S ;
break ;
case i64 :
op = I64_GE_S ;
break ;
case f32 :
op = F32_GE ;
break ;
case f64 :
op = F64_GE ;
2018-03-27 20:04:35 +02:00
break ;
2019-03-31 10:26:08 +02:00
default :
2018-03-27 20:04:35 +02:00
}
break ;
2018-11-16 20:12:55 +01:00
case max :
switch ( valueType ) {
case f32 :
op = F32_MAX ;
break ;
case f64 :
op = F64_MAX ;
break ;
2019-03-31 10:26:08 +02:00
default :
2018-11-16 20:12:55 +01:00
}
break ;
2019-07-18 19:21:11 +02:00
case min :
switch ( valueType ) {
case f32 :
op = F32_MIN ;
break ;
case f64 :
op = F64_MIN ;
break ;
default :
}
break ;
2018-12-15 22:33:25 +01:00
case ifnull :
op = REF_ISNULL ;
break ;
case ifnonnull :
codeStream . writeOpCode ( REF_ISNULL ) ;
op = I32_EQZ ;
break ;
2018-12-17 21:22:10 +01:00
case ref_eq :
2019-09-11 20:32:36 +02:00
if ( options . useGC ( ) ) {
op = REF_EQ ;
} else {
2020-03-21 22:57:42 +01:00
writeFunctionCall ( options . ref_eq , null ) ;
2019-09-11 20:32:36 +02:00
return ;
}
2018-12-17 21:22:10 +01:00
break ;
case ref_ne :
2019-09-11 20:32:36 +02:00
if ( options . useGC ( ) ) {
codeStream . writeOpCode ( REF_EQ ) ;
} else {
2020-03-21 22:57:42 +01:00
writeFunctionCall ( options . ref_eq , null ) ;
2019-09-11 20:32:36 +02:00
}
2018-12-17 21:22:10 +01:00
op = I32_EQZ ;
break ;
2019-07-13 15:46:20 +02:00
case sqrt :
switch ( valueType ) {
case f32 :
op = F32_SQRT ;
break ;
case f64 :
op = F64_SQRT ;
break ;
default :
}
break ;
2019-07-17 18:45:36 +02:00
case abs :
switch ( valueType ) {
case f32 :
op = F32_ABS ;
break ;
case f64 :
op = F64_ABS ;
break ;
default :
}
break ;
2019-07-13 15:46:20 +02:00
case ceil :
switch ( valueType ) {
case f32 :
op = F32_CEIL ;
break ;
case f64 :
op = F64_CEIL ;
break ;
default :
}
break ;
case floor :
switch ( valueType ) {
case f32 :
op = F32_FLOOR ;
break ;
case f64 :
op = F64_FLOOR ;
break ;
default :
}
break ;
case trunc :
switch ( valueType ) {
case f32 :
op = F32_TRUNC ;
break ;
case f64 :
op = F64_TRUNC ;
break ;
default :
}
break ;
case nearest :
switch ( valueType ) {
case f32 :
op = F32_NEAREST ;
break ;
case f64 :
op = F64_NEAREST ;
break ;
default :
}
break ;
2019-08-03 20:51:00 +02:00
case copysign :
switch ( valueType ) {
case f32 :
op = F32_COPYSIGN ;
break ;
case f64 :
op = F64_COPYSIGN ;
break ;
default :
}
break ;
2019-07-13 15:46:20 +02:00
2019-03-31 10:26:08 +02:00
default :
2017-04-09 18:46:27 +02:00
}
2017-04-11 21:12:27 +02:00
if ( op = = 0 ) {
2018-03-25 12:57:04 +02:00
throw new Error ( valueType + " . " + numOp ) ;
2017-04-11 21:12:27 +02:00
}
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( op ) ;
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 {
int 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 :
op = I32_WRAP_I64 ;
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 :
op = F64_PROMOTE_F32 ;
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 :
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
}
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( op ) ;
2017-04-11 17:47:21 +02:00
}
2018-03-24 12:33:56 +01:00
/ * *
* { @inheritDoc }
* /
@Override
2020-03-21 22:57:42 +01:00
protected void writeFunctionCall ( FunctionName name , String comments ) throws IOException {
2018-12-11 22:38:53 +01:00
Function func = getFunction ( name ) ;
codeStream . writeOpCode ( CALL ) ;
codeStream . writeVaruint32 ( func . id ) ;
}
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-05-05 17:25:43 +02:00
Function func = getFunction ( name ) ;
codeStream . writeOpCode ( CALL_INDIRECT ) ;
codeStream . writeVaruint32 ( func . typeId ) ;
codeStream . writeVaruint32 ( 0 ) ; // table 0
}
2018-12-11 22:38:53 +01:00
/ * *
* Get the function object for the name . If not exists then it will be created .
*
* @param name
* the function name
* @return the function object
* /
@Nonnull
private Function getFunction ( FunctionName name ) {
2018-11-24 16:14:52 +01:00
String signatureName = name . signatureName ;
Function func = functions . get ( signatureName ) ;
2018-12-11 22:38:53 +01:00
if ( func = = null ) {
func = imports . get ( signatureName ) ;
if ( func = = null ) {
2020-03-20 20:35:13 +01:00
func = abstracts . get ( signatureName ) ;
if ( func = = null ) {
func = new Function ( ) ;
func . id = functions . size ( ) + imports . size ( ) ;
functions . put ( signatureName , func ) ;
}
2018-05-30 19:26:34 +02:00
}
2018-03-24 12:33:56 +01:00
}
2018-12-11 22:38:53 +01:00
return func ;
2018-03-24 12:33:56 +01: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 {
2018-03-25 12:57:04 +02:00
switch ( op ) {
2018-04-02 11:53:12 +02:00
case RETURN :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( RETURN ) ;
2018-04-02 11:53:12 +02:00
break ;
2018-03-25 12:57:04 +02:00
case IF :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( IF ) ;
2020-01-02 18:10:48 +01:00
codeStream . writeValueType ( ( ( AnyType ) data ) ) ;
2018-03-25 12:57:04 +02:00
break ;
2018-03-27 20:04:35 +02:00
case ELSE :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( ELSE ) ;
2018-03-27 20:04:35 +02:00
break ;
2018-03-25 12:57:04 +02:00
case END :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( END ) ;
2018-03-25 12:57:04 +02:00
break ;
2018-04-02 11:53:12 +02:00
case DROP :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( DROP ) ;
2018-04-02 11:53:12 +02:00
break ;
2018-05-03 22:57:44 +02:00
case BLOCK :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( BLOCK ) ;
2020-01-02 18:10:48 +01:00
codeStream . writeValueType ( data = = null ? ValueType . empty : ( AnyType ) data ) ; // void; the return type of the block.
2018-05-03 22:57:44 +02:00
break ;
2018-05-04 20:52:54 +02:00
case BR :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( BR ) ;
2018-05-04 20:52:54 +02:00
codeStream . writeVaruint32 ( ( Integer ) data ) ;
break ;
2018-05-11 21:39:04 +02:00
case BR_IF :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( BR_IF ) ;
2018-05-11 21:39:04 +02:00
codeStream . writeVaruint32 ( ( Integer ) data ) ;
break ;
2018-05-05 21:39:36 +02:00
case BR_TABLE :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( BR_TABLE ) ;
2018-05-05 21:39:36 +02:00
int [ ] targets = ( int [ ] ) data ;
codeStream . writeVaruint32 ( targets . length - 1 ) ;
for ( int i : targets ) {
codeStream . writeVaruint32 ( i ) ;
}
break ;
2018-05-20 11:52:16 +02:00
case LOOP :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( LOOP ) ;
2018-12-02 18:17:27 +01:00
codeStream . writeValueType ( ValueType . empty ) ; // void; the return type of the loop. currently we does not use it
2018-05-20 11:52:16 +02:00
break ;
2018-07-27 17:51:36 +02:00
case UNREACHABLE :
2018-08-11 16:18:01 +02:00
codeStream . writeOpCode ( UNREACHABLE ) ;
2018-07-27 17:51:36 +02:00
break ;
2018-11-03 18:01:42 +01:00
case TRY :
2020-04-13 12:01:21 +02:00
codeStream . writeOpCode ( options . useEH ( ) ? TRY : BLOCK ) ;
2018-12-02 18:17:27 +01:00
codeStream . writeValueType ( ValueType . empty ) ; // void; the return type of the try. currently we does not use it
2018-11-03 18:01:42 +01:00
break ;
case CATCH :
2020-04-13 12:01:21 +02:00
if ( options . useEH ( ) ) {
codeStream . writeOpCode ( CATCH ) ;
} else {
codeStream . writeOpCode ( BR ) ;
codeStream . writeVaruint32 ( 0 ) ;
}
2018-11-03 18:01:42 +01:00
break ;
2019-03-02 21:54:27 +01:00
case THROW :
2020-01-12 12:42:31 +01:00
if ( options . useEH ( ) ) {
codeStream . writeOpCode ( THROW ) ;
codeStream . writeVaruint32 ( 0 ) ; // event/exception ever 0 because currently there is only one with signature anyref
} else {
codeStream . writeOpCode ( UNREACHABLE ) ;
}
2019-03-02 21:54:27 +01:00
break ;
case RETHROW :
codeStream . writeOpCode ( RETHROW ) ;
break ;
case BR_ON_EXN :
2020-04-10 16:33:32 +02:00
if ( options . useEH ( ) ) {
codeStream . writeOpCode ( BR_ON_EXN ) ;
codeStream . writeVaruint32 ( ( Integer ) data ) ; // break depth
codeStream . writeVaruint32 ( 0 ) ; // event/exception ever 0 because currently there is only one with signature anyref
} else {
codeStream . writeOpCode ( UNREACHABLE ) ;
}
2019-03-02 21:54:27 +01:00
break ;
2019-03-10 18:32:26 +01:00
case MONITOR_ENTER :
case MONITOR_EXIT :
codeStream . writeOpCode ( DROP ) ;
break ;
2018-03-25 12:57:04 +02:00
default :
throw new Error ( " Unknown block: " + op ) ;
}
}
2018-12-02 19:54:59 +01:00
/ * *
* { @inheritDoc }
* /
@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
int opCode ;
switch ( op ) {
case NEW :
2021-01-02 16:44:38 +01:00
codeStream . writeOpCode ( RTT_CANON ) ;
codeStream . writeValueType ( type . getNativeArrayType ( ) ) ;
opCode = ARRAY_NEW_DEFAULT ;
2018-12-02 19:54:59 +01:00
break ;
case GET :
opCode = ARRAY_GET ;
break ;
case SET :
opCode = ARRAY_SET ;
break ;
2019-08-11 13:06:31 +02:00
case LEN :
2018-12-02 19:54:59 +01:00
opCode = ARRAY_LEN ;
break ;
2021-01-02 16:44:38 +01:00
case NEW_ARRAY_WITH_RTT :
opCode = ARRAY_NEW_DEFAULT ;
break ;
2018-12-02 19:54:59 +01:00
default :
throw new Error ( " Unknown operator: " + op ) ;
}
codeStream . writeOpCode ( opCode ) ;
2021-01-02 16:44:38 +01:00
codeStream . writeValueType ( type . getNativeArrayType ( ) ) ;
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
int opCode ;
switch ( op ) {
case NEW :
case NEW_DEFAULT :
2020-09-26 15:59:57 +02:00
codeStream . writeOpCode ( RTT_CANON ) ;
codeStream . writeValueType ( type ) ;
2018-12-05 22:14:26 +01:00
opCode = STRUCT_NEW ;
break ;
case GET :
opCode = STRUCT_GET ;
break ;
case SET :
opCode = STRUCT_SET ;
break ;
2018-12-14 20:47:53 +01:00
case NULL :
opCode = REF_NULL ;
2020-09-24 21:46:02 +02:00
type = options . useGC ( ) ? type : ValueType . externref ;
2018-12-14 20:47:53 +01:00
break ;
2021-01-02 16:44:38 +01:00
case RTT_CANON :
opCode = RTT_CANON ;
break ;
case NEW_WITH_RTT :
opCode = STRUCT_NEW ;
break ;
2018-12-05 22:14:26 +01:00
default :
throw new Error ( " Unknown operator: " + op ) ;
}
codeStream . writeOpCode ( opCode ) ;
2018-12-14 20:47:53 +01:00
if ( type ! = null ) {
codeStream . writeValueType ( type ) ;
}
2021-01-02 16:44:38 +01:00
if ( idx > = 0 ) {
2019-04-22 15:56:11 +02:00
codeStream . writeVaruint32 ( idx ) ;
2019-01-13 11:36:07 +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 {
int op = 0 ;
switch ( memOp ) {
case load :
switch ( valueType ) {
case i32 :
op = I32_LOAD ;
break ;
case i64 :
op = I64_LOAD ;
break ;
}
break ;
2019-11-19 19:48:24 +01:00
case load8_u :
switch ( valueType ) {
case i32 :
op = I32_LOAD8_U ;
break ;
case i64 :
op = I64_LOAD8_U ;
break ;
}
break ;
2019-11-18 20:08:18 +01:00
}
if ( op = = 0 ) {
throw new Error ( valueType + " . " + memOp ) ;
}
2019-11-21 20:02:44 +01:00
codeStream . writeOpCode ( op ) ;
2019-11-18 20:08:18 +01:00
codeStream . write ( alignment ) ; // 0: 8 Bit; 1: 16 Bit; 2: 32 Bit of the resulting offset
2019-09-13 20:04:03 +02:00
codeStream . writeVaruint32 ( offset ) ;
}
2017-03-31 20:43:57 +02:00
}