generalize WasmRule for better GC testing

This commit is contained in:
Volker Berlin 2020-06-13 17:18:21 +02:00
parent b91c076344
commit 9469d6d334
3 changed files with 131 additions and 101 deletions

View File

@ -69,23 +69,9 @@ public class WasmRule extends TemporaryFolder {
private final JWebAssembly compiler; private final JWebAssembly compiler;
private File wasmFile; private Map<ScriptEngine, File> compiledFiles = new HashMap<>();
private File watFile; private Map<ScriptEngine, File> scriptFiles = new HashMap<>();
private File nodeScript;
private File spiderMonkeyScript;
private File spiderMonkeyScriptGC;
private File spiderMonkeyScriptWatGC;
private File nodeWatScript;
private File spiderMonkeyWatScript;
private File wat2WasmScript;
private boolean failed; private boolean failed;
@ -111,6 +97,14 @@ public class WasmRule extends TemporaryFolder {
URL url = clazz.getResource( '/' + clazz.getName().replace( '.', '/' ) + ".class" ); URL url = clazz.getResource( '/' + clazz.getName().replace( '.', '/' ) + ".class" );
compiler.addFile( url ); compiler.addFile( url );
} }
// add the libraries that it can be scanned for annotations
final String[] libraries = System.getProperty("java.class.path").split(File.pathSeparator);
for( String lib : libraries ) {
if( lib.endsWith( ".jar" ) || lib.toLowerCase().contains( "jwebassembly-api" ) ) {
compiler.addLibrary( new File(lib) );
}
}
} }
/** /**
@ -144,7 +138,7 @@ public class WasmRule extends TemporaryFolder {
*/ */
@Override @Override
protected void before() throws Throwable { protected void before() throws Throwable {
compile(); super.before();
if( testData != null ) { if( testData != null ) {
writeJsonTestData( testData ); writeJsonTestData( testData );
} }
@ -200,14 +194,16 @@ public class WasmRule extends TemporaryFolder {
@Override @Override
protected void after() { protected void after() {
if( failed ) { if( failed ) {
if( wasmFile != null ) { for( File wasmFile : compiledFiles.values() ) {
File jsFile = new File( wasmFile.toString() + ".js" ); if( wasmFile.getName().endsWith( ".wasm" ) ) {
if( jsFile.isFile() ) { File jsFile = new File( wasmFile.toString() + ".js" );
try { if( jsFile.isFile() ) {
System.out.println( new String( Files.readAllBytes( jsFile.toPath() ), StandardCharsets.UTF_8 ) ); try {
System.out.println(); System.out.println( new String( Files.readAllBytes( jsFile.toPath() ), StandardCharsets.UTF_8 ) );
} catch( IOException e ) { System.out.println();
e.printStackTrace(); } catch( IOException e ) {
e.printStackTrace();
}
} }
} }
} }
@ -223,45 +219,86 @@ public class WasmRule extends TemporaryFolder {
* if the compiling is failing * if the compiling is failing
*/ */
public void compile() throws WasmException { public void compile() throws WasmException {
compiler.setProperty( JWebAssembly.DEBUG_NAMES, "true" );
assertEquals( "true", compiler.getProperty( JWebAssembly.DEBUG_NAMES ) );
// add the libraries that it can be scanned for annotations
final String[] libraries = System.getProperty("java.class.path").split(File.pathSeparator);
for( String lib : libraries ) {
if( lib.endsWith( ".jar" ) || lib.toLowerCase().contains( "jwebassembly-api" ) ) {
compiler.addLibrary( new File(lib) );
}
}
textCompiled = compiler.compileToText();
try { try {
create(); create();
} catch( Throwable ex ) {
throwException( ex );
}
compile( ScriptEngine.NodeJS );
}
watFile = newFile( "test.wat" ); /**
try( FileOutputStream stream = new FileOutputStream( watFile ) ) { * Compile the classes of the script engine if not already compiled.
stream.write( textCompiled.getBytes( StandardCharsets.UTF_8 ) ); *
* @param script
* the script engine
* @return the compiled main file
* @throws WasmException
* if the compiling is failing
*/
public File compile( ScriptEngine script ) throws WasmException {
File file = compiledFiles.get( script );
if( file != null ) {
// compile only once
return file;
}
compiler.setProperty( JWebAssembly.DEBUG_NAMES, "true" );
assertEquals( "true", compiler.getProperty( JWebAssembly.DEBUG_NAMES ) );
compiler.setProperty( JWebAssembly.WASM_USE_GC, script.useGC );
if( textCompiled == null ) {
textCompiled = compiler.compileToText();
}
try {
String name = script.name();
if( name.contains( "Wat" ) ) {
file = newFile( name + ".wat" );
compiler.compileToText( file );
} else {
file = newFile( name + ".wasm" );
compiler.compileToBinary( file );
} }
compiledFiles.put( script, file );
wasmFile = newFile( "test.wasm" );
compiler.compileToBinary( wasmFile );
nodeScript = createScript( "nodetest.js", "{test.wasm}", wasmFile.getName() );
} catch( Throwable ex ) { } catch( Throwable ex ) {
System.out.println( textCompiled ); System.out.println( textCompiled );
throwException( ex ); throwException( ex );
} }
return file;
}
/**
* Prepare the node node script.
*
* @param script
* the script engine
* @return the script file
* @throws IOException
* if any error occur.
*/
private File prepareNodeJs( ScriptEngine script ) throws IOException {
File scriptFile = scriptFiles.get( script );
if( scriptFile == null ) {
compile( script );
scriptFile = createScript( "nodetest.js", "{test}", script.name() );
}
return scriptFile;
} }
/** /**
* Prepare the node wabt module. * Prepare the node wabt module.
* *
* @param script
* the script engine
* @return the script file
* @throws Exception * @throws Exception
* if any error occur. * if any error occur.
*/ */
private void prepareNodeWat() throws Exception { private File prepareNodeWat( ScriptEngine script ) throws Exception {
if( nodeWatScript == null ) { File scriptFile = scriptFiles.get( script );
nodeWatScript = createScript( "WatTest.js", "{test.wat}", watFile.getName() ); if( scriptFile == null ) {
compile( script );
scriptFile = createScript( "WatTest.js", "{test}", script.name() );
if( !npmWabtNightly ) { if( !npmWabtNightly ) {
npmWabtNightly = true; npmWabtNightly = true;
@ -273,6 +310,7 @@ public class WasmRule extends TemporaryFolder {
execute( processBuilder ); execute( processBuilder );
} }
} }
return scriptFile;
} }
/** /**
@ -290,7 +328,7 @@ public class WasmRule extends TemporaryFolder {
processBuilder.command().add( 1, "/C" ); processBuilder.command().add( 1, "/C" );
} }
Process process = processBuilder.start(); Process process = processBuilder.start();
int exitCode = process.waitFor(); process.waitFor();
nodeModulePath = readStream( process.getInputStream() ).trim(); // module install path nodeModulePath = readStream( process.getInputStream() ).trim(); // module install path
System.out.println( "node global module path: " + nodeModulePath ); System.out.println( "node global module path: " + nodeModulePath );
@ -301,11 +339,16 @@ public class WasmRule extends TemporaryFolder {
/** /**
* Prepare the Wat2Wasm tool if not already do. Fire an JUnit fail if the process produce an error. * Prepare the Wat2Wasm tool if not already do. Fire an JUnit fail if the process produce an error.
* *
* @param script
* the script engine
* @return the script file
* @throws Exception * @throws Exception
* if any error occur. * if any error occur.
*/ */
private void prepareWat2Wasm() throws Exception { private File prepareWat2Wasm( ScriptEngine script ) throws Exception {
if( wat2WasmScript == null ) { File scriptFile = scriptFiles.get( script );
if( scriptFile == null ) {
File watFile = compile( ScriptEngine.NodeJS );
String cmd = wat2Wasm.getCommand(); String cmd = wat2Wasm.getCommand();
File wat2WasmFile = new File( getRoot(), "wat2Wasm.wasm" ); File wat2WasmFile = new File( getRoot(), "wat2Wasm.wasm" );
// the wat2wasm tool // the wat2wasm tool
@ -314,8 +357,9 @@ public class WasmRule extends TemporaryFolder {
execute( processBuilder ); execute( processBuilder );
// create the node script // create the node script
wat2WasmScript = createScript( "nodetest.js", "{test.wasm}", wat2WasmFile.getName() ); scriptFile = createScript( "nodetest.js", "{test}", script.name() );
} }
return scriptFile;
} }
/** /**
@ -466,25 +510,23 @@ public class WasmRule extends TemporaryFolder {
compiler.setProperty( JWebAssembly.WASM_USE_GC, script.useGC ); compiler.setProperty( JWebAssembly.WASM_USE_GC, script.useGC );
switch( script ) { switch( script ) {
case SpiderMonkey: case SpiderMonkey:
return spiderMonkeyCommand( true, false ); return spiderMonkeyCommand( true, script );
case SpiderMonkeyWat: case SpiderMonkeyWat:
return spiderMonkeyCommand( false, false ); return spiderMonkeyCommand( false, script );
case SpiderMonkeyGC: case SpiderMonkeyGC:
return spiderMonkeyCommand( true, true ); return spiderMonkeyCommand( true, script );
case SpiderMonkeyWatGC: case SpiderMonkeyWatGC:
return spiderMonkeyCommand( false, true ); return spiderMonkeyCommand( false, script );
case NodeJS: case NodeJS:
case NodeJsGC: case NodeJsGC:
return nodeJsCommand( nodeScript ); return nodeJsCommand( prepareNodeJs( script ) );
case NodeWat: case NodeWat:
case NodeWatGC: case NodeWatGC:
prepareNodeWat(); ProcessBuilder processBuilder = nodeJsCommand( prepareNodeWat( script ) );
ProcessBuilder processBuilder = nodeJsCommand( nodeWatScript );
processBuilder.environment().put( "NODE_PATH", getNodeModulePath() ); processBuilder.environment().put( "NODE_PATH", getNodeModulePath() );
return processBuilder; return processBuilder;
case Wat2Wasm: case Wat2Wasm:
prepareWat2Wasm(); return nodeJsCommand( prepareWat2Wasm( script ) );
return nodeJsCommand( wat2WasmScript );
default: default:
throw new IllegalStateException( script.toString() ); throw new IllegalStateException( script.toString() );
} }
@ -541,6 +583,7 @@ public class WasmRule extends TemporaryFolder {
// read the result from file // read the result from file
try( InputStreamReader jsonData = new InputStreamReader( new FileInputStream( new File( getRoot(), "testresult.json" ) ), StandardCharsets.UTF_8 ) ) { try( InputStreamReader jsonData = new InputStreamReader( new FileInputStream( new File( getRoot(), "testresult.json" ) ), StandardCharsets.UTF_8 ) ) {
@SuppressWarnings( "unchecked" )
Map<String, String> map = new Gson().fromJson( jsonData, Map.class ); Map<String, String> map = new Gson().fromJson( jsonData, Map.class );
if( testData != null ) { if( testData != null ) {
testResults.put( script, map ); testResults.put( script, map );
@ -570,49 +613,35 @@ public class WasmRule extends TemporaryFolder {
/** /**
* Create a ProcessBuilder for spider monkey script shell. * Create a ProcessBuilder for spider monkey script shell.
* *
* @param binary true, if the WASM format should be test; false, if the WAT format should be tested. * @param binary
* @param gc true, if with gc should be test * true, if the WASM format should be test; false, if the WAT format should be tested.
* @param script
* the script engine
* @return the value from the script * @return the value from the script
* @throws IOException * @throws IOException
* if the download failed * if the download failed
*/ */
private ProcessBuilder spiderMonkeyCommand( boolean binary, boolean gc ) throws IOException { private ProcessBuilder spiderMonkeyCommand( boolean binary, ScriptEngine script ) throws IOException {
File script; boolean gc = Boolean.valueOf( script.useGC );
if( gc ) { File scriptFile = scriptFiles.get( script );
if( binary ) { if( scriptFile == null ) {
if( spiderMonkeyScriptGC == null ) { File file = compile( script );
File file = newFile( "spiderMonkeyGC.wasm" ); if( gc ) {
compiler.compileToBinary( file ); if( binary ) {
spiderMonkeyScriptGC = createScript( "SpiderMonkeyTest.js", "{test.wasm}", file.getName() ); scriptFile = createScript( "SpiderMonkeyTest.js", "{test.wasm}", file.getName() );
} else {
scriptFile = createScript( "SpiderMonkeyWatTest.js", "{test}", script.name() );
} }
script = spiderMonkeyScriptGC;
} else { } else {
if( spiderMonkeyScriptWatGC == null ) { if( binary ) {
File file = newFile( "spiderMonkeyGC.wat" ); scriptFile = createScript( "SpiderMonkeyTest.js", "{test.wasm}", file.getName() );
compiler.compileToText( file ); } else {
spiderMonkeyScriptWatGC = createScript( "SpiderMonkeyWatTest.js", "{test}", "spiderMonkeyGC" ); scriptFile = createScript( "SpiderMonkeyWatTest.js", "{test}", script.name() );
} }
script = spiderMonkeyScriptWatGC;
}
} else {
if( binary ) {
if( spiderMonkeyScript == null ) {
File file = newFile( "spiderMonkey.wasm" );
compiler.compileToBinary( file );
spiderMonkeyScript = createScript( "SpiderMonkeyTest.js", "{test.wasm}", file.getName() );
}
script = spiderMonkeyScript;
} else {
if( spiderMonkeyWatScript == null ) {
File file = newFile( "spiderMonkey.wat" );
compiler.compileToText( file );
spiderMonkeyWatScript = createScript( "SpiderMonkeyWatTest.js", "{test}", "spiderMonkey" );
}
script = spiderMonkeyWatScript;
} }
} }
ProcessBuilder process = new ProcessBuilder( spiderMonkey.getCommand(), script.getAbsolutePath() ); ProcessBuilder process = new ProcessBuilder( spiderMonkey.getCommand(), scriptFile.getAbsolutePath() );
if( gc ) { if( gc ) {
process.command().add( 1, "--wasm-gc" ); process.command().add( 1, "--wasm-gc" );
} }
@ -644,13 +673,13 @@ public class WasmRule extends TemporaryFolder {
/** /**
* Create a ProcessBuilder for node.js * Create a ProcessBuilder for node.js
* *
* @param script * @param nodeScript
* the path to the script that should be executed * the path to the script that should be executed
* @return the value from the script * @return the value from the script
* @throws IOException * @throws IOException
* if any I/O error occur * if any I/O error occur
*/ */
private static ProcessBuilder nodeJsCommand( File script ) throws IOException { private static ProcessBuilder nodeJsCommand( File nodeScript ) throws IOException {
String command = nodeExecuable(); String command = nodeExecuable();
// details see with command: node --v8-options // details see with command: node --v8-options
ProcessBuilder processBuilder = new ProcessBuilder( command, // ProcessBuilder processBuilder = new ProcessBuilder( command, //
@ -660,7 +689,7 @@ public class WasmRule extends TemporaryFolder {
"--experimental-wasm-gc", // "--experimental-wasm-gc", //
"--experimental-wasm-bigint", // "--experimental-wasm-bigint", //
"--experimental-wasm-bulk-memory", // bulk memory for WABT version 1.0.13, https://github.com/WebAssembly/wabt/issues/1311 "--experimental-wasm-bulk-memory", // bulk memory for WABT version 1.0.13, https://github.com/WebAssembly/wabt/issues/1311
script.getName() ); nodeScript.getName() );
if( IS_WINDOWS ) { if( IS_WINDOWS ) {
processBuilder.command().add( 0, "cmd" ); processBuilder.command().add( 0, "cmd" );
processBuilder.command().add( 1, "/C" ); processBuilder.command().add( 1, "/C" );
@ -696,6 +725,7 @@ public class WasmRule extends TemporaryFolder {
* @throws T * @throws T
* a generic helper * a generic helper
*/ */
@SuppressWarnings( "unchecked" )
public static <T extends Throwable> void throwException( Throwable exception ) throws T { public static <T extends Throwable> void throwException( Throwable exception ) throws T {
throw (T)exception; throw (T)exception;
} }

View File

@ -3,8 +3,8 @@
var fs = require('fs'); var fs = require('fs');
var wabt = require("wabt")(); var wabt = require("wabt")();
const wasmImports = require( "./test.wasm.js" ); const wasmImports = require( "./{test}.wasm.js" );
var filename = '{test.wat}'; var filename = '{test}.wat';
var text = fs.readFileSync(filename, "utf8"); var text = fs.readFileSync(filename, "utf8");
var testData = JSON.parse( fs.readFileSync( "testdata.json", "utf8" ) ); var testData = JSON.parse( fs.readFileSync( "testdata.json", "utf8" ) );

View File

@ -2,8 +2,8 @@
var fs = require('fs'); var fs = require('fs');
const wasmImports = require( "./test.wasm.js" ); const wasmImports = require( "./{test}.wasm.js" );
var filename = '{test.wasm}'; var filename = '{test}.wasm';
var wasm = fs.readFileSync(filename); var wasm = fs.readFileSync(filename);
var testData = JSON.parse( fs.readFileSync( "testdata.json", "utf8" ) ); var testData = JSON.parse( fs.readFileSync( "testdata.json", "utf8" ) );