mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: preprocess JS runtime with uglifyjs, add modular runtime
This commit is contained in:
parent
14a4a99fa5
commit
cb3ce477e2
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -4,13 +4,13 @@ build
|
||||||
/build-cache
|
/build-cache
|
||||||
/deploy-with-env.sh
|
/deploy-with-env.sh
|
||||||
/release-with-env.sh
|
/release-with-env.sh
|
||||||
/.gradle
|
.gradle
|
||||||
/build-logic/.gradle
|
|
||||||
/relocated
|
/relocated
|
||||||
/samples/.gradle
|
/samples/.gradle
|
||||||
local.properties
|
local.properties
|
||||||
teavm-local.properties
|
teavm-local.properties
|
||||||
.kotlin
|
.kotlin
|
||||||
|
node_modules
|
||||||
|
|
||||||
# KDE
|
# KDE
|
||||||
.directory
|
.directory
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import com.github.gradle.node.npm.task.NpmTask
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2023 Alexey Andreev.
|
* Copyright 2023 Alexey Andreev.
|
||||||
*
|
*
|
||||||
|
@ -17,10 +19,15 @@
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
`teavm-publish`
|
`teavm-publish`
|
||||||
|
alias(libs.plugins.nodejs)
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "Compiler, backends and runtime"
|
description = "Compiler, backends and runtime"
|
||||||
|
|
||||||
|
node {
|
||||||
|
download = true
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":interop:core"))
|
api(project(":interop:core"))
|
||||||
api(project(":metaprogramming:api"))
|
api(project(":metaprogramming:api"))
|
||||||
|
@ -35,6 +42,55 @@ dependencies {
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val jsOutputDir = layout.buildDirectory.dir("generated/js")
|
||||||
|
val jsOutputPackageDir = jsOutputDir.map { it.dir("org/teavm/backend/wasm") }
|
||||||
|
val jsInputDir = layout.projectDirectory.dir("src/main/js/wasm-gc-runtime")
|
||||||
|
val jsInput = jsInputDir.file("runtime.js")
|
||||||
|
|
||||||
|
fun registerRuntimeTasks(taskName: String, wrapperType: String, outputName: String) {
|
||||||
|
val generateTask by tasks.register<DefaultTask>("generate${taskName}Runtime") {
|
||||||
|
val wrapperFile = jsInputDir.file(wrapperType)
|
||||||
|
val runtimeFile = jsInput
|
||||||
|
val outputFile = jsOutputPackageDir.map { it.file("$outputName.js") }
|
||||||
|
inputs.files(wrapperFile, runtimeFile)
|
||||||
|
outputs.file(outputFile)
|
||||||
|
doLast {
|
||||||
|
val wrapper = wrapperFile.asFile.readText()
|
||||||
|
var runtime = runtimeFile.asFile.readText()
|
||||||
|
val startText = "// !BEGINNING!\n"
|
||||||
|
val startIndex = runtime.indexOf(startText)
|
||||||
|
if (startIndex >= 0) {
|
||||||
|
runtime = runtime.substring(startIndex + startText.length)
|
||||||
|
}
|
||||||
|
outputFile.get().asFile.writeText(wrapper.replace("include();", runtime))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val optimizeTask = tasks.register<NpmTask>("optimize${taskName}Runtime") {
|
||||||
|
val inputFiles = generateTask.outputs.files
|
||||||
|
val outputFile = jsOutputPackageDir.map { it.file("$outputName.min.js") }
|
||||||
|
inputs.files(inputFiles)
|
||||||
|
outputs.file(outputFile)
|
||||||
|
npmCommand.addAll("run", "uglify")
|
||||||
|
args.addAll(provider {
|
||||||
|
listOf(
|
||||||
|
"--",
|
||||||
|
"-m", "--module", "--toplevel",
|
||||||
|
inputFiles.singleFile.absolutePath,
|
||||||
|
"-o", outputFile.get().asFile.absolutePath
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.main.configure {
|
||||||
|
output.dir(mapOf("builtBy" to generateTask), jsOutputDir)
|
||||||
|
output.dir(mapOf("builtBy" to optimizeTask), jsOutputDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerRuntimeTasks("Simple", "simple-wrapper.js", "wasm-gc-runtime")
|
||||||
|
registerRuntimeTasks("Module", "module-wrapper.js", "wasm-gc-module-runtime")
|
||||||
|
|
||||||
teavmPublish {
|
teavmPublish {
|
||||||
artifactId = "teavm-core"
|
artifactId = "teavm-core"
|
||||||
}
|
}
|
25
core/package-lock.json
generated
Normal file
25
core/package-lock.json
generated
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "core",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"uglify-js": "3.19.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/uglify-js": {
|
||||||
|
"version": "3.19.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
|
||||||
|
"integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"bin": {
|
||||||
|
"uglifyjs": "bin/uglifyjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
core/package.json
Normal file
8
core/package.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"uglify": "uglifyjs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"uglify-js": "3.19.3"
|
||||||
|
}
|
||||||
|
}
|
18
core/src/main/js/wasm-gc-runtime/module-wrapper.js
Normal file
18
core/src/main/js/wasm-gc-runtime/module-wrapper.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include();
|
||||||
|
export { load, defaults };
|
609
core/src/main/js/wasm-gc-runtime/runtime.js
Normal file
609
core/src/main/js/wasm-gc-runtime/runtime.js
Normal file
|
@ -0,0 +1,609 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// !BEGINNING!
|
||||||
|
|
||||||
|
let globalsCache = new Map();
|
||||||
|
let stackDeobfuscator = null;
|
||||||
|
let chromeExceptionRegex = / *at .+\.wasm:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/;
|
||||||
|
let getGlobalName = function(name) {
|
||||||
|
let result = globalsCache.get(name);
|
||||||
|
if (typeof result === "undefined") {
|
||||||
|
result = new Function("return " + name + ";");
|
||||||
|
globalsCache.set(name, result);
|
||||||
|
}
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
let setGlobalName = function(name, value) {
|
||||||
|
new Function("value", name + " = value;")(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaults(imports) {
|
||||||
|
let context = {
|
||||||
|
exports: null
|
||||||
|
};
|
||||||
|
dateImports(imports);
|
||||||
|
consoleImports(imports, context);
|
||||||
|
coreImports(imports, context);
|
||||||
|
jsoImports(imports, context);
|
||||||
|
imports.teavmMath = Math;
|
||||||
|
return {
|
||||||
|
supplyExports(exports) {
|
||||||
|
context.exports = exports
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let javaExceptionSymbol = Symbol("javaException");
|
||||||
|
class JavaError extends Error {
|
||||||
|
constructor(javaException) {
|
||||||
|
super();
|
||||||
|
this[javaExceptionSymbol] = javaException;
|
||||||
|
}
|
||||||
|
get message() {
|
||||||
|
let exceptionMessage = exports["teavm.exceptionMessage"];
|
||||||
|
if (typeof exceptionMessage === "function") {
|
||||||
|
let message = exceptionMessage(this[javaExceptionSymbol]);
|
||||||
|
if (message != null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "(could not fetch message)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateImports(imports) {
|
||||||
|
imports.teavmDate = {
|
||||||
|
currentTimeMillis: () => new Date().getTime(),
|
||||||
|
dateToString: timestamp => new Date(timestamp).toString(),
|
||||||
|
getYear: timestamp => new Date(timestamp).getFullYear(),
|
||||||
|
setYear(timestamp, year) {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
date.setFullYear(year);
|
||||||
|
return date.getTime();
|
||||||
|
},
|
||||||
|
getMonth: timestamp =>new Date(timestamp).getMonth(),
|
||||||
|
setMonth(timestamp, month) {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
date.setMonth(month);
|
||||||
|
return date.getTime();
|
||||||
|
},
|
||||||
|
getDate: timestamp =>new Date(timestamp).getDate(),
|
||||||
|
setDate(timestamp, value) {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
date.setDate(value);
|
||||||
|
return date.getTime();
|
||||||
|
},
|
||||||
|
create: (year, month, date, hrs, min, sec) => new Date(year, month, date, hrs, min, sec).getTime(),
|
||||||
|
createFromUTC: (year, month, date, hrs, min, sec) => Date.UTC(year, month, date, hrs, min, sec)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function consoleImports(imports) {
|
||||||
|
let stderr = "";
|
||||||
|
let stdout = "";
|
||||||
|
imports.teavmConsole = {
|
||||||
|
putcharStderr(c) {
|
||||||
|
if (c === 10) {
|
||||||
|
console.error(stderr);
|
||||||
|
stderr = "";
|
||||||
|
} else {
|
||||||
|
stderr += String.fromCharCode(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
putcharStdout(c) {
|
||||||
|
if (c === 10) {
|
||||||
|
console.log(stdout);
|
||||||
|
stdout = "";
|
||||||
|
} else {
|
||||||
|
stdout += String.fromCharCode(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function coreImports(imports, context) {
|
||||||
|
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
|
let report = context.exports["teavm.reportGarbageCollectedValue"];
|
||||||
|
if (typeof report === "function") {
|
||||||
|
report(heldValue)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
|
let report = context.exports["teavm.reportGarbageCollectedString"];
|
||||||
|
if (typeof report === "function") {
|
||||||
|
report(heldValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
imports.teavm = {
|
||||||
|
createWeakRef(value, heldValue) {
|
||||||
|
let weakRef = new WeakRef(value);
|
||||||
|
if (heldValue !== null) {
|
||||||
|
finalizationRegistry.register(value, heldValue)
|
||||||
|
}
|
||||||
|
return weakRef;
|
||||||
|
},
|
||||||
|
deref: weakRef => weakRef.deref(),
|
||||||
|
createStringWeakRef(value, heldValue) {
|
||||||
|
let weakRef = new WeakRef(value);
|
||||||
|
stringFinalizationRegistry.register(value, heldValue)
|
||||||
|
return weakRef;
|
||||||
|
},
|
||||||
|
stringDeref: weakRef => weakRef.deref(),
|
||||||
|
currentStackTrace() {
|
||||||
|
if (stackDeobfuscator) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let reportCallFrame = context.exports["teavm.reportCallFrame"];
|
||||||
|
if (typeof reportCallFrame !== "function") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let stack = new Error().stack;
|
||||||
|
for (let line in stack.split("\n")) {
|
||||||
|
let match = chromeExceptionRegex.exec(line);
|
||||||
|
if (match !== null) {
|
||||||
|
let address = parseInt(match.groups[1], 16);
|
||||||
|
let frames = stackDeobfuscator(address);
|
||||||
|
for (let frame of frames) {
|
||||||
|
let line = frame.line;
|
||||||
|
reportCallFrame(file, method, cls, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsoImports(imports, context) {
|
||||||
|
let javaObjectSymbol = Symbol("javaObject");
|
||||||
|
let functionsSymbol = Symbol("functions");
|
||||||
|
let functionOriginSymbol = Symbol("functionOrigin");
|
||||||
|
let wrapperCallMarkerSymbol = Symbol("wrapperCallMarker");
|
||||||
|
|
||||||
|
let jsWrappers = new WeakMap();
|
||||||
|
let javaWrappers = new WeakMap();
|
||||||
|
let primitiveWrappers = new Map();
|
||||||
|
let primitiveFinalization = new FinalizationRegistry(token => primitiveFinalization.delete(token));
|
||||||
|
let hashCodes = new WeakMap();
|
||||||
|
let javaExceptionWrappers = new WeakMap();
|
||||||
|
let lastHashCode = 2463534242;
|
||||||
|
let nextHashCode = () => {
|
||||||
|
let x = lastHashCode;
|
||||||
|
x ^= x << 13;
|
||||||
|
x ^= x >>> 17;
|
||||||
|
x ^= x << 5;
|
||||||
|
lastHashCode = x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function identity(value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function sanitizeName(str) {
|
||||||
|
let result = "";
|
||||||
|
let firstChar = str.charAt(0);
|
||||||
|
result += isIdentifierStart(firstChar) ? firstChar : '_';
|
||||||
|
for (let i = 1; i < str.length; ++i) {
|
||||||
|
let c = str.charAt(i)
|
||||||
|
result += isIdentifierPart(c) ? c : '_';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function isIdentifierStart(s) {
|
||||||
|
return s >= 'A' && s <= 'Z' || s >= 'a' && s <= 'z' || s === '_' || s === '$';
|
||||||
|
}
|
||||||
|
function isIdentifierPart(s) {
|
||||||
|
return isIdentifierStart(s) || s >= '0' && s <= '9';
|
||||||
|
}
|
||||||
|
function setProperty(obj, prop, value) {
|
||||||
|
if (obj === null) {
|
||||||
|
setGlobalName(prop, value);
|
||||||
|
} else {
|
||||||
|
obj[prop] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function javaExceptionToJs(e) {
|
||||||
|
if (e instanceof WebAssembly.Exception) {
|
||||||
|
let tag = context.exports["teavm.javaException"];
|
||||||
|
if (e.is(tag)) {
|
||||||
|
let javaException = e.getArg(tag, 0);
|
||||||
|
let extracted = extractException(javaException);
|
||||||
|
if (extracted !== null) {
|
||||||
|
return extracted;
|
||||||
|
}
|
||||||
|
let wrapperRef = javaExceptionWrappers.get(javaException);
|
||||||
|
if (typeof wrapperRef != "undefined") {
|
||||||
|
let wrapper = wrapperRef.deref();
|
||||||
|
if (typeof wrapper !== "undefined") {
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let wrapper = new JavaError(javaException);
|
||||||
|
javaExceptionWrappers.set(javaException, new WeakRef(wrapper));
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
function jsExceptionAsJava(e) {
|
||||||
|
if (javaExceptionSymbol in e) {
|
||||||
|
return e[javaExceptionSymbol];
|
||||||
|
} else {
|
||||||
|
return context.exports["teavm.js.wrapException"](e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function rethrowJsAsJava(e) {
|
||||||
|
context.exports["teavm.js.throwException"](jsExceptionAsJava(e));
|
||||||
|
}
|
||||||
|
function extractException(e) {
|
||||||
|
return context.exports["teavm.js.extractException"](e);
|
||||||
|
}
|
||||||
|
function rethrowJavaAsJs(e) {
|
||||||
|
throw javaExceptionToJs(e);
|
||||||
|
}
|
||||||
|
function getProperty(obj, prop) {
|
||||||
|
try {
|
||||||
|
return obj !== null ? obj[prop] : getGlobalName(prop)
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function defineFunction(fn) {
|
||||||
|
let params = [];
|
||||||
|
for (let i = 0; i < fn.length; ++i) {
|
||||||
|
params.push("p" + i);
|
||||||
|
}
|
||||||
|
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
||||||
|
return new Function("rethrowJavaAsJs", "fn",
|
||||||
|
`return function(${paramsAsString}) {\n` +
|
||||||
|
` try {\n` +
|
||||||
|
` return fn(${paramsAsString});\n` +
|
||||||
|
` } catch (e) {\n` +
|
||||||
|
` rethrowJavaAsJs(e);\n` +
|
||||||
|
` }\n` +
|
||||||
|
`};`
|
||||||
|
)(rethrowJavaAsJs, fn);
|
||||||
|
}
|
||||||
|
function renameConstructor(name, c) {
|
||||||
|
return new Function(
|
||||||
|
"constructor",
|
||||||
|
`return function ${name}(marker, javaObject) {\n` +
|
||||||
|
` return constructor.call(this, marker, javaObject);\n` +
|
||||||
|
`}\n`
|
||||||
|
)(c);
|
||||||
|
}
|
||||||
|
imports.teavmJso = {
|
||||||
|
emptyString: () => "",
|
||||||
|
stringFromCharCode: code => String.fromCharCode(code),
|
||||||
|
concatStrings: (a, b) => a + b,
|
||||||
|
stringLength: s => s.length,
|
||||||
|
charAt: (s, index) => s.charCodeAt(index),
|
||||||
|
emptyArray: () => [],
|
||||||
|
appendToArray: (array, e) => array.push(e),
|
||||||
|
unwrapBoolean: value => value ? 1 : 0,
|
||||||
|
wrapBoolean: value => !!value,
|
||||||
|
getProperty: getProperty,
|
||||||
|
getPropertyPure: getProperty,
|
||||||
|
setProperty: setProperty,
|
||||||
|
setPropertyPure: setProperty,
|
||||||
|
global(name) {
|
||||||
|
try {
|
||||||
|
return getGlobalName(name);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createClass(name, parent, constructor) {
|
||||||
|
name = sanitizeName(name || "JavaObject");
|
||||||
|
let action;
|
||||||
|
if (parent === null) {
|
||||||
|
action = function (javaObject) {
|
||||||
|
this[javaObjectSymbol] = javaObject;
|
||||||
|
this[functionsSymbol] = null;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
action = function (javaObject) {
|
||||||
|
parent.call(this, javaObject);
|
||||||
|
};
|
||||||
|
fn.prototype = Object.create(parent);
|
||||||
|
fn.prototype.constructor = parent;
|
||||||
|
}
|
||||||
|
let fn = renameConstructor(name, function (marker, javaObject) {
|
||||||
|
if (marker === wrapperCallMarkerSymbol) {
|
||||||
|
action.call(this, javaObject);
|
||||||
|
} else if (constructor === null) {
|
||||||
|
throw new Error("This class can't be instantiated directly");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return constructor.apply(null, arguments);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fn.prototype = Object.create(parent || Object.prototype);
|
||||||
|
fn.prototype.constructor = fn;
|
||||||
|
let boundFn = renameConstructor(name, function(javaObject) {
|
||||||
|
return fn.call(this, wrapperCallMarkerSymbol, javaObject);
|
||||||
|
});
|
||||||
|
boundFn[wrapperCallMarkerSymbol] = fn;
|
||||||
|
boundFn.prototype = fn.prototype;
|
||||||
|
return boundFn;
|
||||||
|
},
|
||||||
|
exportClass(cls) {
|
||||||
|
return cls[wrapperCallMarkerSymbol];
|
||||||
|
},
|
||||||
|
defineMethod(cls, name, fn) {
|
||||||
|
let params = [];
|
||||||
|
for (let i = 1; i < fn.length; ++i) {
|
||||||
|
params.push("p" + i);
|
||||||
|
}
|
||||||
|
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
||||||
|
cls.prototype[name] = new Function("rethrowJavaAsJs", "fn",
|
||||||
|
`return function(${paramsAsString}) {\n` +
|
||||||
|
` try {\n` +
|
||||||
|
` return fn(${['this', params].join(", ")});\n` +
|
||||||
|
` } catch (e) {\n` +
|
||||||
|
` rethrowJavaAsJs(e);\n` +
|
||||||
|
` }\n` +
|
||||||
|
`};`
|
||||||
|
)(rethrowJavaAsJs, fn);
|
||||||
|
},
|
||||||
|
defineStaticMethod(cls, name, fn) {
|
||||||
|
cls[name] = defineFunction(fn);
|
||||||
|
},
|
||||||
|
defineFunction: defineFunction,
|
||||||
|
defineProperty(cls, name, getFn, setFn) {
|
||||||
|
let descriptor = {
|
||||||
|
get() {
|
||||||
|
try {
|
||||||
|
return getFn(this);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (setFn !== null) {
|
||||||
|
descriptor.set = function(value) {
|
||||||
|
try {
|
||||||
|
setFn(this, value);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.defineProperty(cls.prototype, name, descriptor);
|
||||||
|
},
|
||||||
|
defineStaticProperty(cls, name, getFn, setFn) {
|
||||||
|
let descriptor = {
|
||||||
|
get() {
|
||||||
|
try {
|
||||||
|
return getFn();
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (setFn !== null) {
|
||||||
|
descriptor.set = function(value) {
|
||||||
|
try {
|
||||||
|
setFn(value);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.defineProperty(cls, name, descriptor);
|
||||||
|
},
|
||||||
|
javaObjectToJS(instance, cls) {
|
||||||
|
let existing = jsWrappers.get(instance);
|
||||||
|
if (typeof existing != "undefined") {
|
||||||
|
let result = existing.deref();
|
||||||
|
if (typeof result !== "undefined") {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let obj = new cls(instance);
|
||||||
|
jsWrappers.set(instance, new WeakRef(obj));
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
unwrapJavaObject(instance) {
|
||||||
|
return instance[javaObjectSymbol];
|
||||||
|
},
|
||||||
|
asFunction(instance, propertyName) {
|
||||||
|
let functions = instance[functionsSymbol];
|
||||||
|
if (functions === null) {
|
||||||
|
functions = Object.create(null);
|
||||||
|
instance[functionsSymbol] = functions;
|
||||||
|
}
|
||||||
|
let result = functions[propertyName];
|
||||||
|
if (typeof result !== 'function') {
|
||||||
|
result = function() {
|
||||||
|
return instance[propertyName].apply(instance, arguments);
|
||||||
|
}
|
||||||
|
result[functionOriginSymbol] = instance;
|
||||||
|
functions[propertyName] = result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
functionAsObject(fn, property) {
|
||||||
|
let origin = fn[functionOriginSymbol];
|
||||||
|
if (typeof origin !== 'undefined') {
|
||||||
|
let functions = origin[functionsSymbol];
|
||||||
|
if (functions !== void 0 && functions[property] === fn) {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
[property]: function(...args) {
|
||||||
|
try {
|
||||||
|
return fn(...args);
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
wrapObject(obj) {
|
||||||
|
if (obj === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof obj === "object" || typeof obj === "function" || typeof "obj" === "symbol") {
|
||||||
|
let result = obj[javaObjectSymbol];
|
||||||
|
if (typeof result === "object") {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = javaWrappers.get(obj);
|
||||||
|
if (result !== void 0) {
|
||||||
|
result = result.deref();
|
||||||
|
if (result !== void 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = context.exports["teavm.jso.createWrapper"](obj);
|
||||||
|
javaWrappers.set(obj, new WeakRef(result));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
let result = primitiveWrappers.get(obj);
|
||||||
|
if (result !== void 0) {
|
||||||
|
result = result.deref();
|
||||||
|
if (result !== void 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = context.exports["teavm.jso.createWrapper"](obj);
|
||||||
|
primitiveWrappers.set(obj, new WeakRef(result));
|
||||||
|
primitiveFinalization.register(result, obj);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isPrimitive: (value, type) => typeof value === type,
|
||||||
|
instanceOf: (value, type) => value instanceof type,
|
||||||
|
instanceOfOrNull: (value, type) => value === null || value instanceof type,
|
||||||
|
sameRef: (a, b) => a === b,
|
||||||
|
hashCode: (obj) => {
|
||||||
|
if (typeof obj === "object" || typeof obj === "function" || typeof obj === "symbol") {
|
||||||
|
let code = hashCodes.get(obj);
|
||||||
|
if (typeof code === "number") {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
code = nextHashCode();
|
||||||
|
hashCodes.set(obj, code);
|
||||||
|
return code;
|
||||||
|
} else if (typeof obj === "number") {
|
||||||
|
return obj | 0;
|
||||||
|
} else if (typeof obj === "bigint") {
|
||||||
|
return BigInt.asIntN(obj, 32);
|
||||||
|
} else if (typeof obj === "boolean") {
|
||||||
|
return obj ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apply: (instance, method, args) => {
|
||||||
|
try {
|
||||||
|
if (instance === null) {
|
||||||
|
let fn = getGlobalName(method);
|
||||||
|
return fn(...args);
|
||||||
|
} else {
|
||||||
|
return instance[method](...args);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
concatArray: (a, b) => a.concat(b),
|
||||||
|
getJavaException: e => e[javaExceptionSymbol]
|
||||||
|
};
|
||||||
|
for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte",
|
||||||
|
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
||||||
|
imports.teavmJso[name] = identity;
|
||||||
|
}
|
||||||
|
function wrapCallFromJavaToJs(call) {
|
||||||
|
try {
|
||||||
|
return call();
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let argumentList = [];
|
||||||
|
for (let i = 0; i < 32; ++i) {
|
||||||
|
let args = argumentList.length === 0 ? "" : argumentList.join(", ");
|
||||||
|
let argsAndBody = [...argumentList, "body"].join(", ");
|
||||||
|
imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body",
|
||||||
|
`return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs);`
|
||||||
|
).bind(null, wrapCallFromJavaToJs);
|
||||||
|
imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList,
|
||||||
|
`try {\n` +
|
||||||
|
` return fn(${args});\n` +
|
||||||
|
`} catch (e) {\n` +
|
||||||
|
` rethrowJsAsJava(e);\n` +
|
||||||
|
`}`
|
||||||
|
).bind(null, rethrowJsAsJava);
|
||||||
|
imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance",
|
||||||
|
"method", ...argumentList,
|
||||||
|
`try {\n`+
|
||||||
|
` return instance !== null\n` +
|
||||||
|
` ? instance[method](${args})\n` +
|
||||||
|
` : getGlobalName(method)(${args});\n` +
|
||||||
|
`} catch (e) {\n` +
|
||||||
|
` rethrowJsAsJava(e);\n` +
|
||||||
|
`}`
|
||||||
|
).bind(null, rethrowJsAsJava, getGlobalName);
|
||||||
|
imports.teavmJso["construct" + i] = new Function("rethrowJsAsJava", "constructor", ...argumentList,
|
||||||
|
`try {\n` +
|
||||||
|
` return new constructor(${args});\n` +
|
||||||
|
`} catch (e) {\n` +
|
||||||
|
` rethrowJsAsJava(e);\n` +
|
||||||
|
`}`
|
||||||
|
).bind(null, rethrowJsAsJava);
|
||||||
|
imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]");
|
||||||
|
|
||||||
|
let param = "p" + (i + 1);
|
||||||
|
argumentList.push(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function load(path, options) {
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const importObj = {};
|
||||||
|
const defaultsResults = defaults(importObj);
|
||||||
|
if (typeof options.installImports !== "undefined") {
|
||||||
|
options.installImports(importObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WebAssembly.instantiateStreaming(fetch(path), importObj)
|
||||||
|
.then(r => {
|
||||||
|
defaultsResults.supplyExports(r.instance.exports);
|
||||||
|
let userExports = {};
|
||||||
|
let teavm = {
|
||||||
|
exports: userExports,
|
||||||
|
instance: r.instance,
|
||||||
|
module: r.module
|
||||||
|
};
|
||||||
|
for (let key in r.instance.exports) {
|
||||||
|
let exportObj = r.instance.exports[key];
|
||||||
|
if (exportObj instanceof WebAssembly.Global) {
|
||||||
|
Object.defineProperty(userExports, key, {
|
||||||
|
get: () => exportObj.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return teavm;
|
||||||
|
});
|
||||||
|
}
|
22
core/src/main/js/wasm-gc-runtime/simple-wrapper.js
Normal file
22
core/src/main/js/wasm-gc-runtime/simple-wrapper.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TeaVM = TeaVM || {};
|
||||||
|
TeaVM.wasmGC = TeaVM.wasmGC || (() => {
|
||||||
|
include();
|
||||||
|
return { load, defaults };
|
||||||
|
})();
|
||||||
|
|
|
@ -1,609 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2024 Alexey Andreev.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var TeaVM = TeaVM || {};
|
|
||||||
if (window && window.TeaVM === undefined) {
|
|
||||||
window.TeaVM = TeaVM;
|
|
||||||
}
|
|
||||||
TeaVM.wasmGC = TeaVM.wasmGC || function() {
|
|
||||||
let exports;
|
|
||||||
let globalsCache = new Map();
|
|
||||||
let stackDeobfuscator = null;
|
|
||||||
let chromeExceptionRegex = / *at .+\.wasm:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/;
|
|
||||||
let getGlobalName = function(name) {
|
|
||||||
let result = globalsCache.get(name);
|
|
||||||
if (typeof result === "undefined") {
|
|
||||||
result = new Function("return " + name + ";");
|
|
||||||
globalsCache.set(name, result);
|
|
||||||
}
|
|
||||||
return result();
|
|
||||||
}
|
|
||||||
let setGlobalName = function(name, value) {
|
|
||||||
new Function("value", name + " = value;")(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaults(imports) {
|
|
||||||
dateImports(imports);
|
|
||||||
consoleImports(imports);
|
|
||||||
coreImports(imports);
|
|
||||||
jsoImports(imports);
|
|
||||||
imports.teavmMath = Math;
|
|
||||||
}
|
|
||||||
|
|
||||||
let javaExceptionSymbol = Symbol("javaException");
|
|
||||||
class JavaError extends Error {
|
|
||||||
constructor(javaException) {
|
|
||||||
super();
|
|
||||||
this[javaExceptionSymbol] = javaException;
|
|
||||||
}
|
|
||||||
get message() {
|
|
||||||
let exceptionMessage = exports["teavm.exceptionMessage"];
|
|
||||||
if (typeof exceptionMessage === "function") {
|
|
||||||
let message = exceptionMessage(this[javaExceptionSymbol]);
|
|
||||||
if (message != null) {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "(could not fetch message)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateImports(imports) {
|
|
||||||
imports.teavmDate = {
|
|
||||||
currentTimeMillis: () => new Date().getTime(),
|
|
||||||
dateToString: timestamp => new Date(timestamp).toString(),
|
|
||||||
getYear: timestamp => new Date(timestamp).getFullYear(),
|
|
||||||
setYear(timestamp, year) {
|
|
||||||
let date = new Date(timestamp);
|
|
||||||
date.setFullYear(year);
|
|
||||||
return date.getTime();
|
|
||||||
},
|
|
||||||
getMonth: timestamp =>new Date(timestamp).getMonth(),
|
|
||||||
setMonth(timestamp, month) {
|
|
||||||
let date = new Date(timestamp);
|
|
||||||
date.setMonth(month);
|
|
||||||
return date.getTime();
|
|
||||||
},
|
|
||||||
getDate: timestamp =>new Date(timestamp).getDate(),
|
|
||||||
setDate(timestamp, value) {
|
|
||||||
let date = new Date(timestamp);
|
|
||||||
date.setDate(value);
|
|
||||||
return date.getTime();
|
|
||||||
},
|
|
||||||
create: (year, month, date, hrs, min, sec) => new Date(year, month, date, hrs, min, sec).getTime(),
|
|
||||||
createFromUTC: (year, month, date, hrs, min, sec) => Date.UTC(year, month, date, hrs, min, sec)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function consoleImports(imports) {
|
|
||||||
let stderr = "";
|
|
||||||
let stdout = "";
|
|
||||||
imports.teavmConsole = {
|
|
||||||
putcharStderr(c) {
|
|
||||||
if (c === 10) {
|
|
||||||
console.error(stderr);
|
|
||||||
stderr = "";
|
|
||||||
} else {
|
|
||||||
stderr += String.fromCharCode(c);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
putcharStdout(c) {
|
|
||||||
if (c === 10) {
|
|
||||||
console.log(stdout);
|
|
||||||
stdout = "";
|
|
||||||
} else {
|
|
||||||
stdout += String.fromCharCode(c);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function coreImports(imports) {
|
|
||||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
|
||||||
let report = exports["teavm.reportGarbageCollectedValue"];
|
|
||||||
if (typeof report === "function") {
|
|
||||||
report(heldValue)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
|
||||||
let report = exports["teavm.reportGarbageCollectedString"];
|
|
||||||
if (typeof report === "function") {
|
|
||||||
report(heldValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
imports.teavm = {
|
|
||||||
createWeakRef(value, heldValue) {
|
|
||||||
let weakRef = new WeakRef(value);
|
|
||||||
if (heldValue !== null) {
|
|
||||||
finalizationRegistry.register(value, heldValue)
|
|
||||||
}
|
|
||||||
return weakRef;
|
|
||||||
},
|
|
||||||
deref: weakRef => weakRef.deref(),
|
|
||||||
createStringWeakRef(value, heldValue) {
|
|
||||||
let weakRef = new WeakRef(value);
|
|
||||||
stringFinalizationRegistry.register(value, heldValue)
|
|
||||||
return weakRef;
|
|
||||||
},
|
|
||||||
stringDeref: weakRef => weakRef.deref(),
|
|
||||||
currentStackTrace() {
|
|
||||||
if (stackDeobfuscator) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let reportCallFrame = exports["teavm.reportCallFrame"];
|
|
||||||
if (typeof reportCallFrame !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let stack = new Error().stack;
|
|
||||||
for (let line in stack.split("\n")) {
|
|
||||||
let match = chromeExceptionRegex.exec(line);
|
|
||||||
if (match !== null) {
|
|
||||||
let address = parseInt(match.groups[1], 16);
|
|
||||||
let frames = stackDeobfuscator(address);
|
|
||||||
for (let frame of frames) {
|
|
||||||
let line = frame.line;
|
|
||||||
reportCallFrame(file, method, cls, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsoImports(imports) {
|
|
||||||
let javaObjectSymbol = Symbol("javaObject");
|
|
||||||
let functionsSymbol = Symbol("functions");
|
|
||||||
let functionOriginSymbol = Symbol("functionOrigin");
|
|
||||||
let wrapperCallMarkerSymbol = Symbol("wrapperCallMarker");
|
|
||||||
|
|
||||||
let jsWrappers = new WeakMap();
|
|
||||||
let javaWrappers = new WeakMap();
|
|
||||||
let primitiveWrappers = new Map();
|
|
||||||
let primitiveFinalization = new FinalizationRegistry(token => primitiveFinalization.delete(token));
|
|
||||||
let hashCodes = new WeakMap();
|
|
||||||
let javaExceptionWrappers = new WeakMap();
|
|
||||||
let lastHashCode = 2463534242;
|
|
||||||
let nextHashCode = () => {
|
|
||||||
let x = lastHashCode;
|
|
||||||
x ^= x << 13;
|
|
||||||
x ^= x >>> 17;
|
|
||||||
x ^= x << 5;
|
|
||||||
lastHashCode = x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function identity(value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
function sanitizeName(str) {
|
|
||||||
let result = "";
|
|
||||||
let firstChar = str.charAt(0);
|
|
||||||
result += isIdentifierStart(firstChar) ? firstChar : '_';
|
|
||||||
for (let i = 1; i < str.length; ++i) {
|
|
||||||
let c = str.charAt(i)
|
|
||||||
result += isIdentifierPart(c) ? c : '_';
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
function isIdentifierStart(s) {
|
|
||||||
return s >= 'A' && s <= 'Z' || s >= 'a' && s <= 'z' || s === '_' || s === '$';
|
|
||||||
}
|
|
||||||
function isIdentifierPart(s) {
|
|
||||||
return isIdentifierStart(s) || s >= '0' && s <= '9';
|
|
||||||
}
|
|
||||||
function setProperty(obj, prop, value) {
|
|
||||||
if (obj === null) {
|
|
||||||
setGlobalName(prop, value);
|
|
||||||
} else {
|
|
||||||
obj[prop] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function javaExceptionToJs(e) {
|
|
||||||
if (e instanceof WebAssembly.Exception) {
|
|
||||||
let tag = exports["teavm.javaException"];
|
|
||||||
if (e.is(tag)) {
|
|
||||||
let javaException = e.getArg(tag, 0);
|
|
||||||
let extracted = extractException(javaException);
|
|
||||||
if (extracted !== null) {
|
|
||||||
return extracted;
|
|
||||||
}
|
|
||||||
let wrapperRef = javaExceptionWrappers.get(javaException);
|
|
||||||
if (typeof wrapperRef != "undefined") {
|
|
||||||
let wrapper = wrapperRef.deref();
|
|
||||||
if (typeof wrapper !== "undefined") {
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let wrapper = new JavaError(javaException);
|
|
||||||
javaExceptionWrappers.set(javaException, new WeakRef(wrapper));
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
function jsExceptionAsJava(e) {
|
|
||||||
if (javaExceptionSymbol in e) {
|
|
||||||
return e[javaExceptionSymbol];
|
|
||||||
} else {
|
|
||||||
return exports["teavm.js.wrapException"](e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function rethrowJsAsJava(e) {
|
|
||||||
exports["teavm.js.throwException"](jsExceptionAsJava(e));
|
|
||||||
}
|
|
||||||
function extractException(e) {
|
|
||||||
return exports["teavm.js.extractException"](e);
|
|
||||||
}
|
|
||||||
function rethrowJavaAsJs(e) {
|
|
||||||
throw javaExceptionToJs(e);
|
|
||||||
}
|
|
||||||
function getProperty(obj, prop) {
|
|
||||||
try {
|
|
||||||
return obj !== null ? obj[prop] : getGlobalName(prop)
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJsAsJava(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function defineFunction(fn) {
|
|
||||||
let params = [];
|
|
||||||
for (let i = 0; i < fn.length; ++i) {
|
|
||||||
params.push("p" + i);
|
|
||||||
}
|
|
||||||
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
|
||||||
return new Function("rethrowJavaAsJs", "fn",
|
|
||||||
`return function(${paramsAsString}) {\n` +
|
|
||||||
` try {\n` +
|
|
||||||
` return fn(${paramsAsString});\n` +
|
|
||||||
` } catch (e) {\n` +
|
|
||||||
` rethrowJavaAsJs(e);\n` +
|
|
||||||
` }\n` +
|
|
||||||
`};`
|
|
||||||
)(rethrowJavaAsJs, fn);
|
|
||||||
}
|
|
||||||
function renameConstructor(name, c) {
|
|
||||||
return new Function(
|
|
||||||
"constructor",
|
|
||||||
`return function ${name}(marker, javaObject) {\n` +
|
|
||||||
` return constructor.call(this, marker, javaObject);\n` +
|
|
||||||
`}\n`
|
|
||||||
)(c);
|
|
||||||
}
|
|
||||||
imports.teavmJso = {
|
|
||||||
emptyString: () => "",
|
|
||||||
stringFromCharCode: code => String.fromCharCode(code),
|
|
||||||
concatStrings: (a, b) => a + b,
|
|
||||||
stringLength: s => s.length,
|
|
||||||
charAt: (s, index) => s.charCodeAt(index),
|
|
||||||
emptyArray: () => [],
|
|
||||||
appendToArray: (array, e) => array.push(e),
|
|
||||||
unwrapBoolean: value => value ? 1 : 0,
|
|
||||||
wrapBoolean: value => !!value,
|
|
||||||
getProperty: getProperty,
|
|
||||||
getPropertyPure: getProperty,
|
|
||||||
setProperty: setProperty,
|
|
||||||
setPropertyPure: setProperty,
|
|
||||||
global(name) {
|
|
||||||
try {
|
|
||||||
return getGlobalName(name);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJsAsJava(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
createClass(name, parent, constructor) {
|
|
||||||
name = sanitizeName(name || "JavaObject");
|
|
||||||
let action;
|
|
||||||
if (parent === null) {
|
|
||||||
action = function (javaObject) {
|
|
||||||
this[javaObjectSymbol] = javaObject;
|
|
||||||
this[functionsSymbol] = null;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
action = function (javaObject) {
|
|
||||||
parent.call(this, javaObject);
|
|
||||||
};
|
|
||||||
fn.prototype = Object.create(parent);
|
|
||||||
fn.prototype.constructor = parent;
|
|
||||||
}
|
|
||||||
let fn = renameConstructor(name, function (marker, javaObject) {
|
|
||||||
if (marker === wrapperCallMarkerSymbol) {
|
|
||||||
action.call(this, javaObject);
|
|
||||||
} else if (constructor === null) {
|
|
||||||
throw new Error("This class can't be instantiated directly");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return constructor.apply(null, arguments);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fn.prototype = Object.create(parent || Object.prototype);
|
|
||||||
fn.prototype.constructor = fn;
|
|
||||||
let boundFn = renameConstructor(name, function(javaObject) {
|
|
||||||
return fn.call(this, wrapperCallMarkerSymbol, javaObject);
|
|
||||||
});
|
|
||||||
boundFn[wrapperCallMarkerSymbol] = fn;
|
|
||||||
boundFn.prototype = fn.prototype;
|
|
||||||
return boundFn;
|
|
||||||
},
|
|
||||||
exportClass(cls) {
|
|
||||||
return cls[wrapperCallMarkerSymbol];
|
|
||||||
},
|
|
||||||
defineMethod(cls, name, fn) {
|
|
||||||
let params = [];
|
|
||||||
for (let i = 1; i < fn.length; ++i) {
|
|
||||||
params.push("p" + i);
|
|
||||||
}
|
|
||||||
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
|
||||||
cls.prototype[name] = new Function("rethrowJavaAsJs", "fn",
|
|
||||||
`return function(${paramsAsString}) {\n` +
|
|
||||||
` try {\n` +
|
|
||||||
` return fn(${['this', params].join(", ")});\n` +
|
|
||||||
` } catch (e) {\n` +
|
|
||||||
` rethrowJavaAsJs(e);\n` +
|
|
||||||
` }\n` +
|
|
||||||
`};`
|
|
||||||
)(rethrowJavaAsJs, fn);
|
|
||||||
},
|
|
||||||
defineStaticMethod(cls, name, fn) {
|
|
||||||
cls[name] = defineFunction(fn);
|
|
||||||
},
|
|
||||||
defineFunction: defineFunction,
|
|
||||||
defineProperty(cls, name, getFn, setFn) {
|
|
||||||
let descriptor = {
|
|
||||||
get() {
|
|
||||||
try {
|
|
||||||
return getFn(this);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (setFn !== null) {
|
|
||||||
descriptor.set = function(value) {
|
|
||||||
try {
|
|
||||||
setFn(this, value);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.defineProperty(cls.prototype, name, descriptor);
|
|
||||||
},
|
|
||||||
defineStaticProperty(cls, name, getFn, setFn) {
|
|
||||||
let descriptor = {
|
|
||||||
get() {
|
|
||||||
try {
|
|
||||||
return getFn();
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (setFn !== null) {
|
|
||||||
descriptor.set = function(value) {
|
|
||||||
try {
|
|
||||||
setFn(value);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.defineProperty(cls, name, descriptor);
|
|
||||||
},
|
|
||||||
javaObjectToJS(instance, cls) {
|
|
||||||
let existing = jsWrappers.get(instance);
|
|
||||||
if (typeof existing != "undefined") {
|
|
||||||
let result = existing.deref();
|
|
||||||
if (typeof result !== "undefined") {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let obj = new cls(instance);
|
|
||||||
jsWrappers.set(instance, new WeakRef(obj));
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
unwrapJavaObject(instance) {
|
|
||||||
return instance[javaObjectSymbol];
|
|
||||||
},
|
|
||||||
asFunction(instance, propertyName) {
|
|
||||||
let functions = instance[functionsSymbol];
|
|
||||||
if (functions === null) {
|
|
||||||
functions = Object.create(null);
|
|
||||||
instance[functionsSymbol] = functions;
|
|
||||||
}
|
|
||||||
let result = functions[propertyName];
|
|
||||||
if (typeof result !== 'function') {
|
|
||||||
result = function() {
|
|
||||||
return instance[propertyName].apply(instance, arguments);
|
|
||||||
}
|
|
||||||
result[functionOriginSymbol] = instance;
|
|
||||||
functions[propertyName] = result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
functionAsObject(fn, property) {
|
|
||||||
let origin = fn[functionOriginSymbol];
|
|
||||||
if (typeof origin !== 'undefined') {
|
|
||||||
let functions = origin[functionsSymbol];
|
|
||||||
if (functions !== void 0 && functions[property] === fn) {
|
|
||||||
return origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
[property]: function(...args) {
|
|
||||||
try {
|
|
||||||
return fn(...args);
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
wrapObject(obj) {
|
|
||||||
if (obj === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (typeof obj === "object" || typeof obj === "function" || typeof "obj" === "symbol") {
|
|
||||||
let result = obj[javaObjectSymbol];
|
|
||||||
if (typeof result === "object") {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result = javaWrappers.get(obj);
|
|
||||||
if (result !== void 0) {
|
|
||||||
result = result.deref();
|
|
||||||
if (result !== void 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = exports["teavm.jso.createWrapper"](obj);
|
|
||||||
javaWrappers.set(obj, new WeakRef(result));
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
let result = primitiveWrappers.get(obj);
|
|
||||||
if (result !== void 0) {
|
|
||||||
result = result.deref();
|
|
||||||
if (result !== void 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = exports["teavm.jso.createWrapper"](obj);
|
|
||||||
primitiveWrappers.set(obj, new WeakRef(result));
|
|
||||||
primitiveFinalization.register(result, obj);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isPrimitive: (value, type) => typeof value === type,
|
|
||||||
instanceOf: (value, type) => value instanceof type,
|
|
||||||
instanceOfOrNull: (value, type) => value === null || value instanceof type,
|
|
||||||
sameRef: (a, b) => a === b,
|
|
||||||
hashCode: (obj) => {
|
|
||||||
if (typeof obj === "object" || typeof obj === "function" || typeof obj === "symbol") {
|
|
||||||
let code = hashCodes.get(obj);
|
|
||||||
if (typeof code === "number") {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
code = nextHashCode();
|
|
||||||
hashCodes.set(obj, code);
|
|
||||||
return code;
|
|
||||||
} else if (typeof obj === "number") {
|
|
||||||
return obj | 0;
|
|
||||||
} else if (typeof obj === "bigint") {
|
|
||||||
return BigInt.asIntN(obj, 32);
|
|
||||||
} else if (typeof obj === "boolean") {
|
|
||||||
return obj ? 1 : 0;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
apply: (instance, method, args) => {
|
|
||||||
try {
|
|
||||||
if (instance === null) {
|
|
||||||
let fn = getGlobalName(method);
|
|
||||||
return fn(...args);
|
|
||||||
} else {
|
|
||||||
return instance[method](...args);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJsAsJava(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
concatArray: (a, b) => a.concat(b),
|
|
||||||
getJavaException: e => e[javaExceptionSymbol]
|
|
||||||
};
|
|
||||||
for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte",
|
|
||||||
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
|
||||||
imports.teavmJso[name] = identity;
|
|
||||||
}
|
|
||||||
function wrapCallFromJavaToJs(call) {
|
|
||||||
try {
|
|
||||||
return call();
|
|
||||||
} catch (e) {
|
|
||||||
rethrowJsAsJava(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let argumentList = [];
|
|
||||||
for (let i = 0; i < 32; ++i) {
|
|
||||||
let args = argumentList.length === 0 ? "" : argumentList.join(", ");
|
|
||||||
let argsAndBody = [...argumentList, "body"].join(", ");
|
|
||||||
imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body",
|
|
||||||
`return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs);`
|
|
||||||
).bind(null, wrapCallFromJavaToJs);
|
|
||||||
imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList,
|
|
||||||
`try {\n` +
|
|
||||||
` return fn(${args});\n` +
|
|
||||||
`} catch (e) {\n` +
|
|
||||||
` rethrowJsAsJava(e);\n` +
|
|
||||||
`}`
|
|
||||||
).bind(null, rethrowJsAsJava);
|
|
||||||
imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance",
|
|
||||||
"method", ...argumentList,
|
|
||||||
`try {\n`+
|
|
||||||
` return instance !== null\n` +
|
|
||||||
` ? instance[method](${args})\n` +
|
|
||||||
` : getGlobalName(method)(${args});\n` +
|
|
||||||
`} catch (e) {\n` +
|
|
||||||
` rethrowJsAsJava(e);\n` +
|
|
||||||
`}`
|
|
||||||
).bind(null, rethrowJsAsJava, getGlobalName);
|
|
||||||
imports.teavmJso["construct" + i] = new Function("rethrowJsAsJava", "constructor", ...argumentList,
|
|
||||||
`try {\n` +
|
|
||||||
` return new constructor(${args});\n` +
|
|
||||||
`} catch (e) {\n` +
|
|
||||||
` rethrowJsAsJava(e);\n` +
|
|
||||||
`}`
|
|
||||||
).bind(null, rethrowJsAsJava);
|
|
||||||
imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]");
|
|
||||||
|
|
||||||
let param = "p" + (i + 1);
|
|
||||||
argumentList.push(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function load(path, options) {
|
|
||||||
if (!options) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const importObj = {};
|
|
||||||
defaults(importObj);
|
|
||||||
if (typeof options.installImports !== "undefined") {
|
|
||||||
options.installImports(importObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return WebAssembly.instantiateStreaming(fetch(path), importObj)
|
|
||||||
.then(r => {
|
|
||||||
exports = r.instance.exports;
|
|
||||||
let userExports = {};
|
|
||||||
let teavm = {
|
|
||||||
exports: userExports,
|
|
||||||
instance: r.instance,
|
|
||||||
module: r.module
|
|
||||||
};
|
|
||||||
for (let key in r.instance.exports) {
|
|
||||||
let exportObj = r.instance.exports[key];
|
|
||||||
if (exportObj instanceof WebAssembly.Global) {
|
|
||||||
Object.defineProperty(userExports, key, {
|
|
||||||
get: () => exportObj.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return teavm;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { load, defaults };
|
|
||||||
}();
|
|
|
@ -70,4 +70,5 @@ version.ref = "shadow"
|
||||||
|
|
||||||
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||||
intellij = { id = "org.jetbrains.intellij", version = "1.17.2" }
|
intellij = { id = "org.jetbrains.intellij", version = "1.17.2" }
|
||||||
pluginPublish = { id = "com.gradle.plugin-publish", version = "1.1.0" }
|
pluginPublish = { id = "com.gradle.plugin-publish", version = "1.1.0" }
|
||||||
|
nodejs = { id = "com.github.node-gradle.node", version = "7.1.0" }
|
|
@ -203,8 +203,8 @@ public class ExportTest {
|
||||||
}
|
}
|
||||||
var testProviderFile = new File(outputDir, "provider.js");
|
var testProviderFile = new File(outputDir, "provider.js");
|
||||||
try (var writer = new OutputStreamWriter(new FileOutputStream(testProviderFile), StandardCharsets.UTF_8)) {
|
try (var writer = new OutputStreamWriter(new FileOutputStream(testProviderFile), StandardCharsets.UTF_8)) {
|
||||||
writer.write("await import('/resources/org/teavm/backend/wasm/wasm-gc-runtime.js');\n");
|
writer.write("import { load } from '/resources/org/teavm/backend/wasm/wasm-gc-module-runtime.js';\n");
|
||||||
writer.write("let teavm = await TeaVM.wasmGC.load('/tests/" + name + "/test.wasm');\n");
|
writer.write("let teavm = await load('/tests/" + name + "/test.wasm');\n");
|
||||||
writer.write("export default teavm.exports;\n");
|
writer.write("export default teavm.exports;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ val generateLibJs by tasks.register<JavaExec>("generateLibJs") {
|
||||||
}
|
}
|
||||||
|
|
||||||
val zipWithJs by tasks.register<Jar>("zipWithJs") {
|
val zipWithJs by tasks.register<Jar>("zipWithJs") {
|
||||||
//dependsOn(generateJs, generateLibJs)
|
dependsOn(generateJs, generateLibJs)
|
||||||
archiveClassifier = "js"
|
archiveClassifier = "js"
|
||||||
from(layout.buildDirectory.dir("teavm"), layout.buildDirectory.dir("teavm-lib"))
|
from(layout.buildDirectory.dir("teavm"), layout.buildDirectory.dir("teavm-lib"))
|
||||||
entryCompression = ZipEntryCompression.DEFLATED
|
entryCompression = ZipEntryCompression.DEFLATED
|
||||||
|
|
|
@ -29,7 +29,7 @@ public abstract class CopyWasmGCRuntimeTask extends DefaultTask {
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public void copyRuntime() throws IOException {
|
public void copyRuntime() throws IOException {
|
||||||
var resourceName = "org/teavm/backend/wasm/wasm-gc-runtime.js";
|
var resourceName = "org/teavm/backend/wasm/wasm-gc-runtime.min.js";
|
||||||
var classLoader = CopyWasmGCRuntimeTask.class.getClassLoader();
|
var classLoader = CopyWasmGCRuntimeTask.class.getClassLoader();
|
||||||
var output = getOutputFile().get().getAsFile();
|
var output = getOutputFile().get().getAsFile();
|
||||||
try (var input = classLoader.getResourceAsStream(resourceName)) {
|
try (var input = classLoader.getResourceAsStream(resourceName)) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user