mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-15 02:44:47 +01:00
Add WatParser and tests
This commit is contained in:
parent
101b759acb
commit
0119a2bbc2
@ -99,8 +99,8 @@ public abstract class WasmCodeBuilder {
|
||||
* the code position/offset in the Java method
|
||||
*/
|
||||
@Nonnull
|
||||
protected void addLoadStoreInstruction( boolean load, @Nonnegative int wasmIdx, int javaCodePos ) {
|
||||
instructions.add( new WasmLoadStoreInstruction( load, wasmIdx, null, javaCodePos ) );
|
||||
protected void addLocalInstruction( boolean load, @Nonnegative int wasmIdx, int javaCodePos ) {
|
||||
instructions.add( new WasmLocalInstruction( load, wasmIdx, javaCodePos ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2018 Volker Berlin (i-net software)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nonnegative;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* WasmInstruction for load and store local variables.
|
||||
*
|
||||
* @author Volker Berlin
|
||||
*
|
||||
*/
|
||||
class WasmLocalInstruction extends WasmInstruction {
|
||||
|
||||
private boolean load;
|
||||
|
||||
private int idx;
|
||||
|
||||
/**
|
||||
* Create an instance of a load/store instruction
|
||||
*
|
||||
* @param load
|
||||
* true: if load
|
||||
* @param idx
|
||||
* the memory/slot idx of the variable
|
||||
* @param javaCodePos
|
||||
* the code position/offset in the Java method
|
||||
*/
|
||||
WasmLocalInstruction( boolean load, @Nonnegative int idx, int javaCodePos ) {
|
||||
super( javaCodePos );
|
||||
this.load = load;
|
||||
this.idx = idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
|
||||
if( load ) {
|
||||
writer.writeLoad( idx );
|
||||
} else {
|
||||
writer.writeStore( idx );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
ValueType getPushValueType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
int getPopCount() {
|
||||
return load ? 0 : 1;
|
||||
}
|
||||
}
|
151
src/de/inetsoftware/jwebassembly/watparser/WatParser.java
Normal file
151
src/de/inetsoftware/jwebassembly/watparser/WatParser.java
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2018 Volker Berlin (i-net software)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.watparser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnegative;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import de.inetsoftware.jwebassembly.WasmException;
|
||||
import de.inetsoftware.jwebassembly.module.NumericOperator;
|
||||
import de.inetsoftware.jwebassembly.module.ValueType;
|
||||
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
|
||||
import de.inetsoftware.jwebassembly.module.WasmBlockOperator;
|
||||
import de.inetsoftware.jwebassembly.module.WasmCodeBuilder;
|
||||
|
||||
/**
|
||||
* Parser for text format of a function.
|
||||
*
|
||||
* @author Volker Berlin
|
||||
*/
|
||||
public class WatParser extends WasmCodeBuilder {
|
||||
|
||||
public WatParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given wasm text format and generate a list of WasmInstuctions
|
||||
*
|
||||
* @param wat
|
||||
* the text format content of a function
|
||||
*/
|
||||
public void parse( String wat, int lineNumber ) {
|
||||
try {
|
||||
reset();
|
||||
|
||||
List<String> tokens = splitTokens( wat );
|
||||
for( int i = 0; i < tokens.size(); i++ ) {
|
||||
int javaCodePos = i;
|
||||
String tok = tokens.get( i );
|
||||
switch( tok ) {
|
||||
case "get_local":
|
||||
addLocalInstruction( true, getInt( tokens, ++i), javaCodePos );
|
||||
break;
|
||||
case "set_local":
|
||||
addLocalInstruction( false, getInt( tokens, ++i), javaCodePos );
|
||||
break;
|
||||
// case "get_global":
|
||||
// addGlobalInstruction( true, ref, javaCodePos );
|
||||
// break;
|
||||
case "i32.const":
|
||||
addConstInstruction( getInt( tokens, ++i), ValueType.i32, javaCodePos );
|
||||
break;
|
||||
case "i32.add":
|
||||
addNumericInstruction( NumericOperator.add, ValueType.i32, javaCodePos );
|
||||
break;
|
||||
case "i64.extend_s/i32":
|
||||
addConvertInstruction( ValueTypeConvertion.i2l, javaCodePos );
|
||||
break;
|
||||
// case "call":
|
||||
// addCallInstruction( method, javaCodePos );
|
||||
// break;
|
||||
case "return":
|
||||
addBlockInstruction( WasmBlockOperator.RETURN, null, javaCodePos );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch( Exception ex ) {
|
||||
throw WasmException.create( ex, lineNumber );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token at given position as int.
|
||||
*
|
||||
* @param tokens
|
||||
* the token list
|
||||
* @param idx
|
||||
* the position in the tokens
|
||||
* @return the int value
|
||||
*/
|
||||
private int getInt( List<String> tokens, @Nonnegative int idx ) {
|
||||
return Integer.parseInt( get( tokens, idx ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token at given position
|
||||
*
|
||||
* @param tokens
|
||||
* the token list
|
||||
* @param idx
|
||||
* the position in the tokens
|
||||
* @return the token
|
||||
*/
|
||||
@Nonnull
|
||||
private String get( List<String> tokens, @Nonnegative int idx ) {
|
||||
if( idx >= tokens.size() ) {
|
||||
String previous = tokens.get( Math.min( idx, tokens.size() ) - 1 );
|
||||
throw new WasmException( "Missing Token in wasm text format after token: " + previous, -1 );
|
||||
}
|
||||
return tokens.get( idx );
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the string in tokens.
|
||||
*
|
||||
* @param wat
|
||||
* string with wasm text format
|
||||
* @return the token list.
|
||||
*/
|
||||
private List<String> splitTokens( @Nullable String wat ) {
|
||||
ArrayList<String> tokens = new ArrayList<>();
|
||||
int count = wat.length();
|
||||
|
||||
int off = 0;
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
char ch = wat.charAt( i );
|
||||
switch( ch ) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
if( off + 1 < i ) {
|
||||
tokens.add( wat.substring( off, i ) );
|
||||
}
|
||||
off = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( off < count ) {
|
||||
tokens.add( wat.substring( off, count ) );
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
123
test/de/inetsoftware/jwebassembly/module/WatParserTest.java
Normal file
123
test/de/inetsoftware/jwebassembly/module/WatParserTest.java
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2018 Volker Berlin (i-net software)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.module;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.inetsoftware.jwebassembly.WasmException;
|
||||
import de.inetsoftware.jwebassembly.text.TextModuleWriter;
|
||||
import de.inetsoftware.jwebassembly.watparser.WatParser;
|
||||
|
||||
/**
|
||||
* @author Volker Berlin
|
||||
*/
|
||||
public class WatParserTest {
|
||||
|
||||
private void test( String wat ) throws IOException {
|
||||
WatParser parser = new WatParser();
|
||||
parser.parse( wat, 100 );
|
||||
WasmCodeBuilder codeBuilder = parser;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ModuleWriter writer = new TextModuleWriter( builder, new HashMap<>() );
|
||||
for( WasmInstruction instruction : codeBuilder.getInstructions() ) {
|
||||
instruction.writeTo( writer );
|
||||
}
|
||||
writer.writeMethodFinish();
|
||||
String expected = normalize( "(module " + wat + " )" );
|
||||
String actual = normalize( builder );
|
||||
assertEquals( expected, actual );
|
||||
}
|
||||
|
||||
private String normalize( @Nullable CharSequence str ) {
|
||||
boolean wasSpace = false;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for( int i = 0; i < str.length(); i++ ) {
|
||||
char ch = str.charAt( i );
|
||||
switch( ch ) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
if( !wasSpace ) {
|
||||
builder.append( ' ' );
|
||||
wasSpace = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
builder.append( ch );
|
||||
wasSpace = false;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void testError( String wat, String errorMessage ) throws IOException {
|
||||
try {
|
||||
test( wat );
|
||||
fail( "Exception expected with message: " + errorMessage );
|
||||
} catch( WasmException ex ) {
|
||||
String error = ex.getMessage();
|
||||
int newlineIdx = error.indexOf( '\n' );
|
||||
if( newlineIdx > 0 ) {
|
||||
error = error.substring( 0, newlineIdx );
|
||||
}
|
||||
assertEquals( errorMessage, error );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLocal() throws IOException {
|
||||
test( "get_local 1" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLocal() throws IOException {
|
||||
test( "set_local 2" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void i32_add() throws IOException {
|
||||
test( "i32.add" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void i32_const() throws IOException {
|
||||
test( " i32.const -7 " );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void i64_extend_s_i32() throws IOException {
|
||||
test( "i64.extend_s/i32" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void return_() throws IOException {
|
||||
test( "return\n" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorMissingToken() throws IOException {
|
||||
testError( "i32.const", "Missing Token in wasm text format after token: i32.const" );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user