From 5133890a6b94fec7f2c52ac0a6e1bc8d8f00b48d Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Tue, 2 Jul 2019 18:40:11 +0200 Subject: [PATCH] Add the JavaScriptWriter and some tests. --- .../javascript/JavaScriptWriter.java | 134 ++++++++++++++++++ .../javascript/JavaScriptWriterTest.java | 79 +++++++++++ 2 files changed, 213 insertions(+) create mode 100644 src/de/inetsoftware/jwebassembly/javascript/JavaScriptWriter.java create mode 100644 test/de/inetsoftware/jwebassembly/javascript/JavaScriptWriterTest.java diff --git a/src/de/inetsoftware/jwebassembly/javascript/JavaScriptWriter.java b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptWriter.java new file mode 100644 index 0000000..04021cf --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptWriter.java @@ -0,0 +1,134 @@ +/* + Copyright 2019 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.javascript; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import de.inetsoftware.jwebassembly.module.WasmTarget; + +/** + * Write JavaScipt glue code. + * + * @author Volker Berlin + */ +public class JavaScriptWriter { + + private WasmTarget target; + + /** annotation attribute for the JavaScript content */ + static final String JAVA_SCRIPT_CONTENT = "js"; + + private Map> modules = new HashMap<>(); + + /** + * Create a new instance + * + * @param target + * the target for the module data. + */ + public JavaScriptWriter( WasmTarget target ) { + this.target = target; + } + + /** + * Add an import from a needed function with import annotation. + * + * @param module + * the module name + * @param name + * the function name + * @param annotationValues + * the other values of the annotation + */ + public void addImport( String module, String name, Map annotationValues ) { + String content = (String)annotationValues.get( JAVA_SCRIPT_CONTENT ); + Map moduleEntries = modules.get( module ); + if( moduleEntries == null ) { + modules.put( module, moduleEntries = new HashMap<>() ); + } + + String old = moduleEntries.put( name, content ); + if( old != null ) { + System.err.println( "Redefine JavaScript function: " + module + "." + name ); + } + } + + /** + * Finish the accumulate of imports and write the JavaScript file. + * + * @throws IOException + * if any I/O error occur + */ + public void finish() throws IOException { + Appendable out = target.getJavaScriptOutput(); + if( out == null ) { + return; + } + finish( out ); + } + + /** + * Finish the accumulate of imports and write the JavaScript to the Appendable. + * + * @param out + * the target for the script + * @throws IOException + * if any I/O error occur + */ + void finish( Appendable out ) throws IOException { + out.append( "var wasmImports = {\n" ); + boolean isFirst = true; + for( Entry> module : modules.entrySet() ) { + if( !isFirst ) { + out.append( ",\n" ); + } + out.append( module.getKey() ).append( ':' ); + Map functions = module.getValue(); + + boolean hasContent = false; + for( String content : functions.values() ) { + if( content != null && !content.isEmpty() ) { + hasContent = true; + break; + } + } + + if( hasContent ) { + out.append( "{\n" ); + + // write the function + boolean isFirstFunc = true; + for( Entry func : functions.entrySet() ) { + if( !isFirstFunc ) { + out.append( ",\n" ); + } + out.append( func.getKey() ).append( ':' ).append( func.getValue() ); + isFirstFunc = false; + } + + out.append( "\n}" ); + } else { + out.append( module.getKey() ); + } + isFirst = false; + } + out.append( "\n}\n" ); + } +} diff --git a/test/de/inetsoftware/jwebassembly/javascript/JavaScriptWriterTest.java b/test/de/inetsoftware/jwebassembly/javascript/JavaScriptWriterTest.java new file mode 100644 index 0000000..a143495 --- /dev/null +++ b/test/de/inetsoftware/jwebassembly/javascript/JavaScriptWriterTest.java @@ -0,0 +1,79 @@ +package de.inetsoftware.jwebassembly.javascript; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.Collections; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import de.inetsoftware.jwebassembly.module.WasmTarget; + +public class JavaScriptWriterTest { + + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void single() throws IOException { + JavaScriptWriter writer = new JavaScriptWriter( new WasmTarget( temp.newFile() ) ); + writer.addImport( "Foo", "bar", Collections.singletonMap( JavaScriptWriter.JAVA_SCRIPT_CONTENT, "1 + 1" ) ); + StringBuilder builder = new StringBuilder(); + writer.finish( builder ); + assertEquals( "var wasmImports = {\n" + + "Foo:{\n" + + "bar:1 + 1\n" + + "}\n" + + "}\n" + + "", builder.toString() ); + } + + @Test + public void twoFunctions() throws IOException { + JavaScriptWriter writer = new JavaScriptWriter( new WasmTarget( temp.newFile() ) ); + writer.addImport( "Foo", "bar", Collections.singletonMap( JavaScriptWriter.JAVA_SCRIPT_CONTENT, "1 + 1" ) ); + writer.addImport( "Foo", "xyz", Collections.singletonMap( JavaScriptWriter.JAVA_SCRIPT_CONTENT, "3" ) ); + StringBuilder builder = new StringBuilder(); + writer.finish( builder ); + assertEquals( "var wasmImports = {\n" + + "Foo:{\n" + + "bar:1 + 1,\n" + + "xyz:3\n" + + "}\n" + + "}\n" + + "", builder.toString() ); + } + + @Test + public void twoModules() throws IOException { + JavaScriptWriter writer = new JavaScriptWriter( new WasmTarget( temp.newFile() ) ); + writer.addImport( "Foo", "foo", Collections.singletonMap( JavaScriptWriter.JAVA_SCRIPT_CONTENT, "1 + 1" ) ); + writer.addImport( "Bar", "bar", Collections.singletonMap( JavaScriptWriter.JAVA_SCRIPT_CONTENT, "3" ) ); + StringBuilder builder = new StringBuilder(); + writer.finish( builder ); + assertEquals( "var wasmImports = {\n" + + "Bar:{\n" + + "bar:3\n" + + "},\n" + + "Foo:{\n" + + "foo:1 + 1\n" + + "}\n" + + "}\n" + + "", builder.toString() ); + } + + @Test + public void rootModule() throws IOException { + JavaScriptWriter writer = new JavaScriptWriter( new WasmTarget( temp.newFile() ) ); + writer.addImport( "Foo", "foo", Collections.emptyMap() ); + writer.addImport( "Foo", "bar", Collections.emptyMap() ); + StringBuilder builder = new StringBuilder(); + writer.finish( builder ); + assertEquals( "var wasmImports = {\n" + + "Foo:Foo\n" + + "}\n" + + "", builder.toString() ); + } +}