From 278a04724faca704a7d22272d0a7e91bbd8368de Mon Sep 17 00:00:00 2001 From: LatvianModder Date: Sat, 9 Dec 2023 16:06:46 +0200 Subject: [PATCH] Attempt at bandaid fixing concurrency issues --- .../dev/latvian/mods/rhino/ArrowFunction.java | 4 +- .../dev/latvian/mods/rhino/BoundFunction.java | 2 +- .../java/dev/latvian/mods/rhino/Context.java | 52 +++++++++++----- .../dev/latvian/mods/rhino/ES6Generator.java | 4 +- .../latvian/mods/rhino/InterfaceAdapter.java | 2 +- .../mods/rhino/InterpretedFunction.java | 4 +- .../dev/latvian/mods/rhino/Interpreter.java | 18 +++--- .../mods/rhino/IteratorLikeIterable.java | 2 +- .../dev/latvian/mods/rhino/NativeArray.java | 4 +- .../dev/latvian/mods/rhino/NativeMap.java | 2 +- .../dev/latvian/mods/rhino/NativeSet.java | 2 +- .../dev/latvian/mods/rhino/ScriptRuntime.java | 59 +++++-------------- .../mods/rhino/mod/util/MojangMappings.java | 5 ++ 13 files changed, 81 insertions(+), 79 deletions(-) diff --git a/common/src/main/java/dev/latvian/mods/rhino/ArrowFunction.java b/common/src/main/java/dev/latvian/mods/rhino/ArrowFunction.java index 8e6a5d86..1a8b30ea 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ArrowFunction.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ArrowFunction.java @@ -37,8 +37,8 @@ public ArrowFunction(Context cx, Scriptable scope, Callable targetFunction, Scri } @Override - public synchronized Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + Scriptable callThis = boundThis != null ? boundThis : cx.getTopCallOrThrow(); return targetFunction.call(cx, scope, callThis, args); } diff --git a/common/src/main/java/dev/latvian/mods/rhino/BoundFunction.java b/common/src/main/java/dev/latvian/mods/rhino/BoundFunction.java index 174fe5fd..0e35eaf6 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/BoundFunction.java +++ b/common/src/main/java/dev/latvian/mods/rhino/BoundFunction.java @@ -55,7 +55,7 @@ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scri @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] extraArgs) { - Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); + Scriptable callThis = boundThis != null ? boundThis : cx.getTopCallOrThrow(); return targetFunction.call(cx, scope, callThis, concat(boundArgs, extraArgs)); } diff --git a/common/src/main/java/dev/latvian/mods/rhino/Context.java b/common/src/main/java/dev/latvian/mods/rhino/Context.java index ea2eaedb..5915054d 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/Context.java +++ b/common/src/main/java/dev/latvian/mods/rhino/Context.java @@ -338,7 +338,7 @@ public static String getSourcePositionFromStack(Context cx, int[] linep) { // Generate an observer count on compiled code public boolean generateObserverCount = false; - Scriptable topCallScope; + private Scriptable topCallScope; boolean isContinuationsTopCall; NativeCall currentActivationCall; BaseFunction typeErrorThrower; @@ -358,12 +358,9 @@ public static String getSourcePositionFromStack(Context cx, int[] linep) { // It can be used to return the second uint32 result from function long scratchUint32; // It can be used to return the second Scriptable result from function - Scriptable scratchScriptable; + private Scriptable scratchScriptable; boolean isTopLevelStrict; - private Object sealKey; - private ErrorReporter errorReporter; private int maximumInterpreterStackDepth; - private Object propertyListeners; private Map threadLocalMap; private ClassLoader applicationClassLoader; @@ -385,10 +382,6 @@ public static String getSourcePositionFromStack(Context cx, int[] linep) { * Creates a new context. Provided as a preferred super constructor for * subclasses in place of the deprecated default public constructor. * - * @param factory the context factory associated with this context (most - * likely, the one that created the context). Can not be null. The context - * features are inherited from the factory, and the context will also - * otherwise use its factory's services. * @throws IllegalArgumentException if factory parameter is null. */ protected Context() { @@ -421,10 +414,7 @@ public final String getImplementationVersion() { * @see ErrorReporter */ public final ErrorReporter getErrorReporter() { - if (errorReporter == null) { - return DefaultErrorReporter.instance; - } - return errorReporter; + return DefaultErrorReporter.instance; } /** @@ -681,7 +671,7 @@ public Object callFunctionWithContinuations(Callable function, Scriptable scope, // Can only be applied to scripts throw new IllegalArgumentException("Function argument was not" + " created by interpreted mode "); } - if (ScriptRuntime.hasTopCall(this)) { + if (hasTopCallScope()) { throw new IllegalStateException("Cannot have any pending top " + "calls when executing a script with continuations"); } // Annotate so we can check later to ensure no java code in @@ -1368,4 +1358,38 @@ public final void setWrapFactory(WrapFactory wrapFactory) { } this.wrapFactory = wrapFactory; } + + public synchronized boolean hasTopCallScope() { + return topCallScope != null; + } + + public synchronized Scriptable getTopCallScope() { + return topCallScope; + } + + public synchronized Scriptable getTopCallOrThrow() { + if (topCallScope == null) { + throw new IllegalStateException(); + } + + return topCallScope; + } + + public synchronized void setTopCall(Scriptable scope) { + topCallScope = scope; + } + + public synchronized void storeScriptable(Scriptable value) { + // The previously stored scratchScriptable should be consumed + if (scratchScriptable != null) { + throw new IllegalStateException(); + } + scratchScriptable = value; + } + + public synchronized Scriptable lastStoredScriptable() { + Scriptable result = scratchScriptable; + scratchScriptable = null; + return result; + } } diff --git a/common/src/main/java/dev/latvian/mods/rhino/ES6Generator.java b/common/src/main/java/dev/latvian/mods/rhino/ES6Generator.java index f3f351ef..fee11b17 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ES6Generator.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ES6Generator.java @@ -150,7 +150,7 @@ private Scriptable resumeDelegee(Context cx, Scriptable scope, Object value) { Object[] nextArgs = Undefined.instance.equals(value) ? ScriptRuntime.EMPTY_OBJECTS : new Object[]{value}; Callable nextFn = ScriptRuntime.getPropFunctionAndThis(cx, scope, delegee, ES6Iterator.NEXT_METHOD); - Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx); + Scriptable nextThis = cx.lastStoredScriptable(); Object nr = nextFn.call(cx, scope, nextThis, nextArgs); Scriptable nextResult = ScriptableObject.ensureScriptable(nr, cx); @@ -178,7 +178,7 @@ private Scriptable resumeDelegeeThrow(Context cx, Scriptable scope, Object value try { // Delegate to "throw" method. If it's not defined we'll get an error here. Callable throwFn = ScriptRuntime.getPropFunctionAndThis(cx, scope, delegee, "throw"); - Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx); + Scriptable nextThis = cx.lastStoredScriptable(); Object throwResult = throwFn.call(cx, scope, nextThis, new Object[]{value}); if (ScriptRuntime.isIteratorDone(cx, throwResult)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/InterfaceAdapter.java b/common/src/main/java/dev/latvian/mods/rhino/InterfaceAdapter.java index 5076317d..b5e7d179 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/InterfaceAdapter.java +++ b/common/src/main/java/dev/latvian/mods/rhino/InterfaceAdapter.java @@ -27,7 +27,7 @@ static Object create(Context cx, Class cl, ScriptableObject object) { throw new IllegalArgumentException(); } - Scriptable topScope = ScriptRuntime.getTopCallScope(cx); + Scriptable topScope = cx.getTopCallOrThrow(); InterfaceAdapter adapter; adapter = (InterfaceAdapter) cx.getInterfaceAdapter(cl); if (adapter == null) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java b/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java index fc954f51..b40a4e69 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java +++ b/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java @@ -66,7 +66,7 @@ public String getFunctionName() { */ @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { + if (!cx.hasTopCallScope()) { return ScriptRuntime.doTopCall(cx, scope, this, thisObj, args, idata.isStrict); } return Interpreter.interpret(this, cx, scope, thisObj, args); @@ -78,7 +78,7 @@ public Object exec(Context cx, Scriptable scope) { // Can only be applied to scripts throw new IllegalStateException(); } - if (!ScriptRuntime.hasTopCall(cx)) { + if (!cx.hasTopCallScope()) { // It will go through "call" path. but they are equivalent return ScriptRuntime.doTopCall(cx, scope, this, scope, ScriptRuntime.EMPTY_OBJECTS, idata.isStrict); } diff --git a/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java b/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java index bcceda54..1234e0a1 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java +++ b/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java @@ -231,7 +231,7 @@ public boolean equals(Object other) { // in order to evaluate their attributes. //final Context cx = Context.enter(); //try { - if (ScriptRuntime.hasTopCall(localContext)) { + if (localContext.hasTopCallScope()) { return equalsInTopScope(otherCallFrame); } final Scriptable top = ScriptableObject.getTopLevelScope(scope); @@ -412,7 +412,7 @@ private static void initFunction(Context cx, Scriptable scope, InterpretedFuncti } static Object interpret(InterpretedFunction ifun, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { + if (!cx.hasTopCallScope()) { Kit.codeBug(); } @@ -445,7 +445,7 @@ public static Object resumeGenerator(Context cx, Scriptable scope, int operation } public static Object restartContinuation(NativeContinuation c, Context cx, Scriptable scope, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { + if (!cx.hasTopCallScope()) { return ScriptRuntime.doTopCall(cx, scope, c, null, args, cx.isTopLevelStrict); } @@ -935,7 +935,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl ++stackTop; stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(cx, frame.scope, stringReg); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = cx.lastStoredScriptable(); continue; case Icode_PROP_AND_THIS: { Object obj = stack[stackTop]; @@ -945,7 +945,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl // stringReg: property stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(cx, frame.scope, obj, stringReg); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = cx.lastStoredScriptable(); continue; } case Icode_ELEM_AND_THIS: { @@ -958,7 +958,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl id = ScriptRuntime.wrapNumber(sDbl[stackTop]); } stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(cx, frame.scope, obj, id); - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = cx.lastStoredScriptable(); continue; } case Icode_VALUE_AND_THIS: { @@ -968,7 +968,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl } stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(cx, value); ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + stack[stackTop] = cx.lastStoredScriptable(); continue; } case Icode_CALLSPECIAL: { @@ -2118,7 +2118,7 @@ private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame, in } if (applyThis == null) { // This covers the case of args[0] == (null|undefined) as well. - applyThis = ScriptRuntime.getTopCallScope(cx); + applyThis = cx.getTopCallOrThrow(); } if (op == Icode_TAIL_CALL) { exitFrame(cx, frame, null); @@ -2223,7 +2223,7 @@ public static NativeContinuation captureContinuation(Context cx) { private static NativeContinuation captureContinuation(Context cx, CallFrame frame, boolean requireContinuationsTopFrame) { NativeContinuation c = new NativeContinuation(); - ScriptRuntime.setObjectProtoAndParent(cx, ScriptRuntime.getTopCallScope(cx), c); + ScriptRuntime.setObjectProtoAndParent(cx, cx.getTopCallOrThrow(), c); // Make sure that all frames are frozen CallFrame x = frame; diff --git a/common/src/main/java/dev/latvian/mods/rhino/IteratorLikeIterable.java b/common/src/main/java/dev/latvian/mods/rhino/IteratorLikeIterable.java index a6f3f1b1..8621620c 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/IteratorLikeIterable.java +++ b/common/src/main/java/dev/latvian/mods/rhino/IteratorLikeIterable.java @@ -34,7 +34,7 @@ public IteratorLikeIterable(Context cx, Scriptable scope, Object target) { this.scope = scope; // This will throw if "next" is not a function or undefined next = ScriptRuntime.getPropFunctionAndThis(cx, scope, target, ES6Iterator.NEXT_METHOD); - iterator = ScriptRuntime.lastStoredScriptable(cx); + iterator = cx.lastStoredScriptable(); Object retObj = ScriptRuntime.getObjectPropNoWarn(cx, scope, target, ES6Iterator.RETURN_PROPERTY); // We only care about "return" if it is not null or undefined if ((retObj != null) && !Undefined.isUndefined(retObj)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/NativeArray.java b/common/src/main/java/dev/latvian/mods/rhino/NativeArray.java index 09db08c8..e51b4030 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/NativeArray.java +++ b/common/src/main/java/dev/latvian/mods/rhino/NativeArray.java @@ -462,7 +462,7 @@ private static String toStringHelper(Context cx, Scriptable scope, Scriptable th Callable fun; Scriptable funThis; fun = ScriptRuntime.getPropFunctionAndThis(cx, scope, elem, "toLocaleString"); - funThis = ScriptRuntime.lastStoredScriptable(cx); + funThis = cx.lastStoredScriptable(); elem = fun.call(cx, scope, funThis, ScriptRuntime.EMPTY_OBJECTS); } result.append(ScriptRuntime.toString(cx, elem)); @@ -586,7 +586,7 @@ private static Scriptable js_sort(final Context cx, final Scriptable scope, fina final Comparator comparator; if (args.length > 0 && Undefined.instance != args[0]) { final Callable jsCompareFunction = ScriptRuntime.getValueFunctionAndThis(cx, args[0]); - final Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx); + final Scriptable funThis = cx.lastStoredScriptable(); final Object[] cmpBuf = new Object[2]; // Buffer for cmp arguments comparator = new ElementComparator((x, y) -> { // This comparator is invoked only for non-undefined objects diff --git a/common/src/main/java/dev/latvian/mods/rhino/NativeMap.java b/common/src/main/java/dev/latvian/mods/rhino/NativeMap.java index 583938f1..becdef94 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/NativeMap.java +++ b/common/src/main/java/dev/latvian/mods/rhino/NativeMap.java @@ -64,7 +64,7 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, // so that we can get our own prototype. ScriptableObject dummy = ensureScriptableObject(cx.newObject(scope, map.getClassName()), cx); final Callable set = ScriptRuntime.getPropFunctionAndThis(cx, scope, dummy.getPrototype(cx), "set"); - ScriptRuntime.lastStoredScriptable(cx); + cx.lastStoredScriptable(); // Finally, run through all the iterated values and add them! try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/NativeSet.java b/common/src/main/java/dev/latvian/mods/rhino/NativeSet.java index 08130247..6b943b40 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/NativeSet.java +++ b/common/src/main/java/dev/latvian/mods/rhino/NativeSet.java @@ -66,7 +66,7 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject set, ScriptableObject dummy = ensureScriptableObject(cx.newObject(scope, set.getClassName()), cx); final Callable add = ScriptRuntime.getPropFunctionAndThis(cx, scope, dummy.getPrototype(cx), "add"); // Clean up the value left around by the previous function - ScriptRuntime.lastStoredScriptable(cx); + cx.lastStoredScriptable(); // Finally, run through all the iterated values and add them! try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java b/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java index 88cc76b3..3fdcbe1b 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java @@ -159,7 +159,7 @@ public int getLength() { return 0; } }; - ScriptRuntime.setFunctionProtoAndParent(cx, cx.topCallScope, thrower); + ScriptRuntime.setFunctionProtoAndParent(cx, cx.getTopCallScope(), thrower); thrower.preventExtensions(); cx.typeErrorThrower = thrower; } @@ -887,7 +887,7 @@ public static Scriptable toObjectOrNull(Context cx, Object obj) { if (obj instanceof Scriptable) { return (Scriptable) obj; } else if (obj != null && obj != Undefined.instance) { - return toObject(cx, getTopCallScope(cx), obj); + return toObject(cx, cx.getTopCallOrThrow(), obj); } return null; } @@ -1527,7 +1527,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope, Scriptable pa if (!(result instanceof Callable)) { throw notFunctionError(cx, result, name); } - storeScriptable(cx, thisObj); + cx.storeScriptable(thisObj); } return result; @@ -1719,7 +1719,7 @@ public static Callable getNameFunctionAndThis(Context cx, Scriptable scope, Stri } // Top scope is not NativeWith or NativeCall => thisObj == scope Scriptable thisObj = scope; - storeScriptable(cx, thisObj); + cx.storeScriptable(thisObj); return (Callable) result; } @@ -1763,7 +1763,7 @@ public static Callable getElemFunctionAndThis(Context cx, Scriptable scope, Obje throw notFunctionError(cx, value, elem); } - storeScriptable(cx, thisObj); + cx.storeScriptable(thisObj); return (Callable) value; } @@ -1796,7 +1796,7 @@ private static Callable getPropFunctionAndThisHelper(Context cx, Scriptable this throw notFunctionError(cx, thisObj, value, property); } - storeScriptable(cx, thisObj); + cx.storeScriptable(thisObj); return (Callable) value; } @@ -1816,12 +1816,11 @@ public static Callable getValueFunctionAndThis(Context cx, Object value) { if (f instanceof Scriptable) { thisObj = ((Scriptable) f).getParentScope(); } + if (thisObj == null) { - if (cx.topCallScope == null) { - throw new IllegalStateException(); - } - thisObj = cx.topCallScope; + thisObj = cx.getTopCallOrThrow(); } + if (thisObj.getParentScope() != null) { if (thisObj instanceof NativeWith) { // functions defined inside with should have with target @@ -1831,7 +1830,7 @@ public static Callable getValueFunctionAndThis(Context cx, Object value) { thisObj = ScriptableObject.getTopLevelScope(thisObj); } } - storeScriptable(cx, thisObj); + cx.storeScriptable(thisObj); return f; } @@ -1842,7 +1841,7 @@ public static Callable getValueFunctionAndThis(Context cx, Object value) { */ public static Object callIterator(Context cx, Scriptable scope, Object obj) { final Callable getIterator = ScriptRuntime.getElemFunctionAndThis(cx, scope, obj, SymbolKey.ITERATOR); - final Scriptable iterable = ScriptRuntime.lastStoredScriptable(cx); + final Scriptable iterable = cx.lastStoredScriptable(); return getIterator.call(cx, scope, iterable, ScriptRuntime.EMPTY_OBJECTS); } @@ -2591,34 +2590,22 @@ public static boolean cmp_LE(Context cx, Object val1, Object val2) { return d1 <= d2; } - public static boolean hasTopCall(Context cx) { - return (cx.topCallScope != null); - } - - public static Scriptable getTopCallScope(Context cx) { - Scriptable scope = cx.topCallScope; - if (scope == null) { - throw new IllegalStateException(); - } - return scope; - } - public static Object doTopCall(Context cx, Scriptable scope, Callable callable, Scriptable thisObj, Object[] args, boolean isTopLevelStrict) { if (scope == null) { throw new IllegalArgumentException(); } - if (cx.topCallScope != null) { + if (cx.hasTopCallScope()) { throw new IllegalStateException(); } Object result; - cx.topCallScope = ScriptableObject.getTopLevelScope(scope); + cx.setTopCall(ScriptableObject.getTopLevelScope(scope)); boolean previousTopLevelStrict = cx.isTopLevelStrict; cx.isTopLevelStrict = isTopLevelStrict; try { result = cx.doTopCall(callable, scope, thisObj, args); } finally { - cx.topCallScope = null; + cx.setTopCall(null); // Cleanup cached references cx.isTopLevelStrict = previousTopLevelStrict; @@ -2632,7 +2619,7 @@ public static Object doTopCall(Context cx, Scriptable scope, Callable callable, } public static void initScript(Context cx, Scriptable scope, NativeFunction funObj, Scriptable thisObj, boolean evalScript) { - if (cx.topCallScope == null) { + if (!cx.hasTopCallScope()) { throw new IllegalStateException(); } @@ -2678,7 +2665,7 @@ public static Scriptable createArrowFunctionActivation(Context cx, Scriptable sc } public static void enterActivationFunction(Context cx, Scriptable scope) { - if (cx.topCallScope == null) { + if (!cx.hasTopCallScope()) { throw new IllegalStateException(); } NativeCall call = (NativeCall) scope; @@ -3243,20 +3230,6 @@ public static long lastUint32Result(Context cx) { return value; } - private static void storeScriptable(Context cx, Scriptable value) { - // The previously stored scratchScriptable should be consumed - if (cx.scratchScriptable != null) { - throw new IllegalStateException(); - } - cx.scratchScriptable = value; - } - - public static Scriptable lastStoredScriptable(Context cx) { - Scriptable result = cx.scratchScriptable; - cx.scratchScriptable = null; - return result; - } - static String makeUrlForGeneratedScript(boolean isEval, String masterScriptUrl, int masterScriptLine) { if (isEval) { return masterScriptUrl + '#' + masterScriptLine + "(eval)"; diff --git a/common/src/main/java/dev/latvian/mods/rhino/mod/util/MojangMappings.java b/common/src/main/java/dev/latvian/mods/rhino/mod/util/MojangMappings.java index 65032e7a..df5adc70 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/mod/util/MojangMappings.java +++ b/common/src/main/java/dev/latvian/mods/rhino/mod/util/MojangMappings.java @@ -7,6 +7,7 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -156,6 +157,10 @@ public ClassDef getClass(String name) { return mmc != null ? mmc : classes.get(name); } + public Collection getMMClasses() { + return classesMM.values(); + } + public TypeDef getType(String string) { int array = 0;