From 8c7aee107793d35f281de2817bf543dd007d3689 Mon Sep 17 00:00:00 2001 From: LatvianModder Date: Wed, 22 May 2024 13:37:07 +0300 Subject: [PATCH] Implremented generics for reverse, javaToJS methods --- .../java/dev/latvian/mods/rhino/Context.java | 71 ++++++++++++++++--- .../latvian/mods/rhino/ContextFactory.java | 52 +------------- .../latvian/mods/rhino/NativeJavaList.java | 22 +++--- .../dev/latvian/mods/rhino/NativeJavaMap.java | 13 +--- .../mods/rhino/{util => type}/TypeUtils.java | 4 +- .../rhino/util/CustomJavaToJsWrapper.java | 4 +- .../util/CustomJavaToJsWrapperProvider.java | 11 --- .../CustomJavaToJsWrapperProviderHolder.java | 23 ------ .../mods/rhino/util/ValueUnwrapper.java | 11 --- .../mods/rhino/util/wrap/TypeWrappers.java | 4 +- .../mods/rhino/test/GenericsTests.java | 5 ++ .../dev/latvian/mods/rhino/test/Holder.java | 21 ++++++ .../latvian/mods/rhino/test/RhinoTest.java | 1 + .../latvian/mods/rhino/test/TestConsole.java | 4 ++ .../latvian/mods/rhino/test/TestContext.java | 3 - .../latvian/mods/rhino/test/WithContext.java | 2 +- 16 files changed, 110 insertions(+), 141 deletions(-) rename src/main/java/dev/latvian/mods/rhino/{util => type}/TypeUtils.java (92%) delete mode 100644 src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProvider.java delete mode 100644 src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProviderHolder.java delete mode 100644 src/main/java/dev/latvian/mods/rhino/util/ValueUnwrapper.java create mode 100644 src/test/java/dev/latvian/mods/rhino/test/Holder.java diff --git a/src/main/java/dev/latvian/mods/rhino/Context.java b/src/main/java/dev/latvian/mods/rhino/Context.java index 1f5276cd..6f2e5012 100644 --- a/src/main/java/dev/latvian/mods/rhino/Context.java +++ b/src/main/java/dev/latvian/mods/rhino/Context.java @@ -12,11 +12,11 @@ import dev.latvian.mods.rhino.ast.ScriptNode; import dev.latvian.mods.rhino.classfile.ClassFileWriter.ClassFileFormatException; import dev.latvian.mods.rhino.regexp.RegExp; +import dev.latvian.mods.rhino.type.TypeUtils; import dev.latvian.mods.rhino.util.ArrayValueProvider; import dev.latvian.mods.rhino.util.ClassVisibilityContext; import dev.latvian.mods.rhino.util.CustomJavaToJsWrapper; import dev.latvian.mods.rhino.util.JavaSetWrapper; -import dev.latvian.mods.rhino.util.TypeUtils; import dev.latvian.mods.rhino.util.wrap.TypeWrapperFactory; import org.jetbrains.annotations.Nullable; @@ -1134,21 +1134,62 @@ public Scriptable wrapJavaClass(Scriptable scope, Class javaClass) { */ public Scriptable wrapAsJavaObject(Scriptable scope, Object javaObject, Class staticType, Type genericType) { if (javaObject instanceof CustomJavaToJsWrapper w) { - return w.convertJavaToJs(this, scope, staticType); + return w.convertJavaToJs(this, scope, staticType, genericType); } - CustomJavaToJsWrapper w = factory.wrapCustomJavaToJs(javaObject); + if (javaObject instanceof Map map) { + Class kType = null; + Type kGenericType = null; + Class vType = null; + Type vGenericType = null; - if (w != null) { - return w.convertJavaToJs(this, scope, staticType); - } + if (staticType != null && genericType instanceof ParameterizedType pt) { + var types = pt.getActualTypeArguments(); + var kRaw = types.length == 2 ? TypeUtils.getRawType(types[0]) : Object.class; + var vRaw = types.length == 2 ? TypeUtils.getRawType(types[1]) : Object.class; - if (javaObject instanceof Map map) { - return new NativeJavaMap(this, scope, map, map); + if (kRaw != null && kRaw != Object.class) { + kType = kRaw; + kGenericType = types[0]; + } + + if (vRaw != null && vRaw != Object.class) { + vType = vRaw; + vGenericType = types[1]; + } + } + + return new NativeJavaMap(this, scope, map, map, kType, kGenericType, vType, vGenericType); } else if (javaObject instanceof List list) { - return new NativeJavaList(this, scope, list, list); + Class lType = null; + Type lGenericType = null; + + if (staticType != null && genericType instanceof ParameterizedType pt) { + var types = pt.getActualTypeArguments(); + var raw = types.length == 1 ? TypeUtils.getRawType(types[0]) : Object.class; + + if (raw != null && raw != Object.class) { + lType = raw; + lGenericType = pt.getActualTypeArguments()[0]; + } + } + + return new NativeJavaList(this, scope, list, list, lType, lGenericType); } else if (javaObject instanceof Set set) { - return new NativeJavaList(this, scope, set, new JavaSetWrapper<>(set)); + Class lType = null; + Type lGenericType = null; + + if (staticType != null && genericType instanceof ParameterizedType pt) { + var types = pt.getActualTypeArguments(); + var raw = types.length == 1 ? TypeUtils.getRawType(types[0]) : Object.class; + + if (raw != null && raw != Object.class) { + lType = raw; + lGenericType = pt.getActualTypeArguments()[0]; + } + } + + return new NativeJavaList(this, scope, set, new JavaSetWrapper<>(set), lType, lGenericType); } // TODO: Wrap Gson @@ -1356,12 +1397,16 @@ public Object createInterfaceAdapter(Class type, Type genericType, Scriptable } public Object javaToJS(Object value, Scriptable scope) { + return javaToJS(value, scope, null, null); + } + + public Object javaToJS(Object value, Scriptable scope, @Nullable Class target, @Nullable Type genericTarget) { if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof Scriptable) { return value; } else if (value instanceof Character) { return String.valueOf(((Character) value).charValue()); } else { - return wrap(scope, value, null, null); + return wrap(scope, value, target, genericTarget); } } @@ -1497,6 +1542,10 @@ protected Object internalJsToJava(Object from, Class target, Type genericTarg Object unwrappedValue = Wrapper.unwrapped(from); + if (unwrappedValue instanceof TypeWrapperFactory f) { + return f.wrap(this, unwrappedValue, target, genericTarget); + } + TypeWrapperFactory typeWrapper = typeWrappers == null ? null : typeWrappers.getWrapperFactory(unwrappedValue, target, genericTarget); if (typeWrapper != null) { diff --git a/src/main/java/dev/latvian/mods/rhino/ContextFactory.java b/src/main/java/dev/latvian/mods/rhino/ContextFactory.java index 1dd5ffea..85179268 100644 --- a/src/main/java/dev/latvian/mods/rhino/ContextFactory.java +++ b/src/main/java/dev/latvian/mods/rhino/ContextFactory.java @@ -1,28 +1,14 @@ package dev.latvian.mods.rhino; -import dev.latvian.mods.rhino.util.CustomJavaToJsWrapper; -import dev.latvian.mods.rhino.util.CustomJavaToJsWrapperProvider; -import dev.latvian.mods.rhino.util.CustomJavaToJsWrapperProviderHolder; import dev.latvian.mods.rhino.util.wrap.TypeWrappers; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; public class ContextFactory { private final ThreadLocal currentContext; - private TypeWrappers typeWrappers; - private final List> customScriptableWrappers; - private final Map, CustomJavaToJsWrapperProvider> customScriptableWrapperCache; + private final TypeWrappers typeWrappers; public ContextFactory() { this.currentContext = ThreadLocal.withInitial(this::createContext); this.typeWrappers = new TypeWrappers(); - this.customScriptableWrappers = new ArrayList<>(); - this.customScriptableWrapperCache = new HashMap<>(); } protected Context createContext() { @@ -43,40 +29,4 @@ public Context enter() { public synchronized TypeWrappers getTypeWrappers() { return typeWrappers; } - - @Nullable - @SuppressWarnings("unchecked") - public synchronized CustomJavaToJsWrapper wrapCustomJavaToJs(Object javaObject) { - if (customScriptableWrappers.isEmpty()) { - return null; - } - - var provider = customScriptableWrapperCache.get(javaObject.getClass()); - - if (provider == null) { - for (CustomJavaToJsWrapperProviderHolder wrapper : customScriptableWrappers) { - provider = wrapper.create(javaObject); - - if (provider != null) { - break; - } - } - - if (provider == null) { - provider = CustomJavaToJsWrapperProvider.NONE; - } - - customScriptableWrapperCache.put(javaObject.getClass(), provider); - } - - return provider.create(javaObject); - } - - public void addCustomJavaToJsWrapper(Predicate predicate, CustomJavaToJsWrapperProvider provider) { - customScriptableWrappers.add(new CustomJavaToJsWrapperProviderHolder<>(predicate, provider)); - } - - public void addCustomJavaToJsWrapper(Class type, CustomJavaToJsWrapperProvider provider) { - addCustomJavaToJsWrapper(new CustomJavaToJsWrapperProviderHolder.PredicateFromClass<>(type), provider); - } } diff --git a/src/main/java/dev/latvian/mods/rhino/NativeJavaList.java b/src/main/java/dev/latvian/mods/rhino/NativeJavaList.java index 72f6de77..0fd1a465 100644 --- a/src/main/java/dev/latvian/mods/rhino/NativeJavaList.java +++ b/src/main/java/dev/latvian/mods/rhino/NativeJavaList.java @@ -6,7 +6,6 @@ package dev.latvian.mods.rhino; import dev.latvian.mods.rhino.util.Deletable; -import dev.latvian.mods.rhino.util.ValueUnwrapper; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Type; @@ -23,18 +22,12 @@ public class NativeJavaList extends NativeJavaObject { public final List list; public final Class listType; public final Type listGenericType; - private final ValueUnwrapper valueUnwrapper; - public NativeJavaList(Context cx, Scriptable scope, Object jo, List list, @Nullable Class listType, Type listGenericType, ValueUnwrapper valueUnwrapper) { + public NativeJavaList(Context cx, Scriptable scope, Object jo, List list, @Nullable Class listType, @Nullable Type listGenericType) { super(scope, jo, jo.getClass(), cx); this.list = list; this.listType = listType; this.listGenericType = listGenericType; - this.valueUnwrapper = valueUnwrapper; - } - - public NativeJavaList(Context cx, Scriptable scope, Object jo, List list) { - this(cx, scope, jo, list, null, null, ValueUnwrapper.DEFAULT); } @Override @@ -61,8 +54,9 @@ public boolean has(Context cx, Symbol key, Scriptable start) { @Override public Object get(Context cx, int index, Scriptable start) { if (isWithValidIndex(index)) { - return valueUnwrapper.unwrap(cx, this, list.get(index)); + return cx.javaToJS(list.get(index), start, listType, listGenericType); } + return Undefined.INSTANCE; } @@ -286,10 +280,10 @@ private Object reduce(Context cx, Object[] args) { } BinaryOperator operator = (BinaryOperator) args[0]; - Object o = valueUnwrapper.unwrap(cx, this, list.get(0)); + Object o = get(cx, 0, this); for (int i = 1; i < list.size(); i++) { - o = valueUnwrapper.unwrap(cx, this, operator.apply(o, valueUnwrapper.unwrap(cx, this, list.get(i)))); + o = operator.apply(o, get(cx, i, this)); } return o; @@ -299,14 +293,14 @@ private Object reduceRight(Context cx, Object[] args) { if (list.isEmpty()) { return Undefined.INSTANCE; } else if (list.size() == 1) { - return list.get(0); + return list.get(0); // might not be correct start index } BinaryOperator operator = (BinaryOperator) args[0]; - Object o = valueUnwrapper.unwrap(cx, this, list.get(0)); + Object o = get(cx, 0, this); for (int i = list.size() - 1; i >= 1; i--) { - o = valueUnwrapper.unwrap(cx, this, operator.apply(o, valueUnwrapper.unwrap(cx, this, list.get(i)))); + o = operator.apply(o, get(cx, i, this)); } return o; diff --git a/src/main/java/dev/latvian/mods/rhino/NativeJavaMap.java b/src/main/java/dev/latvian/mods/rhino/NativeJavaMap.java index e6833206..a925594a 100644 --- a/src/main/java/dev/latvian/mods/rhino/NativeJavaMap.java +++ b/src/main/java/dev/latvian/mods/rhino/NativeJavaMap.java @@ -6,7 +6,6 @@ package dev.latvian.mods.rhino; import dev.latvian.mods.rhino.util.Deletable; -import dev.latvian.mods.rhino.util.ValueUnwrapper; import java.lang.reflect.Type; import java.util.ArrayList; @@ -20,20 +19,14 @@ public class NativeJavaMap extends NativeJavaObject { public final Type mapKeyGenericType; public final Class mapValueType; public final Type mapValueGenericType; - private final ValueUnwrapper valueUnwrapper; - public NativeJavaMap(Context cx, Scriptable scope, Object jo, Map map, Class mapKeyType, Type mapKeyGenericType, Class mapValueType, Type mapValueGenericType, ValueUnwrapper valueUnwrapper) { + public NativeJavaMap(Context cx, Scriptable scope, Object jo, Map map, Class mapKeyType, Type mapKeyGenericType, Class mapValueType, Type mapValueGenericType) { super(scope, jo, jo.getClass(), cx); this.map = map; this.mapKeyType = mapKeyType; this.mapKeyGenericType = mapKeyGenericType; this.mapValueType = mapValueType; this.mapValueGenericType = mapValueGenericType; - this.valueUnwrapper = valueUnwrapper; - } - - public NativeJavaMap(Context cx, Scriptable scope, Object jo, Map map) { - this(cx, scope, jo, map, null, null, null, null, ValueUnwrapper.DEFAULT); } @Override @@ -60,7 +53,7 @@ public boolean has(Context cx, int index, Scriptable start) { @Override public Object get(Context cx, String name, Scriptable start) { if (map.containsKey(name)) { - return valueUnwrapper.unwrap(cx, this, map.get(name)); + return cx.javaToJS(map.get(cx.jsToJava(name, mapKeyType, mapKeyGenericType)), start, mapValueType, mapValueGenericType); } return super.get(cx, name, start); } @@ -68,7 +61,7 @@ public Object get(Context cx, String name, Scriptable start) { @Override public Object get(Context cx, int index, Scriptable start) { if (map.containsKey(index)) { - return valueUnwrapper.unwrap(cx, this, map.get(index)); + return cx.javaToJS(map.get(cx.jsToJava(index, mapKeyType, mapKeyGenericType)), start, mapValueType, mapValueGenericType); } return super.get(cx, index, start); } diff --git a/src/main/java/dev/latvian/mods/rhino/util/TypeUtils.java b/src/main/java/dev/latvian/mods/rhino/type/TypeUtils.java similarity index 92% rename from src/main/java/dev/latvian/mods/rhino/util/TypeUtils.java rename to src/main/java/dev/latvian/mods/rhino/type/TypeUtils.java index 6418d21c..1349ea90 100644 --- a/src/main/java/dev/latvian/mods/rhino/util/TypeUtils.java +++ b/src/main/java/dev/latvian/mods/rhino/type/TypeUtils.java @@ -1,4 +1,4 @@ -package dev.latvian.mods.rhino.util; +package dev.latvian.mods.rhino.type; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; @@ -8,8 +8,6 @@ import java.lang.reflect.WildcardType; public class TypeUtils { - public static final Type[] NO_TYPES = new Type[0]; - public static Class getRawType(Type type) { if (type instanceof Class clz) { return clz; diff --git a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapper.java b/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapper.java index 6d368168..0bac78c6 100644 --- a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapper.java +++ b/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapper.java @@ -3,7 +3,9 @@ import dev.latvian.mods.rhino.Context; import dev.latvian.mods.rhino.Scriptable; +import java.lang.reflect.Type; + @FunctionalInterface public interface CustomJavaToJsWrapper { - Scriptable convertJavaToJs(Context cx, Scriptable scope, Class staticType); + Scriptable convertJavaToJs(Context cx, Scriptable scope, Class staticType, Type genericType); } diff --git a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProvider.java b/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProvider.java deleted file mode 100644 index 58b6428a..00000000 --- a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.latvian.mods.rhino.util; - -import org.jetbrains.annotations.Nullable; - -@FunctionalInterface -public interface CustomJavaToJsWrapperProvider { - CustomJavaToJsWrapperProvider NONE = object -> null; - - @Nullable - CustomJavaToJsWrapper create(T object); -} diff --git a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProviderHolder.java b/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProviderHolder.java deleted file mode 100644 index f83a266e..00000000 --- a/src/main/java/dev/latvian/mods/rhino/util/CustomJavaToJsWrapperProviderHolder.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.latvian.mods.rhino.util; - -import org.jetbrains.annotations.Nullable; - -import java.util.function.Predicate; - -public record CustomJavaToJsWrapperProviderHolder(Predicate predicate, CustomJavaToJsWrapperProvider provider) { - public record PredicateFromClass(Class type) implements Predicate { - @Override - public boolean test(T object) { - return type.isAssignableFrom(object.getClass()); - } - } - - @Nullable - public CustomJavaToJsWrapperProvider create(T object) { - if (predicate.test(object)) { - return provider; - } - - return null; - } -} diff --git a/src/main/java/dev/latvian/mods/rhino/util/ValueUnwrapper.java b/src/main/java/dev/latvian/mods/rhino/util/ValueUnwrapper.java deleted file mode 100644 index e29d6b36..00000000 --- a/src/main/java/dev/latvian/mods/rhino/util/ValueUnwrapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.latvian.mods.rhino.util; - -import dev.latvian.mods.rhino.Context; -import dev.latvian.mods.rhino.Scriptable; - -@FunctionalInterface -public interface ValueUnwrapper { - ValueUnwrapper DEFAULT = (cx, scope, value) -> value == null ? null : cx.wrap(scope, value, value.getClass()); - - Object unwrap(Context cx, Scriptable scope, Object value); -} diff --git a/src/main/java/dev/latvian/mods/rhino/util/wrap/TypeWrappers.java b/src/main/java/dev/latvian/mods/rhino/util/wrap/TypeWrappers.java index b5029207..89fc6864 100644 --- a/src/main/java/dev/latvian/mods/rhino/util/wrap/TypeWrappers.java +++ b/src/main/java/dev/latvian/mods/rhino/util/wrap/TypeWrappers.java @@ -4,14 +4,14 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Type; -import java.util.LinkedHashMap; +import java.util.IdentityHashMap; import java.util.Map; /** * @author LatvianModder */ public class TypeWrappers { - public final Map, TypeWrapper> wrappers = new LinkedHashMap<>(); + public final Map, TypeWrapper> wrappers = new IdentityHashMap<>(); public void register(Class target, TypeWrapperValidator validator, TypeWrapperFactory factory) { if (target == null || target == Object.class) { diff --git a/src/test/java/dev/latvian/mods/rhino/test/GenericsTests.java b/src/test/java/dev/latvian/mods/rhino/test/GenericsTests.java index fb131857..e21414e9 100644 --- a/src/test/java/dev/latvian/mods/rhino/test/GenericsTests.java +++ b/src/test/java/dev/latvian/mods/rhino/test/GenericsTests.java @@ -76,4 +76,9 @@ public void mapArg() { public void mapArgMap() { TEST.test("mapArgMap", "console.genericsMapArg(console.testMap);", "Generics map:\n{W[M[test]]: 10}"); } + + @Test + public void materialHolder() { + TEST.test("materialHolder", "console.registerMaterial('minecraft:iron');", "Registered material: minecraft:iron"); + } } diff --git a/src/test/java/dev/latvian/mods/rhino/test/Holder.java b/src/test/java/dev/latvian/mods/rhino/test/Holder.java new file mode 100644 index 00000000..c1d09868 --- /dev/null +++ b/src/test/java/dev/latvian/mods/rhino/test/Holder.java @@ -0,0 +1,21 @@ +package dev.latvian.mods.rhino.test; + +import dev.latvian.mods.rhino.Context; +import dev.latvian.mods.rhino.type.TypeUtils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public record Holder(T value) { + public static Holder of(Context cx, Object from, Class target, Type genericTarget) { + if (genericTarget instanceof ParameterizedType pt) { + var types = pt.getActualTypeArguments(); + + if (types.length == 1) { + return new Holder(cx.jsToJava(from, TypeUtils.getRawType(types[0]), types[0])); + } + } + + return new Holder(from); + } +} diff --git a/src/test/java/dev/latvian/mods/rhino/test/RhinoTest.java b/src/test/java/dev/latvian/mods/rhino/test/RhinoTest.java index e180cc3d..f3a8a411 100644 --- a/src/test/java/dev/latvian/mods/rhino/test/RhinoTest.java +++ b/src/test/java/dev/latvian/mods/rhino/test/RhinoTest.java @@ -21,6 +21,7 @@ public RhinoTest(String n) { var typeWrappers = factory.getTypeWrappers(); typeWrappers.registerDirect(TestMaterial.class, TestMaterial::get); typeWrappers.register(WithContext.class, WithContext::of); + typeWrappers.register(Holder.class, Holder::of); } public void test(String name, String script, String match) { diff --git a/src/test/java/dev/latvian/mods/rhino/test/TestConsole.java b/src/test/java/dev/latvian/mods/rhino/test/TestConsole.java index d367aacf..fb8ce92b 100644 --- a/src/test/java/dev/latvian/mods/rhino/test/TestConsole.java +++ b/src/test/java/dev/latvian/mods/rhino/test/TestConsole.java @@ -110,4 +110,8 @@ public void genericsMapArg(Map, Integer> arg) { info("Generics map:"); info(arg); } + + public void registerMaterial(Holder holder) { + info("Registered material: " + holder.value().name()); + } } diff --git a/src/test/java/dev/latvian/mods/rhino/test/TestContext.java b/src/test/java/dev/latvian/mods/rhino/test/TestContext.java index a9fc624a..2cb0adea 100644 --- a/src/test/java/dev/latvian/mods/rhino/test/TestContext.java +++ b/src/test/java/dev/latvian/mods/rhino/test/TestContext.java @@ -1,10 +1,7 @@ package dev.latvian.mods.rhino.test; import dev.latvian.mods.rhino.Context; -import dev.latvian.mods.rhino.EvaluatorException; -import dev.latvian.mods.rhino.util.TypeUtils; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class TestContext extends Context { diff --git a/src/test/java/dev/latvian/mods/rhino/test/WithContext.java b/src/test/java/dev/latvian/mods/rhino/test/WithContext.java index 67519562..2f64c165 100644 --- a/src/test/java/dev/latvian/mods/rhino/test/WithContext.java +++ b/src/test/java/dev/latvian/mods/rhino/test/WithContext.java @@ -1,7 +1,7 @@ package dev.latvian.mods.rhino.test; import dev.latvian.mods.rhino.Context; -import dev.latvian.mods.rhino.util.TypeUtils; +import dev.latvian.mods.rhino.type.TypeUtils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;