Skip to content

Commit 9661295

Browse files
committed
Add emulator for JWebAssembly
1 parent 189a148 commit 9661295

8 files changed

Lines changed: 666 additions & 0 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2020 Volker Berlin (i-net software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.inetsoftware.jwebassembly.emulator;
17+
18+
/**
19+
* The values of a scanned import annotation.
20+
*
21+
* @author Volker Berlin
22+
*/
23+
class ImportAnnotation {
24+
25+
String module;
26+
27+
String name;
28+
29+
String javaScript;
30+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2020 Volker Berlin (i-net software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.inetsoftware.jwebassembly.emulator;
17+
18+
import static org.objectweb.asm.Opcodes.ACC_NATIVE;
19+
import static org.objectweb.asm.Opcodes.ASM7;
20+
21+
import java.util.Map;
22+
23+
import org.objectweb.asm.ClassVisitor;
24+
import org.objectweb.asm.MethodVisitor;
25+
26+
/**
27+
* Scan a class for native methods with Import annotations.
28+
*
29+
* @author Volker Berlin
30+
*/
31+
class ImportAnnotationClassVisitor extends ClassVisitor {
32+
33+
private String className;
34+
35+
private Map<String, ImportAnnotation> annotations;
36+
37+
/**
38+
* Create an instance.
39+
*
40+
* @param className
41+
* the name of the class in the internal form of fully qualified class. For example, "java/util/List".
42+
* @param annotations
43+
* container for found annotations, key is method name with signature
44+
*/
45+
ImportAnnotationClassVisitor( String className, Map<String, ImportAnnotation> annotations ) {
46+
super( ASM7 );
47+
this.className = className;
48+
this.annotations = annotations;
49+
}
50+
51+
/**
52+
* {@inheritDoc}
53+
*/
54+
@Override
55+
public MethodVisitor visitMethod( int access, String name, String descriptor, String signature, String[] exceptions ) {
56+
if( (access & ACC_NATIVE) > 0 ) {
57+
// start scanning of annotations if it is a native method
58+
return new ImportAnnotationMethodVisitor( className, name, descriptor, annotations );
59+
}
60+
61+
// skip all other methods
62+
return null;
63+
}
64+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2020 Volker Berlin (i-net software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.inetsoftware.jwebassembly.emulator;
17+
18+
import java.util.Map;
19+
20+
import org.objectweb.asm.AnnotationVisitor;
21+
import org.objectweb.asm.MethodVisitor;
22+
import org.objectweb.asm.Opcodes;
23+
import org.objectweb.asm.Type;
24+
25+
import de.inetsoftware.jwebassembly.api.annotation.Import;
26+
27+
/**
28+
* Search for methods with the Import annotation of JWebAssembly.
29+
*
30+
* @author Volker Berlin
31+
*/
32+
class ImportAnnotationMethodVisitor extends MethodVisitor {
33+
34+
private static final String IMPORT_ANN = Type.getDescriptor( Import.class );
35+
36+
private String className;
37+
38+
private String methodName;
39+
40+
private String descriptor;
41+
42+
private Map<String, ImportAnnotation> annotations;
43+
44+
/**
45+
* Create an instance.
46+
*
47+
* @param className
48+
* the name of the class in the internal form of fully qualified class. For example, "java/util/List".
49+
* @param methodName
50+
* method name
51+
* @param descriptor
52+
* the method's descriptor (see {@link Type}).
53+
* @param annotations
54+
* container for found annotations, key is method name with signature
55+
*/
56+
ImportAnnotationMethodVisitor( String className, String methodName, String descriptor, Map<String, ImportAnnotation> annotations ) {
57+
super( Opcodes.ASM7 );
58+
this.className = className;
59+
this.methodName = methodName;
60+
this.descriptor = descriptor;
61+
this.annotations = annotations;
62+
}
63+
64+
/**
65+
* {@inheritDoc}
66+
*/
67+
@Override
68+
public AnnotationVisitor visitAnnotation( String descriptor, boolean visible ) {
69+
if( IMPORT_ANN.equals( descriptor ) ) {
70+
return new ImportAnnotationVisitor( className, methodName, this.descriptor, annotations );
71+
}
72+
return null;
73+
}
74+
75+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2020 Volker Berlin (i-net software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.inetsoftware.jwebassembly.emulator;
17+
18+
import static org.objectweb.asm.Opcodes.ASM7;
19+
20+
import java.util.Map;
21+
22+
import org.objectweb.asm.AnnotationVisitor;
23+
import org.objectweb.asm.Type;
24+
25+
/**
26+
* Scan the values of an Import annotation
27+
*
28+
* @author Volker Berlin
29+
*/
30+
class ImportAnnotationVisitor extends AnnotationVisitor {
31+
32+
private ImportAnnotation anno;
33+
34+
/**
35+
* Create an instance and add an ImportAnnotation to the container.
36+
*
37+
* @param className
38+
* the name of the class in the internal form of fully qualified class. For example, "java/util/List".
39+
* @param methodName
40+
* method name
41+
* @param descriptor
42+
* the method's descriptor (see {@link Type}).
43+
* @param annotations
44+
* container for found annotations, key is method name with signature
45+
*/
46+
public ImportAnnotationVisitor( String className, String methodName, String descriptor, Map<String, ImportAnnotation> annotations ) {
47+
super( ASM7 );
48+
annotations.put( methodName + descriptor, anno = new ImportAnnotation() );
49+
// default values for module and name
50+
anno.module = className.substring( className.lastIndexOf( '/' ) + 1 );
51+
anno.name = methodName;
52+
}
53+
54+
/**
55+
* {@inheritDoc}
56+
*/
57+
@Override
58+
public void visit( String name, Object value ) {
59+
switch( name ) {
60+
case "module":
61+
anno.module = (String)value;
62+
break;
63+
case "name":
64+
anno.name = (String)value;
65+
break;
66+
case "js":
67+
anno.javaScript = (String)value;
68+
break;
69+
}
70+
}
71+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2020 Volker Berlin (i-net software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.inetsoftware.jwebassembly.emulator;
17+
18+
import java.net.URL;
19+
20+
import javax.annotation.Nonnull;
21+
22+
import javafx.application.Application;
23+
import javafx.concurrent.Worker;
24+
import javafx.scene.Scene;
25+
import javafx.scene.layout.VBox;
26+
import javafx.scene.web.WebEngine;
27+
import javafx.scene.web.WebView;
28+
import javafx.stage.Stage;
29+
import netscape.javascript.JSException;
30+
import netscape.javascript.JSObject;
31+
32+
/**
33+
* The main class start point for the emulator.
34+
*
35+
* @author Volker Berlin
36+
*/
37+
@SuppressWarnings( "restriction" )
38+
public class JWebAssemblyEmulator {
39+
40+
static {
41+
// move the JavaScript console to System.out
42+
com.sun.javafx.webkit.WebConsoleListener.setDefaultListener( ( webView, message, lineNumber, sourceId ) -> {
43+
System.out.println( message + "[at " + lineNumber + "]" );
44+
} );
45+
}
46+
47+
/**
48+
* Start the emulator, load the html page and call the given main method.
49+
*
50+
* @param htmlPage
51+
* The resource of the html page that the WebAssembly contains.
52+
* @param main
53+
* the executable with the main function
54+
*/
55+
public static void launch( @Nonnull String htmlPage, @Nonnull Runnable main ) {
56+
launch( ClassLoader.getSystemResource( htmlPage ), main );
57+
}
58+
59+
/**
60+
* Start the emulator, load the html page and call the given main method.
61+
*
62+
* @param htmlPageURL
63+
* The URL of the html page that the WebAssembly contains.
64+
* @param main
65+
* the executable with the main function
66+
*/
67+
public static void launch( @Nonnull URL htmlPageURL, @Nonnull Runnable main ) {
68+
JavaFxApplication.url = htmlPageURL.toString();
69+
JavaFxApplication.main = main;
70+
71+
JavaFxApplication.launch( JavaFxApplication.class, new String[0] );
72+
}
73+
74+
/**
75+
* The implementation of the javafx Application.
76+
*
77+
* @author Volker Berlin
78+
*/
79+
public static class JavaFxApplication extends Application {
80+
81+
private static String url;
82+
83+
private static Runnable main;
84+
85+
private static WebEngine webEngine;
86+
87+
private static JSObject wasmImports;
88+
89+
/**
90+
* register a JavaScript function from a nation method with annotation in the wasmimports
91+
*
92+
* @param anno
93+
* the annotation
94+
*/
95+
static void registerScript( @Nonnull ImportAnnotation anno ) {
96+
Object obj = wasmImports.getMember( anno.module );
97+
if( "undefined".equals( obj ) ) {
98+
webEngine.executeScript( "wasmImports." + anno.module + " = {}" );
99+
}
100+
101+
webEngine.executeScript( "wasmImports." + anno.module + "." + anno.name + "=" + anno.javaScript );
102+
}
103+
104+
/**
105+
* The bridge method for the WebAssembly import function into the JavaScript.
106+
*
107+
* @param moduleName
108+
* the name of the module
109+
* @param methodName
110+
* the name of the function
111+
* @param args
112+
* the arguments
113+
* @return the return value if any
114+
*/
115+
public static Object executeScript( String moduleName, String methodName, Object... args ) {
116+
JSObject module = (JSObject)wasmImports.getMember( moduleName );
117+
118+
return module.call( methodName, args );
119+
}
120+
121+
/**
122+
* {@inheritDoc}
123+
*/
124+
@Override
125+
public void start( Stage primaryStage ) throws Exception {
126+
// Create a WebView
127+
WebView browser = new WebView();
128+
129+
// Get WebEngine via WebView
130+
webEngine = browser.getEngine();
131+
// https://stackoverflow.com/questions/41654573/java-fx-javascript
132+
Worker<Void> worker = webEngine.getLoadWorker();
133+
worker.stateProperty().addListener( ( obs, old, neww ) -> {
134+
if( neww == Worker.State.SUCCEEDED ) {
135+
try {
136+
wasmImports = (JSObject)webEngine.executeScript( "wasmImports" );
137+
} catch( JSException e ) {
138+
webEngine.executeScript( "var wasmImports = {}" );
139+
wasmImports = (JSObject)webEngine.executeScript( "wasmImports" );
140+
}
141+
main.run();
142+
}
143+
} );
144+
// Load page
145+
webEngine.load( url );
146+
147+
VBox vBox = new VBox( browser );
148+
Scene scene = new Scene( vBox );
149+
150+
primaryStage.setTitle( "JWebAssembly Emulator" );
151+
primaryStage.setScene( scene );
152+
primaryStage.show();
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)