mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-21 01:00:54 +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
|
||||
/deploy-with-env.sh
|
||||
/release-with-env.sh
|
||||
/.gradle
|
||||
/build-logic/.gradle
|
||||
.gradle
|
||||
/relocated
|
||||
/samples/.gradle
|
||||
local.properties
|
||||
teavm-local.properties
|
||||
.kotlin
|
||||
node_modules
|
||||
|
||||
# KDE
|
||||
.directory
|
||||
|
@ -1,3 +1,5 @@
|
||||
import com.github.gradle.node.npm.task.NpmTask
|
||||
|
||||
/*
|
||||
* Copyright 2023 Alexey Andreev.
|
||||
*
|
||||
@ -17,10 +19,15 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`teavm-publish`
|
||||
alias(libs.plugins.nodejs)
|
||||
}
|
||||
|
||||
description = "Compiler, backends and runtime"
|
||||
|
||||
node {
|
||||
download = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":interop:core"))
|
||||
api(project(":metaprogramming:api"))
|
||||
@ -35,6 +42,55 @@ dependencies {
|
||||
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 {
|
||||
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 };
|
||||
}();
|
@ -71,3 +71,4 @@ version.ref = "shadow"
|
||||
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||
intellij = { id = "org.jetbrains.intellij", version = "1.17.2" }
|
||||
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");
|
||||
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("let teavm = await TeaVM.wasmGC.load('/tests/" + name + "/test.wasm');\n");
|
||||
writer.write("import { load } from '/resources/org/teavm/backend/wasm/wasm-gc-module-runtime.js';\n");
|
||||
writer.write("let teavm = await load('/tests/" + name + "/test.wasm');\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") {
|
||||
//dependsOn(generateJs, generateLibJs)
|
||||
dependsOn(generateJs, generateLibJs)
|
||||
archiveClassifier = "js"
|
||||
from(layout.buildDirectory.dir("teavm"), layout.buildDirectory.dir("teavm-lib"))
|
||||
entryCompression = ZipEntryCompression.DEFLATED
|
||||
|
@ -29,7 +29,7 @@ public abstract class CopyWasmGCRuntimeTask extends DefaultTask {
|
||||
|
||||
@TaskAction
|
||||
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 output = getOutputFile().get().getAsFile();
|
||||
try (var input = classLoader.getResourceAsStream(resourceName)) {
|
||||
|
Loading…
Reference in New Issue
Block a user