add interfaces and implements

This commit is contained in:
ZXMushroom63 2025-01-26 19:30:47 +08:00
parent bd4b5b9d39
commit 42a0f3e148
4 changed files with 43 additions and 17 deletions

View File

@ -24,6 +24,9 @@ Methods:
- Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class. - Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class.
- Also adds some metadata to make the class work with `ModAPI.util.asClass` - Also adds some metadata to make the class work with `ModAPI.util.asClass`
- [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37) - [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37)
- `ModAPI.reflect.implements(target: Class/ConstructorFunction, interface: ReflectClass)`
- Marks the provided interface as a supertype of the target class.
- JavaScript equivalent of the `implements` keyword
### ReflectClass Definition ### ReflectClass Definition
Each `ReflectClass` has the following properties: Each `ReflectClass` has the following properties:

View File

@ -151,6 +151,7 @@
ModAPI.hooks ||= {}; ModAPI.hooks ||= {};
ModAPI.hooks.freezeCallstack = false; ModAPI.hooks.freezeCallstack = false;
ModAPI.hooks._rippedData ||= []; ModAPI.hooks._rippedData ||= [];
ModAPI.hooks._rippedInterfaceMap ||= {};
ModAPI.hooks._teavm ||= {}; ModAPI.hooks._teavm ||= {};
ModAPI.hooks._rippedConstructors ||= {}; ModAPI.hooks._rippedConstructors ||= {};
ModAPI.hooks._rippedInternalConstructors ||= {}; ModAPI.hooks._rippedInternalConstructors ||= {};

View File

@ -50,13 +50,13 @@ function entriesToStaticVariableProxy(entries, prefix, clinitList) {
.join(",")}]; .join(",")}];
/*/ /*/
var proxy = `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( var proxy = `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace(
"var ", "var ",
"" ""
)}\`] = new Proxy({${entries )}\`] = new Proxy({${entries
.flatMap((x) => { .flatMap((x) => {
return '"' + x.name + '"'; return '"' + x.name + '"';
}) })
.join(":null,") + (entries.length > 0 ? ":null" : "") .join(":null,") + (entries.length > 0 ? ":null" : "")
}}, { }}, {
get: function (a,b,c) { get: function (a,b,c) {
switch (b) { switch (b) {
@ -136,7 +136,7 @@ var main;(function(){`
patchedFile = patchedFile.replaceAll("function TeaVMThread(", "globalThis.ModAPI.hooks.TeaVMThread = TeaVMThread;\nfunction TeaVMThread("); patchedFile = patchedFile.replaceAll("function TeaVMThread(", "globalThis.ModAPI.hooks.TeaVMThread = TeaVMThread;\nfunction TeaVMThread(");
_status("Getting clinit list..."); _status("Getting clinit list...");
var clinitList = [...patchedFile.matchAll(/^[\t ]*function \S+?_\S+?_\$callClinit\(/gm)].map(x=>x[0].replaceAll("function ", "").replaceAll("(", "").trim()); var clinitList = [...patchedFile.matchAll(/^[\t ]*function \S+?_\S+?_\$callClinit\(/gm)].map(x => x[0].replaceAll("function ", "").replaceAll("(", "").trim());
console.log(clinitList); console.log(clinitList);
_status("Extracting constructors and methods..."); _status("Extracting constructors and methods...");
@ -171,7 +171,7 @@ var main;(function(){`
); );
} }
); );
const extractInstanceMethodRegex = const extractInstanceMethodRegex =
/^[\t ]*function \S+?_\S+?_\S+?\((\$this)?/gm; // /^[\t ]*function \S+?_\S+?_\S+?\(\$this/gm /^[\t ]*function \S+?_\S+?_\S+?\((\$this)?/gm; // /^[\t ]*function \S+?_\S+?_\S+?\(\$this/gm
const extractInstanceMethodFullNameRegex = /function (\S*?)\(/gm; // /function (\S*?)\(\$this/gm const extractInstanceMethodFullNameRegex = /function (\S*?)\(/gm; // /function (\S*?)\(\$this/gm
@ -205,6 +205,7 @@ var main;(function(){`
}).filter(x => { }).filter(x => {
return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$")) return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$"))
}); });
//Also stores classes from $rt_classWithoutFields(0)
patchedFile = patchedFile.replaceAll( patchedFile = patchedFile.replaceAll(
/var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm, /var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm,
function (match) { function (match) {
@ -213,6 +214,7 @@ var main;(function(){`
"" ""
); );
var entries = []; var entries = [];
staticVariables.forEach((entry) => { staticVariables.forEach((entry) => {
if (entry.startsWith(prefix)) { if (entry.startsWith(prefix)) {
var variableName = entry var variableName = entry
@ -229,14 +231,17 @@ var main;(function(){`
}); });
var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList); var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList);
var shortPrefix = prefix.replace(
return match + proxy; "var ",
""
);
return match + `ModAPI.hooks._rippedInterfaceMap[\`${shortPrefix}\`]=${shortPrefix};` + proxy;
} }
); );
//Edge cases. sigh //Edge cases. sigh
//Done: add support for static properties on classes with constructors like this: function nmcg_GuiMainMenu() { //Done: add support for static properties on classes with constructors like this: function nmcg_GuiMainMenu() {
patchedFile = patchedFile.replaceAll( patchedFile = patchedFile.replaceAll(
/function [a-z]+?_([a-zA-Z0-9\$]+?)\(\) \{/gm, /function [a-z]+?_([a-zA-Z0-9\$]+?)\(\) \{/gm,
(match) => { (match) => {
@ -335,7 +340,7 @@ document.querySelector("#giveme").addEventListener("click", () => {
} else if (globalThis.doShronk) { } else if (globalThis.doShronk) {
patchedFile = await shronk(patchedFile); patchedFile = await shronk(patchedFile);
} }
patchedFile.replace(`{"._|_libserverside_|_."}`, ""); patchedFile.replace(`{"._|_libserverside_|_."}`, "");
var blob = new Blob([patchedFile], { type: file.type }); var blob = new Blob([patchedFile], { type: file.type });
saveAs(blob, "processed." + fileType); saveAs(blob, "processed." + fileType);
@ -356,13 +361,13 @@ document.querySelector("#givemeserver").addEventListener("click", () => {
file.text().then(async (string) => { file.text().then(async (string) => {
var patchedFile = string; var patchedFile = string;
if (globalThis.doEaglerforge) { if (globalThis.doEaglerforge) {
patchedFile = await processClasses(patchedFile); patchedFile = await processClasses(patchedFile);
} else if (globalThis.doShronk) { } else if (globalThis.doShronk) {
patchedFile = await shronk(patchedFile); patchedFile = await shronk(patchedFile);
} }
patchedFile.replace(`{"._|_libserverside_|_."}`, `(${EFServer.toString()})()`); patchedFile.replace(`{"._|_libserverside_|_."}`, `(${EFServer.toString()})()`);
var blob = new Blob([patchedFile], { type: file.type }); var blob = new Blob([patchedFile], { type: file.type });
saveAs(blob, "efserver." + fileType); saveAs(blob, "efserver." + fileType);

View File

@ -269,6 +269,7 @@ globalThis.modapi_postinit = "(" + (() => {
ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors); ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors);
ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors); ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors);
ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap); ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap);
ModAPI.hooks._rippedInterfaceKeys = Object.keys(ModAPI.hooks._rippedInterfaceMap);
var compiledNames = new Set(); var compiledNames = new Set();
var metaMap = {}; var metaMap = {};
@ -302,10 +303,19 @@ globalThis.modapi_postinit = "(" + (() => {
} }
}); });
ModAPI.hooks._rippedInterfaceKeys.forEach(className => {
if (typeof className === "string" && className.length > 0) {
//Interfaces using $rt_classWithoutFields(0) and no constructors.
if (className && className.includes("_")) {
compiledNames.add(className);
}
}
});
//Initialise all compiled names into the class map //Initialise all compiled names into the class map
compiledNames.forEach(compiledName => { compiledNames.forEach(compiledName => {
var item = metaMap[compiledName]; var item = metaMap[compiledName] || ModAPI.hooks._rippedInterfaceMap[compiledName];
var classId = item?.$meta?.name || null; var classId = item?.$meta?.name || null;
if (!ModAPI.hooks._classMap[compiledName]) { if (!ModAPI.hooks._classMap[compiledName]) {
@ -321,7 +331,7 @@ globalThis.modapi_postinit = "(" + (() => {
"staticVariables": {}, "staticVariables": {},
"staticVariableNames": [], "staticVariableNames": [],
"class": item || null, "class": item || null,
"hasMeta": !!item, "hasMeta": !!(item?.$meta),
"instanceOf": function (object) { "instanceOf": function (object) {
try { try {
return ModAPI.hooks._teavm.$rt_isInstance(object, item || null); return ModAPI.hooks._teavm.$rt_isInstance(object, item || null);
@ -431,6 +441,13 @@ globalThis.modapi_postinit = "(" + (() => {
}; };
classFn.classObject = null; classFn.classObject = null;
} }
ModAPI.reflect.implements = function implements(classFn, reflectClass) {
classFn.$meta ||= {};
classFn.$meta.supertypes ||= [];
if (reflectClass && reflectClass.class) {
classFn.$meta.supertypes.push(reflectClass.class);
}
}
var reloadDeprecationWarnings = 0; var reloadDeprecationWarnings = 0;
const TeaVMArray_To_Recursive_BaseData_ProxyConf = { const TeaVMArray_To_Recursive_BaseData_ProxyConf = {