diff --git a/src/de/inetsoftware/jwebassembly/JWebAssembly.java b/src/de/inetsoftware/jwebassembly/JWebAssembly.java index fcd260b..6de8803 100644 --- a/src/de/inetsoftware/jwebassembly/JWebAssembly.java +++ b/src/de/inetsoftware/jwebassembly/JWebAssembly.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Volker Berlin (i-net software) + * Copyright 2017 - 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. @@ -161,6 +161,7 @@ public class JWebAssembly { ClassFile classFile = new ClassFile( new BufferedInputStream( url.openStream() ) ); writer.prepare( classFile ); } + writer.prepareFinish(); for( URL url : classFiles ) { ClassFile classFile = new ClassFile( new BufferedInputStream( url.openStream() ) ); writer.write( classFile ); diff --git a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java index 7a37d3f..3d35ad4 100644 --- a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java @@ -42,25 +42,27 @@ import de.inetsoftware.jwebassembly.module.ValueTypeConvertion; */ 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 functionTypes = new ArrayList<>(); + private List functionTypes = new ArrayList<>(); - private Map functions = new LinkedHashMap<>(); + private Map functions = new LinkedHashMap<>(); - private Map exports = new LinkedHashMap<>(); + private Map exports = new LinkedHashMap<>(); - private Function function; + private Map imports = new LinkedHashMap<>(); - private FunctionType functionType; + private Function function; + + private FunctionType functionType; /** * Create new instance. @@ -83,6 +85,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod wasm.writeInt32( WASM_BINARY_VERSION ); writeTypeSection(); + writeImportSection(); writeFunctionSection(); writeExportSection(); writeCodeSection(); @@ -118,6 +121,32 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } } + /** + * Write the import section to the output. This section declare all imports. + * + * @throws IOException + * if any I/O error occur + */ + private void writeImportSection() throws IOException { + int count = imports.size(); + if( count > 0 ) { + WasmOutputStream stream = new WasmOutputStream(); + stream.writeVaruint32( count ); + for( ImportEntry entry : imports.values() ) { + byte[] bytes = entry.module.getBytes( StandardCharsets.UTF_8 ); + stream.writeVaruint32( bytes.length ); + stream.write( bytes ); + bytes = entry.name.getBytes( StandardCharsets.UTF_8 ); + stream.writeVaruint32( bytes.length ); + stream.write( bytes ); + stream.writeVaruint32( ExternalKind.Function.ordinal() ); + int typeIdx = 0; //TODO + stream.writeVaruint32( typeIdx ); + } + wasm.writeSection( SectionType.Import, stream, null ); + } + } + /** * Write the function section to the output. This section contains a mapping from the function index to the type signature index. * @@ -180,14 +209,29 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod /** * {@inheritDoc} */ - protected void prepareMethod( MethodInfo method ) throws WasmException { - String methodName = method.getName(); - String className = method.getDeclaringClassFile().getThisClass().getName(); - String fullName = className + '.' + methodName; - String signatureName = fullName + method.getDescription(); - Function function = new Function(); - function.id = functions.size(); - functions.put( signatureName, function ); + @Override + protected void prepareFunction( FunctionName name, String importModule, String importName ) { + if( importName != null ) { + imports.put( name.signatureName, new ImportEntry(importModule, importName) ); + } else { + functions.put( name.signatureName, new Function() ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void prepareFinish() { + // initialize the function index IDs + // https://github.com/WebAssembly/design/blob/master/Modules.md#function-index-space + int id = 0; + for( ImportEntry entry : imports.values() ) { + entry.id = id++; + } + for( Function function : functions.values() ) { + function.id = id++; + } } /** diff --git a/src/de/inetsoftware/jwebassembly/binary/ImportEntry.java b/src/de/inetsoftware/jwebassembly/binary/ImportEntry.java new file mode 100644 index 0000000..d172081 --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/binary/ImportEntry.java @@ -0,0 +1,35 @@ +/* + * 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.binary; + +/** + * An entry in the import section of the WebAssembly. + * + * @author Volker Berlin + */ +class ImportEntry { + + final String module; + + final String name; + + int id; + + public ImportEntry( String module, String name ) { + this.module = module; + this.name = name; + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java index 6c086fe..988d348 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java @@ -26,7 +26,6 @@ import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import de.inetsoftware.classparser.Annotations; import de.inetsoftware.classparser.ClassFile; import de.inetsoftware.classparser.Code; import de.inetsoftware.classparser.CodeInputStream; @@ -66,6 +65,14 @@ public abstract class ModuleWriter implements Closeable { iterateMethods( classFile, m -> prepareMethod( m ) ); } + /** + * Finish the prepare after all classes/methods are prepare. This must be call before we can start with write the + * first method. + */ + public void prepareFinish() { + + } + /** * Write the content of the class to the writer. * @@ -107,10 +114,35 @@ public abstract class ModuleWriter implements Closeable { * @throws WasmException * if some Java code can't converted */ - protected void prepareMethod( MethodInfo method ) throws WasmException { - // Nothing + private void prepareMethod( MethodInfo method ) throws WasmException { + try { + String module = null; + String name = null; + Map annotationValues = method.getAnnotation( "org.webassembly.annotation.Import" ); + if( annotationValues != null ) { + module = (String)annotationValues.get( "module" ); + name = (String)annotationValues.get( "name" ); + } + prepareFunction( new FunctionName( method ), module, name ); + } catch( IOException ioex ) { + throw WasmException.create( ioex, sourceFile, -1 ); + } } + /** + * Prepare a single function in the prepare phase. + * + * @param name + * the function name + * @param importModule + * the import module name if it is a import function + * @param importName + * the import name if it is a import function + * @throws IOException + * if any I/O error occur + */ + protected abstract void prepareFunction( FunctionName name, String importModule, String importName ) throws IOException; + /** * Write the content of a method. * @@ -123,7 +155,7 @@ public abstract class ModuleWriter implements Closeable { int lineNumber = -1; try { Code code = method.getCode(); - if( code != null ) { // abstract methods and interface methods does not have code + if( code != null && method.getAnnotation( "org.webassembly.annotation.Import" ) == null ) { // abstract methods and interface methods does not have code FunctionName name = new FunctionName( method ); writeExport( name, method ); writeMethodStart( name ); @@ -185,16 +217,13 @@ public abstract class ModuleWriter implements Closeable { * if any IOException occur */ private void writeExport( FunctionName name, MethodInfo method ) throws IOException { - Annotations annotations = method.getRuntimeInvisibleAnnotations(); - if( annotations != null ) { - Map export = annotations.get( "org.webassembly.annotation.Export" ); - if( export != null ) { - String exportName = (String)export.get( "name" ); - if( exportName == null ) { - exportName = method.getName(); // TODO naming conversion rule if no name was set - } - writeExport( name, exportName ); + Map export = method.getAnnotation( "org.webassembly.annotation.Export" ); + if( export != null ) { + String exportName = (String)export.get( "name" ); + if( exportName == null ) { + exportName = method.getName(); // TODO naming conversion rule if no name was set } + writeExport( name, exportName ); } } diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index 9a03e88..7618ecc 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -66,6 +66,17 @@ public class TextModuleWriter extends ModuleWriter { output.append( ')' ); } + /** + * {@inheritDoc} + */ + @Override + protected void prepareFunction( FunctionName name, String importModule, String importName ) throws IOException { + if( importName != null ) { + newline( output ); + output.append( "(import \"" ).append( importModule ).append( "\" \"" ).append( importName ).append( "\" (func $" ).append( name.fullName ).append( "))" ); + } + } + /** * {@inheritDoc} */