Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions native-image/wasm-spring-shell/ansi_up.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
/*
* (The MIT License)
*
* Copyright (c) 2011 github.com/drudru
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
"use strict";
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
Expand Down
39 changes: 2 additions & 37 deletions native-image/wasm-spring-shell/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@
</div>
<div class="gen-back">
<div id="content">
<span>This demo runs a <a href="https://docs.spring.io/spring-shell/reference/getting-started.html">hello world Spring Shell application</a> on WebAssembly generated by <a href="https://www.graalvm.org" target="_blank">GraalVM</a> Web Image. Press enter to execute a command such as '<code>help</code>' or '<code>hello Jane</code>'. It was shown as part of <a href="https://m.devoxx.com/events/dvbe25/talks/7023/graalvm-meets-webassembly" target="_blank">this talk</a> at Devoxx Belgium 2025.</span>
<pre id="output" contenteditable="true" spellcheck="false">shell:>help</pre>
<script src="wasm-spring-shell.js"></script>
<pre id="output" contenteditable="false" spellcheck="false"></pre>
</div>
</div>
<footer>
Expand All @@ -177,40 +175,7 @@
</div>
</footer>
<span id="forkongithub"><a href="https://github.com/graalvm/graalvm-demos/tree/master/native-image/wasm-spring-shell" target="_blank">Code on GitHub</a></span>
<script type="module" type="text/javascript">
import { AnsiUp } from './ansi_up.js'
const ansi_up = new AnsiUp();
const consolePrefix = "shell:>";

function moveCaretToEnd(el) {
el.focus();
const range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.scrollTop = el.scrollHeight;
}

const output = document.getElementById("output");
output.addEventListener("keydown", async (event) => {
if (event.key === "Enter") {
const lines = event.target.textContent.split("\n");
const lastLine = lines[lines.length - 1];
if (lastLine.startsWith(consolePrefix)) {
await GraalVM.run(lastLine.slice(consolePrefix.length).split(" "));
output.innerHTML += consolePrefix;
moveCaretToEnd(output);
}
}
});

// called from OutputWriter.writeLine()
window.appendOutput = (line) => {
const html = ansi_up.ansi_to_html(line);
output.innerHTML += html + "\n";
}
<script type="module" src="main.mjs" type="text/javascript"></script>
</script>
</body>
</html>
74 changes: 74 additions & 0 deletions native-image/wasm-spring-shell/main.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { AnsiUp } from "./ansi_up.js";
const ansi_up = new AnsiUp();
const consolePrefix = "shell:>";

function moveCaretToEnd(el) {
el.focus();
const range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.scrollTop = el.scrollHeight;
}

const output = document.getElementById("output");
output.addEventListener("keydown", async (event) => {
if (event.key === "Enter") {
const lines = event.target.textContent.split("\n");
const lastLine = lines[lines.length - 1];
if (lastLine.startsWith(consolePrefix)) {
disable();
output.innerHTML += "\n";
const line = lastLine.slice(consolePrefix.length);
postLine(line);
}
}
});

function enable() {
output.setAttribute("contenteditable", "true");
output.focus();
output.innerHTML += consolePrefix;
moveCaretToEnd(output);
}

function disable() {
output.setAttribute("contenteditable", "false");
}

window.appendOutput = (line) => {
const html = ansi_up.ansi_to_html(line);
output.innerHTML += html + "\n";
};

const worker = new Worker("./worker.js");

function postLine(str) {
var bytes = new TextEncoder().encode(str);
sharedI32[1] = bytes.length;
new Uint8Array(sharedBuffer, 8).set(bytes);
Atomics.store(sharedI32, 0, 1);
Atomics.notify(sharedI32, 0, 1);
}

worker.onmessage = (e) => {
const data = e.data;
const type = data.type;

console.log(`Got message: ${type}`);

if (type === "sab") {
globalThis.sharedBuffer = data.sab;
globalThis.sharedI32 = new Int32Array(globalThis.sharedBuffer);
} else if (type === "ready") {
enable();
} else if (type === "output") {
appendOutput(data.message);
} else if (type === "error") {
appendOutput("Got error " + data.message);
} else {
appendOutput("Got unknown message: " + type);
}
};
122 changes: 37 additions & 85 deletions native-image/wasm-spring-shell/wasm-spring-shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,47 +287,6 @@ class ConsoleWriter {
var stdoutWriter = new ConsoleWriter(console.log);
var stderrWriter = new ConsoleWriter(console.error);

class OutputWriter {
constructor(domElement) {
this.line = "";
this.newline = "\n".charCodeAt(0);
this.closed = false;
this.domElement = domElement;
}

printChars(chars) {
let index = chars.lastIndexOf(this.newline);

if (index >= 0) {
this.line += charArrayToString(chars.slice(0, index));
this.writeLine();
chars = chars.slice(index + 1);
}

this.line += charArrayToString(chars);
}

writeLine() {
window.appendOutput(this.line);
this.line = "";
}

flush() {
// nothing to do
}

close() {
if (this.closed) {
return;
}
this.closed = true;

this.flush();
}
}

var stdoutWriter = new OutputWriter(document.getElementById("output"));


/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
Expand Down Expand Up @@ -1354,6 +1313,20 @@ wasmImports.interop = {
}
;
wasmImports.jsbody = {
'_DemoInputProvider.getMessage___String' : (...args) => (function(){
try{
const length = globalThis.sharedI32[1];
const byteArray = new Uint8Array(length);
byteArray.set(new Uint8Array(globalThis.sharedBuffer, 8, length))
return new TextDecoder().decode(byteArray);
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_DemoInputProvider.waitForMessage___V' : (...args) => (function(){
try{
Atomics.wait(globalThis.sharedI32, 0, 0);
Atomics.store(globalThis.sharedI32, 0, 0);
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSBigInt.javaString___String' : (...args) => (function(){
try{
return conversion.toProxy(toJavaString(this.toString()));
Expand Down Expand Up @@ -1399,11 +1372,26 @@ wasmImports.jsbody = {
return conversion.toProxy(conversion.createJavaDouble(this));
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSObject.create___JSObject' : (...args) => (function(){
try{
return conversion.createAnonymousJavaScriptObject();
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSObject.extractFacadeClass___Class_Object' : (...args) => (function(cls){
try{
return conversion.tryExtractFacadeClass(this, cls);
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSObject.get___Object_Object' : (...args) => (function(key){
try{
return this[key];
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSObject.set___Object_Object_V' : (...args) => (function(key,newValue){
try{
this[key] = newValue;
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_JSObject.stringValue___String' : (...args) => (function(){
try{
return conversion.toProxy(toJavaString(this.toString()));
Expand Down Expand Up @@ -1444,6 +1432,11 @@ wasmImports.jsbody = {
return Math.random();
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
'_Worker.postMessage___JSObject_V' : (...args) => (function(message){
try{
postMessage(message);
}catch( e ) {
conversion.handleJSError(e);}}).call(...args),
}
;

Expand Down Expand Up @@ -2819,47 +2812,6 @@ data.wasm = await wasmInstantiate(config, vmArgs);

})();

// (function() {
// /*
// * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
// *
// * This code is free software; you can redistribute it and/or modify it
// * under the terms of the GNU General Public License version 2 only, as
// * published by the Free Software Foundation. Oracle designates this
// * particular file as subject to the "Classpath" exception as provided
// * by Oracle in the LICENSE file that accompanied this code.
// *
// * This code is distributed in the hope that it will be useful, but WITHOUT
// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// * version 2 for more details (a copy is included in the LICENSE file that
// * accompanied this code).
// *
// * You should have received a copy of the GNU General Public License version
// * 2 along with this work; if not, write to the Free Software Foundation,
// * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
// *
// * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
// * or visit www.oracle.com if you need additional information or have any
// * questions.
// */

// /**
// * Try to load commandline arguments for various JS runtimes.
// */
// function load_cmd_args() {
// if (typeof process === "object" && "argv" in process) {
// // nodejs
// return process.argv.slice(2);
// } else if (typeof scriptArgs == "object") {
// // spidermonkey
// return scriptArgs;
// }

// return ['help'];
// }

// const config = new GraalVM.Config();
// GraalVM.run(load_cmd_args(),config).catch(console.error);
// })();
(function() {
globalThis["GraalVM"] = GraalVM;
})();
Binary file modified native-image/wasm-spring-shell/wasm-spring-shell.js.wasm
Binary file not shown.
10 changes: 10 additions & 0 deletions native-image/wasm-spring-shell/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
globalThis.sharedBuffer = new SharedArrayBuffer(65536);
globalThis.sharedI32 = new Int32Array(globalThis.sharedBuffer);
postMessage({
type: "sab",
sab: globalThis.sharedBuffer,
});
importScripts("./wasm-spring-shell.js");
const config = new GraalVM.Config();
config.wasm_path = "./wasm-spring-shell.js.wasm";
GraalVM.run([], config);