diff --git a/build.gradle b/build.gradle index 989bb5c..6c32e1e 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,11 @@ buildscript { allprojects { apply plugin: 'java' group 'com.github.cretz.asmble' - version '0.4.0-fl' + version '0.4.1-fl' + + // skips building and running for the specified examples + ext.skipExamples = ['c-simple', 'go-simple', 'rust-regex'] + // todo disabling Rust regex is temporary because some strings in wasm code exceed the size in 65353 bytes. repositories { mavenCentral() @@ -67,6 +71,38 @@ project(':examples') { dependencies { compileOnly project(':compiler') } + + // C/C++ example helpers + + task cToWasm { + doFirst { + mkdir 'build' + exec { + def cFileName = fileTree(dir: 'src', includes: ['*.c']).files.iterator().next() + commandLine 'clang', '--target=wasm32-unknown-unknown-wasm', '-O3', cFileName, '-c', '-o', 'build/lib.wasm' + } + } + } + + task showCWast(type: JavaExec) { + dependsOn cToWasm + classpath configurations.compileClasspath + main = 'asmble.cli.MainKt' + doFirst { + args 'translate', 'build/lib.wasm' + } + } + + task compileCWasm(type: JavaExec) { + dependsOn cToWasm + classpath configurations.compileClasspath + main = 'asmble.cli.MainKt' + doFirst { + def outFile = 'build/wasm-classes/' + wasmCompiledClassName.replace('.', '/') + '.class' + file(outFile).parentFile.mkdirs() + args 'compile', 'build/lib.wasm', wasmCompiledClassName, '-out', outFile + } + } // Go example helpers @@ -142,8 +178,33 @@ project(':examples') { } } +project(':examples:c-simple') { + if (project.name in skipExamples) { + println("[Note!] Building and runnig for ${project.name} was skipped") + test.onlyIf { false } // explicit skipping tests + compileJava.onlyIf { false } // explicit skipping compile + return + } + + apply plugin: 'application' + ext.wasmCompiledClassName = 'asmble.generated.CSimple' + dependencies { + compile files('build/wasm-classes') + } + + compileJava { + dependsOn compileCWasm + } + mainClassName = 'asmble.examples.csimple.Main' +} project(':examples:go-simple') { + if (project.name in skipExamples) { + println("[Note!] Building and runnig for ${project.name} was skipped") + test.onlyIf { false } // explicit skipping tests + compileJava.onlyIf { false } // explicit skipping compile + return + } apply plugin: 'application' ext.wasmCompiledClassName = 'asmble.generated.GoSimple' dependencies { @@ -155,32 +216,44 @@ project(':examples:go-simple') { mainClassName = 'asmble.examples.gosimple.Main' } -// todo temporary disable Rust regex, because some strings in wasm code exceed the size in 65353 bytes. + project(':examples:rust-regex') { + if (project.name in skipExamples) { + println("[Note!] Building and runnig for ${project.name} was skipped") + test.onlyIf { false } // explicit skipping tests + compileJava.onlyIf { false } // explicit skipping compile + compileTestJava.onlyIf { false } // explicit skipping compile + return + } + apply plugin: 'application' + apply plugin: 'me.champeau.gradle.jmh' + ext.wasmCompiledClassName = 'asmble.generated.RustRegex' + dependencies { + compile files('build/wasm-classes') + testCompile 'junit:junit:4.12' + } + compileJava { + dependsOn compileRustWasm + } + mainClassName = 'asmble.examples.rustregex.Main' -// project(':examples:rust-regex') { -// apply plugin: 'application' -// apply plugin: 'me.champeau.gradle.jmh' -// ext.wasmCompiledClassName = 'asmble.generated.RustRegex' -// dependencies { -// compile files('build/wasm-classes') -// testCompile 'junit:junit:4.12' -// } -// compileJava { -// dependsOn compileRustWasm -// } -// mainClassName = 'asmble.examples.rustregex.Main' -// test { -// testLogging.showStandardStreams = true -// testLogging.events 'PASSED', 'SKIPPED' -// } -// jmh { -// iterations = 5 -// warmupIterations = 5 -// fork = 3 -// } -// } + test { + testLogging.showStandardStreams = true + testLogging.events 'PASSED', 'SKIPPED' + } + jmh { + iterations = 5 + warmupIterations = 5 + fork = 3 + } + } project(':examples:rust-simple') { + if (project.name in skipExamples) { + println("[Note!] Building and runnig for ${project.name} was skipped") + test.onlyIf { false } // explicit skipping tests + compileJava.onlyIf { false } // explicit skipping compile + return + } apply plugin: 'application' ext.wasmCompiledClassName = 'asmble.generated.RustSimple' dependencies { @@ -193,6 +266,12 @@ project(':examples:rust-simple') { } project(':examples:rust-string') { + if (project.name in skipExamples) { + println("[Note!] Building and runnig for ${project.name} was skipped") + test.onlyIf { false } // explicit skipping tests + compileJava.onlyIf { false } // explicit skipping compile + return + } apply plugin: 'application' ext.wasmCompiledClassName = 'asmble.generated.RustString' dependencies { diff --git a/compiler/src/main/kotlin/asmble/cli/ScriptCommand.kt b/compiler/src/main/kotlin/asmble/cli/ScriptCommand.kt index f30a9a5..22715e5 100644 --- a/compiler/src/main/kotlin/asmble/cli/ScriptCommand.kt +++ b/compiler/src/main/kotlin/asmble/cli/ScriptCommand.kt @@ -56,16 +56,20 @@ abstract class ScriptCommand : Command() { // if input file is class file "class" -> ctx.classLoader.addClass(File(inFile).readBytes()).let { ctx } // if input file is wasm file - else -> Translate.also { it.logger = logger }.inToAst(inFile, inFile.substringAfterLast('.')).let { inAst -> - val (mod, name) = (inAst.commands.singleOrNull() as? Script.Cmd.Module) ?: + else -> { + val translateCmd = Translate + translateCmd.logger = this.logger + translateCmd.inToAst(inFile, inFile.substringAfterLast('.')).let { inAst -> + val (mod, name) = (inAst.commands.singleOrNull() as? Script.Cmd.Module) ?: error("Input file must only contain a single module") - val className = name?.javaIdent?.capitalize() ?: + val className = name?.javaIdent?.capitalize() ?: "Temp" + UUID.randomUUID().toString().replace("-", "") - ctx.withCompiledModule(mod, className, name).let { ctx -> - if (name == null && index != args.inFiles.size - 1) - logger.warn { "File '$inFile' not last and has no name so will be unused" } - if (name == null || args.disableAutoRegister) ctx - else ctx.runCommand(Script.Cmd.Register(name, null)) + ctx.withCompiledModule(mod, className, name).let { ctx -> + if (name == null && index != args.inFiles.size - 1) + logger.warn { "File '$inFile' not last and has no name so will be unused" } + if (name == null || args.disableAutoRegister) ctx + else ctx.runCommand(Script.Cmd.Register(name, null)) + } } } } diff --git a/compiler/src/main/kotlin/asmble/cli/Translate.kt b/compiler/src/main/kotlin/asmble/cli/Translate.kt index 146dfe9..c133f4d 100644 --- a/compiler/src/main/kotlin/asmble/cli/Translate.kt +++ b/compiler/src/main/kotlin/asmble/cli/Translate.kt @@ -70,7 +70,7 @@ open class Translate : Command() { } } "wasm" -> - Script(listOf(Script.Cmd.Module(BinaryToAst(logger = logger).toModule( + Script(listOf(Script.Cmd.Module(BinaryToAst(logger = this.logger).toModule( ByteReader.InputStream(inBytes.inputStream())), null))) else -> error("Unknown in format '$inFormat'") } diff --git a/compiler/src/main/kotlin/asmble/io/BinaryToAst.kt b/compiler/src/main/kotlin/asmble/io/BinaryToAst.kt index 67db443..9c70c9d 100644 --- a/compiler/src/main/kotlin/asmble/io/BinaryToAst.kt +++ b/compiler/src/main/kotlin/asmble/io/BinaryToAst.kt @@ -2,12 +2,11 @@ package asmble.io import asmble.ast.Node import asmble.util.* -import java.io.ByteArrayInputStream import java.nio.ByteBuffer open class BinaryToAst( val version: Long = 1L, - val logger: Logger = Logger.Print(Logger.Level.OFF), + val logger: Logger = Logger.Print(Logger.Level.WARN), val includeNameSection: Boolean = true ) : Logger by logger { diff --git a/examples/c-simple/README.md b/examples/c-simple/README.md new file mode 100644 index 0000000..b1cd834 --- /dev/null +++ b/examples/c-simple/README.md @@ -0,0 +1,14 @@ +### Example: C Simple + +This shows a simple example of compiling C to WASM and then to the JVM. This is the C version of +[rust-simple](../rust-simple). + +In order to run the C or C++ examples, the latest LLVM binaries must be on the `PATH`, built with the experimental +WebAssembly target. This can be built by passing `-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly` to `cmake` when +building WebAssembly. Or it can be downloaded from a nightly build site +([this one](http://gsdview.appspot.com/wasm-llvm/builds/) was used for these examples at the time of writing). + +Everything else is basically the same as [rust-simple](../rust-simple) except with C code and using `clang`. To run +execute the following from the root `asmble` dir: + + gradlew --no-daemon :examples:c-simple:run \ No newline at end of file diff --git a/examples/c-simple/src/lib.c b/examples/c-simple/src/lib.c new file mode 100644 index 0000000..c45f40d --- /dev/null +++ b/examples/c-simple/src/lib.c @@ -0,0 +1,3 @@ +int addOne(int x) { + return x + 1; +} \ No newline at end of file diff --git a/examples/c-simple/src/main/java/asmble/examples/csimple/Main.java b/examples/c-simple/src/main/java/asmble/examples/csimple/Main.java new file mode 100644 index 0000000..a073fb3 --- /dev/null +++ b/examples/c-simple/src/main/java/asmble/examples/csimple/Main.java @@ -0,0 +1,13 @@ +package asmble.examples.csimple; + +import java.lang.invoke.MethodHandle; + +import asmble.generated.CSimple; + +class Main { + public static void main(String[] args) { + // Doesn't need memory or method table + CSimple simple = new CSimple(0, new MethodHandle[0]); + System.out.println("25 + 1 = " + simple.addOne(25)); + } +} \ No newline at end of file