Merge pull request #22 from ZXMushroom63/main

Add NoReflect generator tool
This commit is contained in:
radmanplays 2024-05-22 18:50:49 +03:30 committed by GitHub
commit 735f748f33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 747 additions and 0 deletions

188
NoReflect/FileSaver.js Normal file
View File

@ -0,0 +1,188 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.3.2
* 2016-06-16 18:25:19
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function(node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
, throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
, arbitrary_revoke_timeout = 1000 * 40 // in ms
, revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
setTimeout(revoker, arbitrary_revoke_timeout);
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function(blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
}
return blob;
}
, FileSaver = function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, force = type === force_saveable_type
, object_url
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function() {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
var popup = view.open(url, '_blank');
if(!popup) view.location.href = url;
url=undefined; // release reference before dispatching
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (!object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (force) {
view.location.href = object_url;
} else {
var opened = view.open(object_url, "_blank");
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
view.location.href = object_url;
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
;
filesaver.readyState = filesaver.INIT;
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
setTimeout(function() {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
fs_error();
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name, no_auto_bom) {
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
}
;
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function(blob, name, no_auto_bom) {
name = name || blob.name || "download";
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name);
};
}
FS_proto.abort = function(){};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
define("FileSaver.js", function() {
return saveAs;
});
}

15
NoReflect/PLReflect.java Normal file
View File

@ -0,0 +1,15 @@
import net.eaglerforge.api.*;
import java.util.ArrayList;
public class PLReflect {
public static ModData makeModData() {
ModData plReflectGlobal = new ModData();
ArrayList<BaseData> reflectProfiles = new ArrayList<BaseData>();
%classdefs%
BaseData[] reflectProfilesArr = new BaseData[reflectProfiles.size()];
for (int i = 0; i < reflectProfilesArr.length; i++) {
reflectProfilesArr[i] = reflectProfiles.get(i);
}
plReflectGlobal.set("classes", reflectProfilesArr);
return plReflectGlobal;
}
}

260
NoReflect/generate.js Normal file
View File

@ -0,0 +1,260 @@
const templateClassdef = `
//CLASSDEF FOR %classname%
BaseData reflect_%classname% = new ModData();
ArrayList<BaseData> reflect_%classname%_constructors = new ArrayList<BaseData>();
%constructordefs%
BaseData[] reflect_%classname%_constructors_arr = new BaseData[reflectProfiles.size()];
for (int i = 0; i < reflect_%classname%_constructors_arr.length; i++) {
reflect_%classname%_constructors_arr[i] = reflect_%classname%_constructors.get(i);
}
ArrayList<BaseData> reflect_%classname%_methods = new ArrayList<BaseData>();
%methoddefs%
BaseData[] reflect_%classname%_methods_arr = new BaseData[reflectProfiles.size()];
for (int i = 0; i < reflect_%classname%_methods_arr.length; i++) {
reflect_%classname%_methods_arr[i] = reflect_%classname%_methods.get(i);
}
reflect_%classname%.set("constructors", reflect_%classname%_constructors_arr);
reflect_%classname%.set("methods", reflect_%classname%_methods_arr);
reflect_%classname%.set("className", "%classname%");
reflect_%classname%.set("class", %classname%.class);
reflectProfiles.add(reflect_%classname%);
`;
//IXCVVIX
//CXVIIVX
//MVVMCXI
const templateConstructor = `
BaseData reflect_%classname%_constructor_%constructorname%_%idx% = new ModData();
reflect_%classname%_constructor_%constructorname%_%idx%.set("returnType", %returntype%);
reflect_%classname%_constructor_%constructorname%_%idx%.set("argnames", %argkeys%);
reflect_%classname%_constructor_%constructorname%_%idx%.set("argtypes", %argvalues%);
reflect_%classname%_constructors_%idx%.%constructorimpl%
reflect_%classname%_constructors.add(reflect_%classname%_constructor_%constructorname%_%idx%);
`;
const templateMethod = `
BaseData reflect_%classname%_method_%methodname%_%idx% = new ModData();
reflect_%classname%_method_%methodname%_%idx%.set("methodName", "%methodname%");
reflect_%classname%_method_%methodname%_%idx%.set("returnType", %returntype%);
reflect_%classname%_method_%methodname%_%idx%.set("static", %static%);
reflect_%classname%_method_%methodname%_%idx%.set("argnames", %argkeys%);
reflect_%classname%_method_%methodname%_%idx%.set("argtypes", %argvalues%);
reflect_%classname%_method_%methodname%_%idx%.%methodimpl%
reflect_%classname%_methods.add(reflect_%classname%_method_%methodname%_%idx%);
`;
const templateManager = `
import net.eaglerforge.api.*;
import java.util.ArrayList;
public class PLReflect {
public static ModData makeModData() {
ModData plReflectGlobal = new ModData();
ArrayList<BaseData> reflectProfiles = new ArrayList<BaseData>();
%classdefs%
BaseData[] reflectProfilesArr = new BaseData[reflectProfiles.size()];
for (int i = 0; i < reflectProfilesArr.length; i++) {
reflectProfilesArr[i] = reflectProfiles.get(i);
}
plReflectGlobal.set("classes", reflectProfilesArr);
return plReflectGlobal;
}
}`;
function wait(d) {
return new Promise((res, rej)=>{
setTimeout(()=>{res()}, d*1000);
});
}
function logClear() {
document.querySelector("#logs").innerText = "";
}
function logTxt(txt) {
if (document.querySelector("#logs").innerText === "") {
document.querySelector("#logs").innerText += txt;
} else {
document.querySelector("#logs").innerText += "\n" + txt;
}
document.querySelector("#logs").scrollTop = document.querySelector("#logs").scrollHeight;
}
function process(file, reader, classDataDump, className) {
return new Promise((res, rej)=>{
reader.addEventListener("load", ()=>{
var output = reader.result;
classDataDump[className] = (reconJ(output, className));
res(output);
});
reader.readAsText(file);
});
}
function createManagerFile(managerTemplate, config, zip, dataDump, classIdMap) {
var manager = managerTemplate;
var filePath = config.managerFile.replaceAll(".", "/") + ".java";
for (let i = 0; i < config.targetFiles.length; i++) {
manager = `import ${config.targetFiles[i]}` + ";\n" + manager;
}
var imports = [];
var classText = "";
var classes = Object.keys(dataDump);
for (let i = 0; i < classes.length; i++) {
const className = classes[i];
imports = [...new Set(imports.concat(dataDump[className].usedClasses))];
var tmpClassText = templateClassdef;
tmpClassText = tmpClassText.replaceAll("%classname%", className);
var constructorText = "";
for (let i = 0; i < dataDump[className].constructors.length; i++) {
const constructor = dataDump[className].constructors[i];
var tmpConstructorText = templateConstructor;
tmpConstructorText = tmpConstructorText.replaceAll("%classname%", className);
tmpConstructorText = tmpConstructorText.replaceAll("%idx%", constructor.idx);
tmpConstructorText = tmpConstructorText.replaceAll("%constructorname%", constructor.name);
tmpConstructorText = tmpConstructorText.replaceAll("%returntype%", "\""+className+"\"");
tmpConstructorText = tmpConstructorText.replaceAll("%argkeys%", `new String[]{${(()=>{
var txt = "";
var argumentKeys = Object.keys(constructor.arguments);
for (let i = 0; i < argumentKeys.length; i++) {
const k = argumentKeys[i];
txt += `"${k}"`;
if (i !== argumentKeys.length - 1) {
txt += ", ";
}
}
return txt;
})()}}`);
tmpConstructorText = tmpConstructorText.replaceAll("%argvalues%", `new String[]{${(()=>{
var txt = "";
var argumentKeys = Object.keys(constructor.arguments);
for (let i = 0; i < argumentKeys.length; i++) {
const k = argumentKeys[i];
txt += `"${constructor.arguments[k]}"`;
if (i !== argumentKeys.length - 1) {
txt += ", ";
}
}
return txt;
})()}}`);
tmpConstructorText = tmpConstructorText.replaceAll("%constructorimpl%", constructor.impl);
constructorText += tmpConstructorText;
}
tmpClassText = tmpClassText.replaceAll("%constructordefs%", constructorText);
var methodText = "";
for (let i = 0; i < dataDump[className].methods.length; i++) {
const method = dataDump[className].methods[i];
var tmpMethodText = templateMethod;
tmpMethodText = tmpMethodText.replaceAll("%classname%", className);
tmpMethodText = tmpMethodText.replaceAll("%idx%", method.idx);
tmpMethodText = tmpMethodText.replaceAll("%static%", method.isStatic);
tmpMethodText = tmpMethodText.replaceAll("%methodname%", method.name);
tmpMethodText = tmpMethodText.replaceAll("%returntype%", "\""+className+"\"");
tmpMethodText = tmpMethodText.replaceAll("%argkeys%", `new String[]{${(()=>{
var txt = "";
var argumentKeys = Object.keys(method.arguments);
for (let i = 0; i < argumentKeys.length; i++) {
const k = argumentKeys[i];
txt += `"${k}"`;
if (i !== argumentKeys.length - 1) {
txt += ", ";
}
}
return txt;
})()}}`);
tmpMethodText = tmpMethodText.replaceAll("%argvalues%", `new String[]{${(()=>{
var txt = "";
var argumentKeys = Object.keys(method.arguments);
for (let i = 0; i < argumentKeys.length; i++) {
const k = argumentKeys[i];
txt += `"${method.arguments[k]}"`;
if (i !== argumentKeys.length - 1) {
txt += ", ";
}
}
return txt;
})()}}`);
tmpMethodText = tmpMethodText.replaceAll("%methodimpl%", method.impl);
methodText += tmpMethodText;
}
tmpClassText = tmpClassText.replaceAll("%methoddefs%", methodText);
classText += tmpClassText;
}
for (let i = 0; i < config.imports.length; i++) {
manager = `import ${config.imports[i]}` + ";\n" + manager;
logTxt(`Force imported classid: ${config.imports[i]}`);
}
if (config.attemptAutoImport) {
for (let i = 0; i < imports.length; i++) {
if (classIdMap.has(imports[i])) {
manager = `import ${classIdMap.get(imports[i])}` + ";\n" + manager;
logTxt(`Imported classid: ${classIdMap.get(imports[i])}`);
}
}
}
manager = `package ${config.managerFile.match(/(.*)(?=\.[^.]*$)/g)[0]}` + ";\n" + manager;
manager = manager.replaceAll("%classdefs%", classText);
zip.file(filePath, manager);
}
async function generate(fileList) {
var classToLocationMap = new Map();
var cfg;
var output = new JSZip();
const reader = new FileReader();
var classDataDump = {};
logClear();
logTxt("[INIT] Build @ "+(new Date()));
if (!fileList || fileList.length === 0) {
logTxt("[ERROR] Filelist is empty.")
return;
}
try {
cfg = JSON.parse(document.querySelector("#config").value.trim());
} catch (e) {
logTxt("[ERROR] Invalid config.");
return;
}
if (!cfg.targetFiles) {
logTxt("[ERROR] Invalid config.");
}
for (let i = 0; i < fileList.length; i++) {
const file = fileList[i];
if (file.webkitRelativePath.endsWith(".java")) {
var classId = file.webkitRelativePath.replaceAll("java/", "").replaceAll(".java", "").replaceAll("/", ".");
var className = classId.split(".")[classId.split(".").length - 1];
classToLocationMap.set(className, classId);
if (cfg.targetFiles.includes(classId)) {
logTxt("Found "+classId+" ["+file.name+"], processing...");
var javaFileContent = await process(file, reader, classDataDump, className);
if (cfg.includeReadFiles) {
output.file(file.webkitRelativePath.replaceAll("java/", ""), javaFileContent);
}
}
}
}
logTxt(`Creating manager file...`);
createManagerFile(templateManager, cfg, output, classDataDump, classToLocationMap);
logTxt(`Writing log.txt...`);
output.file("log.txt", document.querySelector("#logs").innerText);
output.generateAsync({type:"blob"}).then(function(content) {
saveAs(content, "patch.zip");
});
}
window.addEventListener("load", ()=>{
document.querySelector('#generate').addEventListener("click", ()=>{
generate(document.querySelector('#data').files);
});
logClear();
logTxt("//Upload the ./src/main/java folder and press generate to begin hook generation");
});

117
NoReflect/index.html Normal file
View File

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>♦️ NoReflect Generator WebUI</title>
<style>
h1 {
text-shadow: 0px 0px 3px;
color: lime;
}
html, body {
font-family: monospace;
background-color: rgb(0, 0, 15);
}
textarea {
border: 1px solid lime;
box-shadow: 0px 0px 3px lime;
background-color: transparent;
color: lime;
text-shadow: 0px 0px 3px;
min-height: 10rem;
min-width: 50rem;
font-size: 0.8rem;
}
textarea:focus {
outline: 0;
}
input[type=file] {
color: lime;
text-shadow: 0px 0px 3px;
}
input[type=file]::-webkit-file-upload-button {
color: red;
text-shadow: 0px 0px 3px;
background-color: transparent;
border: 1px solid red;
box-shadow: 0px 0px 6px red;
cursor: pointer;
}
#generate {
color: lime;
text-shadow: 0px 0px 3px;
background-color: transparent;
border: 1px solid lime;
box-shadow: 0px 0px 3px lime;
cursor: pointer;
}
#generate:active {
box-shadow: 0px 0px 0px lime;
}
#logs {
max-height: 16rem;
overflow-y: auto;
overflow-x: hidden;
margin-top: 2rem;
color: orange;
text-shadow: 0px 0px 2px;
}
.crt::after {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(18, 16, 16, 0.1);
opacity: 0;
z-index: 2;
pointer-events: none;
}
.crt::before {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
z-index: 2;
background-size: 100% 2px, 3px 100%;
pointer-events: none;
}
</style>
<script src="FileSaver.js"></script>
<script src="jszip.min.js"></script>
<script src="javaRecon.js"></script>
<script src="generate.js"></script>
</head>
<body class="crt">
<h1>> NoReflect Generator WebUI ♦️</h1>
<br>
<span>Config</span>
<br>
<textarea id="config" spellcheck="false">
{
"targetFiles": ["net.minecraft.item.ItemStack", "net.minecraft.client.Minecraft"],
"imports": ["java.lang.String"],
"managerFile": "net.eaglerforge.reflect.PLReflect",
"includeReadFiles": false,
"attemptAutoImport": true
}
</textarea>
<br><br>
<input id="data" type="file" webkitdirectory>
<br><br>
<button id="generate">
Generate
</button>
<div id="logs">
//Loading...
</div>
</body>
</html>

142
NoReflect/javaRecon.js Normal file
View File

@ -0,0 +1,142 @@
const getStatements = {
"int": "params.getInt(%s)",
"String": "params.getString(%s)",
"boolean": "params.getBoolean(%s)",
"float": "params.getFloat(%s)",
"byte": "params.getByte(%s)",
"char": "params.getChar(%s)",
"double": "params.getDouble(%s)",
}
const callbackStatements = {
"boolean": "setCallbackBooleanWithDataArg",
"int": "setCallbackIntWithDataArg",
"String": "setCallbackStringWithDataArg",
"double": "setCallbackDoubleWithDataArg",
"float": "setCallbackFloatWithDataArg",
}
const getStatementsTypes = Object.keys(getStatements);
const callbackStatementsTypes = Object.keys(callbackStatements);
function reconJ(java, className) {
var usedClasses = [];
var javaText = java;
javaText = javaText.replace(/\/\/.*$/gm, '');
javaText = javaText.replace(/\/\*[\s\S]*?\*\//gm, '');
let constructorRegex = /(public|protected|private|static|\s) +(\w+) *\(([^)]*)\)/g;
let constructors = [...javaText.matchAll(constructorRegex).filter((line)=>{
return !line[0].includes(" private ") && !line[0].includes(" protected ");
})];
let constructorDetails = constructors.map((constructor) => {
let constructorName = constructor[2];
let argumentString = constructor[3];
let arguments = {};
if (argumentString.trim().length > 0) {
let argumentList = argumentString.split(",");
argumentList.forEach((argument) => {
let [type, name] = argument.trim().split(" ");
arguments[name] = type;
});
}
let argStr = "";
var argumentsKeys = Object.keys(arguments);
for (let i = 0; i < argumentsKeys.length; i++) {
const key = argumentsKeys[i];
if (!getStatementsTypes.includes(arguments[key])) {
usedClasses.push(arguments[key]);
}
argStr += `(${arguments[key]}) ${getStatementsTypes.includes(arguments[key]) ? getStatements[arguments[key]].replaceAll("%s", "\""+key+"\"") : `params.getReflective("${key}")`}`;
if (i !== argumentsKeys.length - 1) {
argStr += ", "
}
}
let impl = `setCallbackReflectiveWithArgs("${constructorName}", (BaseData params) -> {
return new ${className}(${argStr});
});
`;
return {
name: constructorName,
idx: constructors.indexOf(constructor),
arguments: arguments,
impl: impl
};
});
let methodRegex = /(public|static|\s)* +([\w\<\>\[\]]+)\s+(\w+) *\(([^)]*)\)/g;
let methods = [...javaText.matchAll(methodRegex).filter((line)=>{
return !line[0].includes("> ") && !line[0].startsWith(" else ") && !line[0].startsWith(" new ") && !line[0].includes(" private ") && !line[0].includes(" protected ");
//Doesn't support Type<Subtype> yet
})];
let methodDetails = methods.map((method) => {
let isStatic = `${method[1]}`.includes("static");
let returnType = method[2];
let methodName = method[3];
let argumentString = method[4];
let arguments = {};
if (argumentString.trim().length > 0) {
let argumentList = argumentString.split(",");
argumentList.forEach((argument) => {
let [type, name] = argument.trim().split(" ");
arguments[name] = type;
});
}
let argStr = "";
var argumentsKeys = Object.keys(arguments);
for (let i = 0; i < argumentsKeys.length; i++) {
const key = argumentsKeys[i];
if (!getStatementsTypes.includes(arguments[key])) {
usedClasses.push(arguments[key]);
}
argStr += `(${arguments[key]}) ${getStatementsTypes.includes(arguments[key]) ? getStatements[arguments[key]].replaceAll("%s", "\""+key+"\"") : `params.getReflective("${key}")`}`;
if (i !== argumentsKeys.length - 1) {
argStr += ", "
}
}
let prefix = isStatic ? className : `((${className}) params.get("_self"))`;
let impl;
if (returnType === "void") {
impl = `setCallbackVoidWithArgs("${methodName}", (BaseData params) -> {
${prefix}.${methodName}(${argStr});
});
`;
} else if (callbackStatementsTypes.includes(returnType)) {
impl = `${callbackStatements[returnType]}("${methodName}", (BaseData params) -> {
return (${returnType}) ${prefix}.${methodName}(${argStr});
});
`;
} else {
usedClasses.push(returnType);
impl = `setCallbackReflectiveWithArgs("${methodName}", (BaseData params) -> {
return (${returnType}) ${prefix}.${methodName}(${argStr});
});
`;
}
return {
name: methodName,
returnType: returnType,
isStatic: isStatic,
arguments: arguments,
impl: impl,
idx: methods.indexOf(method)
};
});
return {
className: className,
constructors: constructorDetails,
methods: methodDetails,
usedClasses: [...new Set(usedClasses)]
}
}

13
NoReflect/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
function reflect_hook() {
var ModAPI = window.ModAPI || {};
//Todo
var reflectInternals = ModAPI.__reflect_internals;
//Todo
ModAPI.javaClass = function javaClass(className) {
//Todo
}
//Todo
}