write methods with Export annotation as exported.

This commit is contained in:
Volker Berlin 2017-03-31 20:43:57 +02:00
parent ec93b799b9
commit 7add526da1
3 changed files with 769 additions and 688 deletions

View File

@ -1,234 +1,270 @@
/* /*
* Copyright 2017 Volker Berlin (i-net software) * Copyright 2017 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.inetsoftware.jwebassembly.binary; package de.inetsoftware.jwebassembly.binary;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap; import java.util.ArrayList;
import java.util.List; import java.util.LinkedHashMap;
import java.util.Map; import java.util.List;
import java.util.Map;
import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.ValueType; import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.ValueType;
/**
* Module Writer for binary format. http://webassembly.org/docs/binary-encoding/ /**
* * Module Writer for binary format. http://webassembly.org/docs/binary-encoding/
* @author Volker Berlin *
*/ * @author Volker Berlin
public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcodes { */
public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcodes {
private static final byte[] WASM_BINARY_MAGIC = { 0, 'a', 's', 'm' };
private static final byte[] WASM_BINARY_MAGIC = { 0, 'a', 's', 'm' };
private static final int WASM_BINARY_VERSION = 1;
private static final int WASM_BINARY_VERSION = 1;
private WasmOutputStream wasm;
private WasmOutputStream wasm;
private WasmOutputStream codeStream = new WasmOutputStream();
private WasmOutputStream codeStream = new WasmOutputStream();
private WasmOutputStream functionsStream = new WasmOutputStream();
private WasmOutputStream functionsStream = new WasmOutputStream();
private List<FunctionType> functionTypes = new ArrayList<>();
private List<FunctionType> functionTypes = new ArrayList<>();
private Map<String, Function> functions = new LinkedHashMap<>();
private Map<String, Function> functions = new LinkedHashMap<>();
private Function function;
private Map<String, String> exports = new LinkedHashMap<>();
private FunctionType functionType;
private Function function;
/**
* Create new instance. private FunctionType functionType;
*
* @param output /**
* the target for the module data. * Create new instance.
* @throws IOException *
* if any I/O error occur * @param output
*/ * the target for the module data.
public BinaryModuleWriter( OutputStream output ) throws IOException { * @throws IOException
wasm = new WasmOutputStream( output ); * if any I/O error occur
} */
public BinaryModuleWriter( OutputStream output ) throws IOException {
/** wasm = new WasmOutputStream( output );
* {@inheritDoc} }
*/
@Override /**
public void close() throws IOException { * {@inheritDoc}
wasm.write( WASM_BINARY_MAGIC ); */
wasm.writeInt32( WASM_BINARY_VERSION ); @Override
public void close() throws IOException {
writeTypeSection(); wasm.write( WASM_BINARY_MAGIC );
writeFunctionSection(); wasm.writeInt32( WASM_BINARY_VERSION );
writeCodeSection();
writeTypeSection();
wasm.close(); writeFunctionSection();
} writeExportSection();
writeCodeSection();
/**
* Write the type section to the output. This section contains the signatures of the functions. wasm.close();
* }
* @throws IOException
* if any I/O error occur /**
*/ * Write the type section to the output. This section contains the signatures of the functions.
private void writeTypeSection() throws IOException { *
int count = functionTypes.size(); * @throws IOException
if( count > 0 ) { * if any I/O error occur
WasmOutputStream stream = new WasmOutputStream(); */
stream.writeVaruint32( count ); private void writeTypeSection() throws IOException {
for( FunctionType type : functionTypes ) { int count = functionTypes.size();
stream.writeVarint32( ValueType.func.getCode() ); if( count > 0 ) {
stream.writeVaruint32( type.params.size() ); WasmOutputStream stream = new WasmOutputStream();
for( ValueType valueType : type.params ) { stream.writeVaruint32( count );
stream.writeVarint32( valueType.getCode() ); for( FunctionType type : functionTypes ) {
} stream.writeVarint32( ValueType.func.getCode() );
if( type.result == null ) { stream.writeVaruint32( type.params.size() );
stream.writeVaruint32( 0 ); for( ValueType valueType : type.params ) {
} else { stream.writeVarint32( valueType.getCode() );
stream.writeVaruint32( 1 ); }
stream.writeVarint32( type.result.getCode() ); if( type.result == null ) {
} stream.writeVaruint32( 0 );
} } else {
wasm.writeSection( SectionType.Type, stream, null ); stream.writeVaruint32( 1 );
} stream.writeVarint32( type.result.getCode() );
} }
}
/** wasm.writeSection( SectionType.Type, stream, null );
* Write the function section to the output. This section contains a mapping from the function index to the type signature index. }
* }
* @throws IOException
* if any I/O error occur /**
*/ * Write the function section to the output. This section contains a mapping from the function index to the type signature index.
private void writeFunctionSection() throws IOException { *
int count = functions.size(); * @throws IOException
if( count > 0 ) { * if any I/O error occur
WasmOutputStream stream = new WasmOutputStream(); */
stream.writeVaruint32( count ); private void writeFunctionSection() throws IOException {
for( Function func : functions.values() ) { int count = functions.size();
stream.writeVaruint32( func.typeId ); if( count > 0 ) {
} WasmOutputStream stream = new WasmOutputStream();
wasm.writeSection( SectionType.Function, stream, null ); stream.writeVaruint32( count );
} for( Function func : functions.values() ) {
} stream.writeVaruint32( func.typeId );
}
/** wasm.writeSection( SectionType.Function, stream, null );
* Write the code section to the output. This section contains the byte code. }
* }
* @throws IOException
* if any I/O error occur /**
*/ * Write the export section to the output. This section contains a mapping from the external index to the type signature index.
private void writeCodeSection() throws IOException { *
int size = functions.size(); * @throws IOException
if( size == 0 ) { * if any I/O error occur
return; */
} private void writeExportSection() throws IOException {
WasmOutputStream stream = new WasmOutputStream(); int count = exports.size();
stream.writeVaruint32( size ); if( count > 0 ) {
functionsStream.writeTo( stream ); WasmOutputStream stream = new WasmOutputStream();
wasm.writeSection( SectionType.Code, stream, null ); stream.writeVaruint32( count );
} for( Map.Entry<String,String> entry : exports.entrySet() ) {
String exportName = entry.getKey();
/** byte[] bytes = exportName.getBytes( StandardCharsets.UTF_8 );
* {@inheritDoc} stream.writeVaruint32( bytes.length );
*/ stream.write( bytes );
@Override stream.writeVaruint32( ExternalKind.Function.ordinal() );
protected void writeMethodStart( String name ) throws IOException { int id = functions.get( entry.getValue() ).id;
function = new Function(); stream.writeVaruint32( id );
function.id = functions.size(); }
functions.put( name, function ); wasm.writeSection( SectionType.Export, stream, null );
functionType = new FunctionType(); }
codeStream.reset(); }
}
/**
/** * Write the code section to the output. This section contains the byte code.
* {@inheritDoc} *
*/ * @throws IOException
@Override * if any I/O error occur
protected void writeMethodParam( String kind, ValueType valueType ) throws IOException { */
switch( kind ) { private void writeCodeSection() throws IOException {
case "param": int size = functions.size();
functionType.params.add( valueType ); if( size == 0 ) {
return; return;
case "return": }
functionType.result = valueType; WasmOutputStream stream = new WasmOutputStream();
return; stream.writeVaruint32( size );
} functionsStream.writeTo( stream );
} wasm.writeSection( SectionType.Code, stream, null );
}
/**
* {@inheritDoc} /**
*/ * {@inheritDoc}
@Override */
protected void writeMethodFinish( List<ValueType> locals ) throws IOException { @Override
// TODO optimize and search for duplicates protected void writeExport( String methodName, String exportName ) throws IOException {
function.typeId = functionTypes.size(); exports.put( exportName, methodName );
functionTypes.add( functionType ); }
/**
WasmOutputStream localsStream = new WasmOutputStream(); * {@inheritDoc}
localsStream.writeVaruint32( locals.size() ); */
for( ValueType valueType : locals ) { @Override
localsStream.writeVaruint32( 1 ); // TODO optimize, write the count of same types. protected void writeMethodStart( String name ) throws IOException {
localsStream.writeVarint32( valueType.getCode() ); function = new Function();
} function.id = functions.size();
functionsStream.writeVaruint32( localsStream.size() + codeStream.size() + 1 ); functions.put( name, function );
localsStream.writeTo( functionsStream ); functionType = new FunctionType();
codeStream.writeTo( functionsStream ); codeStream.reset();
functionsStream.write( END ); }
}
/**
/** * {@inheritDoc}
* {@inheritDoc} */
*/ @Override
@Override protected void writeMethodParam( String kind, ValueType valueType ) throws IOException {
protected void writeConstInt( int value ) throws IOException { switch( kind ) {
codeStream.write( I32_CONST ); case "param":
codeStream.writeVarint32( value ); functionType.params.add( valueType );
} return;
case "return":
/** functionType.result = valueType;
* {@inheritDoc} return;
*/ }
@Override }
protected void writeLoad( int idx ) throws IOException {
codeStream.write( GET_LOCAL ); /**
codeStream.writeVaruint32( idx ); * {@inheritDoc}
} */
@Override
/** protected void writeMethodFinish( List<ValueType> locals ) throws IOException {
* {@inheritDoc} // TODO optimize and search for duplicates
*/ function.typeId = functionTypes.size();
@Override functionTypes.add( functionType );
protected void writeStore( int idx ) throws IOException {
codeStream.write( SET_LOCAL );
codeStream.writeVaruint32( idx ); WasmOutputStream localsStream = new WasmOutputStream();
} localsStream.writeVaruint32( locals.size() );
for( ValueType valueType : locals ) {
/** localsStream.writeVaruint32( 1 ); // TODO optimize, write the count of same types.
* {@inheritDoc} localsStream.writeVarint32( valueType.getCode() );
*/ }
@Override functionsStream.writeVaruint32( localsStream.size() + codeStream.size() + 1 );
protected void writeAddInt() throws IOException { localsStream.writeTo( functionsStream );
codeStream.write( I32_ADD ); codeStream.writeTo( functionsStream );
} functionsStream.write( END );
}
/**
* {@inheritDoc} /**
*/ * {@inheritDoc}
@Override */
protected void writeReturn() throws IOException { @Override
codeStream.write( RETURN ); protected void writeConstInt( int value ) throws IOException {
} codeStream.write( I32_CONST );
} codeStream.writeVarint32( value );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeLoad( int idx ) throws IOException {
codeStream.write( GET_LOCAL );
codeStream.writeVaruint32( idx );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeStore( int idx ) throws IOException {
codeStream.write( SET_LOCAL );
codeStream.writeVaruint32( idx );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeAddInt() throws IOException {
codeStream.write( I32_ADD );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeReturn() throws IOException {
codeStream.write( RETURN );
}
}

View File

@ -1,300 +1,336 @@
/* /*
* Copyright 2017 Volker Berlin (i-net software) * Copyright 2017 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.inetsoftware.jwebassembly.module; package de.inetsoftware.jwebassembly.module;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull; import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import de.inetsoftware.classparser.ClassFile;
import de.inetsoftware.classparser.Code; import de.inetsoftware.classparser.Annotations;
import de.inetsoftware.classparser.CodeInputStream; import de.inetsoftware.classparser.ClassFile;
import de.inetsoftware.classparser.LineNumberTable; import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.MethodInfo; import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.classparser.LineNumberTable;
import de.inetsoftware.classparser.MethodInfo;
/** import de.inetsoftware.jwebassembly.WasmException;
* Module Writer for text format with S-expressions.
* /**
* @author Volker Berlin * Module Writer for text format with S-expressions.
*/ *
public abstract class ModuleWriter implements Closeable { * @author Volker Berlin
*/
private int paramCount; public abstract class ModuleWriter implements Closeable {
private ArrayList<ValueType> locals = new ArrayList<>(); private int paramCount;
/** private ArrayList<ValueType> locals = new ArrayList<>();
* Write the content of the class to the
* /**
* @param classFile * Write the content of the class to the
* the class file *
* @throws IOException * @param classFile
* if any I/O error occur * the class file
* @throws WasmException * @throws IOException
* if some Java code can't converted * if any I/O error occur
*/ * @throws WasmException
public void write( ClassFile classFile ) throws IOException, WasmException { * if some Java code can't converted
MethodInfo[] methods = classFile.getMethods(); */
for( MethodInfo method : methods ) { public void write( ClassFile classFile ) throws IOException, WasmException {
Code code = method.getCode(); MethodInfo[] methods = classFile.getMethods();
if( method.getName().equals( "<init>" ) && method.getDescription().equals( "()V" ) for( MethodInfo method : methods ) {
&& code.isSuperInitReturn( classFile.getSuperClass() ) ) { Code code = method.getCode();
continue; //default constructor if( method.getName().equals( "<init>" ) && method.getDescription().equals( "()V" )
} && code.isSuperInitReturn( classFile.getSuperClass() ) ) {
writeMethod( method ); continue; //default constructor
} }
} writeMethod( method );
}
/** }
* Write the content of a method.
* /**
* @param method * Write the content of a method.
* the method *
* @throws IOException * @param method
* if any I/O error occur * the method
* @throws WasmException * @throws IOException
* if some Java code can't converted * if any I/O error occur
*/ * @throws WasmException
private void writeMethod( MethodInfo method ) throws IOException, WasmException { * if some Java code can't converted
Code code = method.getCode(); */
if( code != null ) { // abstract methods and interface methods does not have code private void writeMethod( MethodInfo method ) throws IOException, WasmException {
writeMethodStart( method.getName() ); Code code = method.getCode();
writeMethodSignature( method ); if( code != null ) { // abstract methods and interface methods does not have code
locals.clear(); String methodName = method.getName(); // TODO naming conversion rule
LineNumberTable lineNumberTable = code.getLineNumberTable(); writeExport( methodName, method );
if( lineNumberTable != null ) { writeMethodStart( methodName );
int lineNumber; writeMethodSignature( method );
for( int i = 0; i < lineNumberTable.size(); i++ ) { locals.clear();
lineNumber = lineNumberTable.getLineNumber( i ); LineNumberTable lineNumberTable = code.getLineNumberTable();
int offset = lineNumberTable.getStartOffset( i ); if( lineNumberTable != null ) {
int nextOffset = int lineNumber;
i + 1 == lineNumberTable.size() ? code.getCodeSize() for( int i = 0; i < lineNumberTable.size(); i++ ) {
: lineNumberTable.getStartOffset( i + 1 ); lineNumber = lineNumberTable.getLineNumber( i );
CodeInputStream byteCode = code.getByteCode( offset, nextOffset - offset ); int offset = lineNumberTable.getStartOffset( i );
writeCodeChunk( byteCode, lineNumber ); int nextOffset =
} i + 1 == lineNumberTable.size() ? code.getCodeSize()
} else { : lineNumberTable.getStartOffset( i + 1 );
CodeInputStream byteCode = code.getByteCode(); CodeInputStream byteCode = code.getByteCode( offset, nextOffset - offset );
writeCodeChunk( byteCode, -1 ); writeCodeChunk( byteCode, lineNumber );
} }
for( int i = Math.min( paramCount, locals.size() ); i > 0; i-- ) { } else {
locals.remove( 0 ); CodeInputStream byteCode = code.getByteCode();
} writeCodeChunk( byteCode, -1 );
writeMethodFinish( locals ); }
} for( int i = Math.min( paramCount, locals.size() ); i > 0; i-- ) {
} locals.remove( 0 );
}
/** writeMethodFinish( locals );
* Write the method header. }
* }
* @param name
* the method name /**
* @throws IOException * Look for a Export annotation and if there write an export directive.
* if any I/O error occur * @param methodName
*/ * @param method
protected abstract void writeMethodStart( String name ) throws IOException; * @throws IOException
*/
/** private void writeExport( String methodName, MethodInfo method ) throws IOException {
* Write the parameter and return signatures Annotations annotations = method.getRuntimeInvisibleAnnotations();
* if( annotations != null ) {
* @param method Map<String,Object> export = annotations.get( "org.webassembly.annotation.Export" );
* the method if( export != null ) {
* @throws IOException String exportName = (String)export.get( "name" );
* if any I/O error occur if( exportName == null ) {
* @throws WasmException exportName = methodName;
* if some Java code can't converted }
*/ writeExport( methodName, exportName );
private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException { }
String signature = method.getDescription(); }
String kind = "param"; }
int paramCount = 0;
for( int i = 1; i < signature.length(); i++ ) { /**
paramCount++; * Write an export directive
String javaType; *
switch( signature.charAt( i ) ) { * @param methodName
case '[': // array * the method name
javaType = "array"; * @param exportName
break; * the export name, if null then the same like the method name
case 'L': * @throws IOException
javaType = "object"; * if any I/O error occur
break; */
case 'B': // byte protected abstract void writeExport( String methodName, String exportName ) throws IOException;
javaType = "byte";
break; /**
case 'C': // char * Write the method header.
javaType = "char"; *
break; * @param name
case 'D': // double * the method name
writeMethodParam( kind, ValueType.f64 ); * @throws IOException
continue; * if any I/O error occur
case 'F': // float */
writeMethodParam( kind, ValueType.f32 ); protected abstract void writeMethodStart( String name ) throws IOException;
continue;
case 'I': // int /**
writeMethodParam( kind, ValueType.i32 ); * Write the parameter and return signatures
continue; *
case 'J': // long * @param method
writeMethodParam( kind, ValueType.i64 ); * the method
continue; * @throws IOException
case 'V': // void * if any I/O error occur
continue; * @throws WasmException
case ')': * if some Java code can't converted
this.paramCount = paramCount - 1; */
kind = "return"; private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException {
continue; String signature = method.getDescription();
default: String kind = "param";
javaType = signature.substring( i, i + 1 ); int paramCount = 0;
} for( int i = 1; i < signature.length(); i++ ) {
int lineNumber = method.getCode().getFirstLineNr(); paramCount++;
throw new WasmException( "Not supported Java data type in method signature: " + javaType, lineNumber ); String javaType;
} switch( signature.charAt( i ) ) {
} case '[': // array
javaType = "array";
/** break;
* Write a method parameter. case 'L':
* javaType = "object";
* @param kind break;
* "param", "return" or "local" case 'B': // byte
* @param valueType javaType = "byte";
* the data type of the parameter break;
* @throws IOException case 'C': // char
* if any I/O error occur javaType = "char";
*/ break;
protected abstract void writeMethodParam( String kind, ValueType valueType ) throws IOException; case 'D': // double
writeMethodParam( kind, ValueType.f64 );
/** continue;
* Complete the method case 'F': // float
* writeMethodParam( kind, ValueType.f32 );
* @param locals continue;
* a list with types of local variables case 'I': // int
* writeMethodParam( kind, ValueType.i32 );
* @throws IOException continue;
* if any I/O error occur case 'J': // long
*/ writeMethodParam( kind, ValueType.i64 );
protected abstract void writeMethodFinish( List<ValueType> locals ) throws IOException; continue;
case 'V': // void
/** continue;
* Write a chunk of byte code. case ')':
* this.paramCount = paramCount - 1;
* @param byteCode kind = "return";
* a stream of byte code continue;
* @param lineNumber default:
* the current line number javaType = signature.substring( i, i + 1 );
* @throws WasmException }
* if some Java code can't converted int lineNumber = method.getCode().getFirstLineNr();
*/ throw new WasmException( "Not supported Java data type in method signature: " + javaType, lineNumber );
private void writeCodeChunk( CodeInputStream byteCode, int lineNumber ) throws WasmException { }
try { }
while( byteCode.available() > 0 ) {
int op = byteCode.readUnsignedByte(); /**
switch( op ) { * Write a method parameter.
case 4: // iconst_1 *
writeConstInt( 1 ); * @param kind
break; * "param", "return" or "local"
case 26: // iload_0 * @param valueType
case 27: // iload_1 * the data type of the parameter
case 28: // iload_2 * @throws IOException
case 29: // iload_3 * if any I/O error occur
writeLoadStore( true, ValueType.i32, op - 26 ); */
break; protected abstract void writeMethodParam( String kind, ValueType valueType ) throws IOException;
case 59: // istore_0
case 60: // istore_1 /**
case 61: // istore_2 * Complete the method
case 62: // istore_3 *
writeLoadStore( false, ValueType.i32, op - 59 ); * @param locals
break; * a list with types of local variables
case 96: // iadd *
writeAddInt(); * @throws IOException
break; * if any I/O error occur
case 177: // return void */
writeReturn(); protected abstract void writeMethodFinish( List<ValueType> locals ) throws IOException;
break;
default: /**
throw new WasmException( "Unimplemented byte code operation: " + op, lineNumber ); * Write a chunk of byte code.
} *
} * @param byteCode
} catch( Exception ex ) { * a stream of byte code
throw WasmException.create( ex, lineNumber ); * @param lineNumber
} * the current line number
} * @throws WasmException
* if some Java code can't converted
/** */
* Write a constant integer value private void writeCodeChunk( CodeInputStream byteCode, int lineNumber ) throws WasmException {
* try {
* @param value while( byteCode.available() > 0 ) {
* the value int op = byteCode.readUnsignedByte();
* @throws IOException switch( op ) {
* if any I/O error occur case 4: // iconst_1
*/ writeConstInt( 1 );
protected abstract void writeConstInt( int value ) throws IOException; break;
case 26: // iload_0
private void writeLoadStore( boolean load, @Nonnull ValueType valueType, @Nonnegative int idx ) throws WasmException, IOException { case 27: // iload_1
while( locals.size() <= idx ) { case 28: // iload_2
locals.add( null ); case 29: // iload_3
} writeLoadStore( true, ValueType.i32, op - 26 );
ValueType oldType = locals.get( idx ); break;
if( oldType != null && oldType != valueType ) { case 59: // istore_0
throw new WasmException( "Redefine local variable type from " + oldType + " to " + valueType, -1 ); case 60: // istore_1
} case 61: // istore_2
locals.set( idx, valueType ); case 62: // istore_3
if( load ) { writeLoadStore( false, ValueType.i32, op - 59 );
writeLoad( idx ); break;
} else { case 96: // iadd
writeStore( idx ); writeAddInt();
} break;
} case 177: // return void
writeReturn();
/** break;
* Write a variable load. default:
* throw new WasmException( "Unimplemented byte code operation: " + op, lineNumber );
* @param idx }
* the index of the parameter variable }
* @throws IOException } catch( Exception ex ) {
* if any I/O error occur throw WasmException.create( ex, lineNumber );
*/ }
protected abstract void writeLoad( int idx ) throws IOException; }
/** /**
* Write a variable store. * Write a constant integer value
* *
* @param idx * @param value
* the index of the parameter variable * the value
* @throws IOException * @throws IOException
* if any I/O error occur * if any I/O error occur
*/ */
protected abstract void writeStore( int idx ) throws IOException; protected abstract void writeConstInt( int value ) throws IOException;
/** private void writeLoadStore( boolean load, @Nonnull ValueType valueType, @Nonnegative int idx ) throws WasmException, IOException {
* Write a add operator while( locals.size() <= idx ) {
* locals.add( null );
* @throws IOException }
* if any I/O error occur ValueType oldType = locals.get( idx );
*/ if( oldType != null && oldType != valueType ) {
protected abstract void writeAddInt() throws IOException; throw new WasmException( "Redefine local variable type from " + oldType + " to " + valueType, -1 );
}
/** locals.set( idx, valueType );
* Write a return if( load ) {
* writeLoad( idx );
* @throws IOException } else {
* if any I/O error occur writeStore( idx );
*/ }
protected abstract void writeReturn() throws IOException; }
}
/**
* Write a variable load.
*
* @param idx
* the index of the parameter variable
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeLoad( int idx ) throws IOException;
/**
* Write a variable store.
*
* @param idx
* the index of the parameter variable
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeStore( int idx ) throws IOException;
/**
* Write a add operator
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeAddInt() throws IOException;
/**
* Write a return
*
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeReturn() throws IOException;
}

View File

@ -1,154 +1,163 @@
/* /*
* Copyright 2017 Volker Berlin (i-net software) * Copyright 2017 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.inetsoftware.jwebassembly.text; package de.inetsoftware.jwebassembly.text;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import de.inetsoftware.jwebassembly.module.ModuleWriter; import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.ValueType; import de.inetsoftware.jwebassembly.module.ValueType;
/** /**
* Module Writer for text format with S-expressions. * Module Writer for text format with S-expressions.
* *
* @author Volker Berlin * @author Volker Berlin
* *
*/ */
public class TextModuleWriter extends ModuleWriter { public class TextModuleWriter extends ModuleWriter {
private Appendable output; private Appendable output;
private StringBuilder methodOutput = new StringBuilder(); private StringBuilder methodOutput = new StringBuilder();
private int inset; private int inset;
/** /**
* Create a new instance. * Create a new instance.
* *
* @param output * @param output
* target for the result * target for the result
* @throws IOException * @throws IOException
* if any I/O error occur * if any I/O error occur
*/ */
public TextModuleWriter( Appendable output ) throws IOException { public TextModuleWriter( Appendable output ) throws IOException {
this.output = output; this.output = output;
output.append( "(module" ); output.append( "(module" );
inset++; inset++;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
inset--; inset--;
newline( output ); newline( output );
output.append( ')' ); output.append( ')' );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void writeMethodStart( String name ) throws IOException { protected void writeExport( String methodName, String exportName ) throws IOException {
newline( output ); newline( output );
output.append( "(func $" ); output.append( "(export \"" ).append( exportName ).append( "\" (func $" ).append( methodName ).append( "))" );
output.append( name ); }
inset++;
methodOutput.setLength( 0 ); /**
} * {@inheritDoc}
*/
/** @Override
* {@inheritDoc} protected void writeMethodStart( String name ) throws IOException {
*/ newline( output );
@Override output.append( "(func $" );
protected void writeMethodParam( String kind, ValueType valueType ) throws IOException { output.append( name );
output.append( " (" ).append( kind ).append( ' ' ).append( valueType.toString() ).append( ')' ); inset++;
} methodOutput.setLength( 0 );
}
/**
* {@inheritDoc} /**
*/ * {@inheritDoc}
@Override */
protected void writeMethodFinish( List<ValueType> locals ) throws IOException { @Override
for( ValueType valueType : locals ) { protected void writeMethodParam( String kind, ValueType valueType ) throws IOException {
output.append( " (local " ).append( valueType.toString() ).append( ')' ); output.append( " (" ).append( kind ).append( ' ' ).append( valueType.toString() ).append( ')' );
} }
output.append( methodOutput );
inset--; /**
newline( output ); * {@inheritDoc}
output.append( ')' ); */
} @Override
protected void writeMethodFinish( List<ValueType> locals ) throws IOException {
/** for( ValueType valueType : locals ) {
* {@inheritDoc} output.append( " (local " ).append( valueType.toString() ).append( ')' );
*/ }
@Override output.append( methodOutput );
protected void writeConstInt( int value ) throws IOException { inset--;
newline( methodOutput ); newline( output );
methodOutput.append( "i32.const " ).append( Integer.toString( value ) ); output.append( ')' );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void writeLoad( int idx ) throws IOException { protected void writeConstInt( int value ) throws IOException {
newline( methodOutput ); newline( methodOutput );
methodOutput.append( "get_local " ).append( Integer.toString( idx ) ); methodOutput.append( "i32.const " ).append( Integer.toString( value ) );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void writeStore( int idx ) throws IOException { protected void writeLoad( int idx ) throws IOException {
newline( methodOutput ); newline( methodOutput );
methodOutput.append( "set_local " ).append( Integer.toString( idx ) ); methodOutput.append( "get_local " ).append( Integer.toString( idx ) );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void writeAddInt() throws IOException { protected void writeStore( int idx ) throws IOException {
newline( methodOutput ); newline( methodOutput );
methodOutput.append( "i32.add" ); methodOutput.append( "set_local " ).append( Integer.toString( idx ) );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void writeReturn() throws IOException { protected void writeAddInt() throws IOException {
newline( methodOutput ); newline( methodOutput );
methodOutput.append( "return" ); methodOutput.append( "i32.add" );
} }
/** /**
* Add a newline the insets. * {@inheritDoc}
* */
* @throws IOException @Override
* if any I/O error occur protected void writeReturn() throws IOException {
*/ newline( methodOutput );
private void newline( Appendable output ) throws IOException { methodOutput.append( "return" );
output.append( '\n' ); }
for( int i = 0; i < inset; i++ ) {
output.append( ' ' ); /**
output.append( ' ' ); * Add a newline the insets.
} *
} * @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( ' ' );
}
}
}