Skip to content

Commit

Permalink
Added context.addCustomJavaToJsWrapper(), rewrote NBT wrappers, remov…
Browse files Browse the repository at this point in the history
…ed 1.18.1- from supported versions, rewrote tests
  • Loading branch information
LatvianModder committed May 9, 2022
1 parent 31fb40d commit dc7fe52
Show file tree
Hide file tree
Showing 41 changed files with 683 additions and 1,154 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id "architectury-plugin" version "3.4-SNAPSHOT"
id "dev.architectury.loom" version "0.10.0-SNAPSHOT" apply false
id "dev.architectury.loom" version "0.11.0-SNAPSHOT" apply false
id "io.github.juuxel.loom-quiltflower" version "1.7.0" apply false
}

architectury {
Expand All @@ -9,6 +10,7 @@ architectury {

subprojects {
apply plugin: "dev.architectury.loom"
apply plugin: "io.github.juuxel.loom-quiltflower"

loom {
silentMojangMappingsLicense()
Expand Down
4 changes: 4 additions & 0 deletions common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ configurations {
dev
}

loom {
accessWidenerPath = file("src/main/resources/rhino.accesswidener")
}

artifacts {
dev(jar)
}
Expand Down
47 changes: 46 additions & 1 deletion common/src/main/java/dev/latvian/mods/rhino/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
import dev.latvian.mods.rhino.ast.AstRoot;
import dev.latvian.mods.rhino.ast.ScriptNode;
import dev.latvian.mods.rhino.classfile.ClassFileWriter.ClassFileFormatException;
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.Remapper;
import dev.latvian.mods.rhino.util.wrap.TypeWrappers;
import org.jetbrains.annotations.Nullable;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
Expand All @@ -25,6 +29,7 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;

/**
* This class represents the runtime context of an executing script.
Expand Down Expand Up @@ -1370,8 +1375,12 @@ public static Object javaToJS(Object value, Scriptable scope) {
* @throws EvaluatorException if the conversion cannot be performed
*/
public static Object jsToJava(Object value, Class<?> desiredType) throws EvaluatorException {
if (desiredType == null) {
return value;
}

Context cx = getCurrentContext();
return NativeJavaObject.coerceTypeImpl(cx.hasTypeWrappers() ? cx.getTypeWrappers() : null, desiredType, value);
return NativeJavaObject.coerceTypeImpl(cx, cx.hasTypeWrappers() ? cx.getTypeWrappers() : null, desiredType, value);
}

/**
Expand Down Expand Up @@ -1910,6 +1919,42 @@ public Remapper getRemapper() {
return factory.remapper;
}

@Nullable
@SuppressWarnings("unchecked")
public CustomJavaToJsWrapper wrapCustomJavaToJs(Object javaObject) {
if (factory.customScriptableWrappers.isEmpty()) {
return null;
}

var provider = factory.customScriptableWrapperCache.get(javaObject.getClass());

if (provider == null) {
for (CustomJavaToJsWrapperProviderHolder wrapper : factory.customScriptableWrappers) {
provider = wrapper.create(javaObject);

if (provider != null) {
break;
}
}

if (provider == null) {
provider = CustomJavaToJsWrapperProvider.NONE;
}

factory.customScriptableWrapperCache.put(javaObject.getClass(), provider);
}

return provider.create(javaObject);
}

public <T> void addCustomJavaToJsWrapper(Predicate<T> predicate, CustomJavaToJsWrapperProvider<T> provider) {
factory.customScriptableWrappers.add(new CustomJavaToJsWrapperProviderHolder<>(predicate, provider));
}

public <T> void addCustomJavaToJsWrapper(Class<T> type, CustomJavaToJsWrapperProvider<T> provider) {
addCustomJavaToJsWrapper(new CustomJavaToJsWrapperProviderHolder.PredicateFromClass<>(type), provider);
}

private final ContextFactory factory;
private boolean sealed;
private Object sealKey;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@

package dev.latvian.mods.rhino;

import dev.latvian.mods.rhino.util.CustomJavaToJsWrapperProvider;
import dev.latvian.mods.rhino.util.CustomJavaToJsWrapperProviderHolder;
import dev.latvian.mods.rhino.util.DefaultRemapper;
import dev.latvian.mods.rhino.util.Remapper;
import dev.latvian.mods.rhino.util.wrap.TypeWrappers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Factory class that Rhino runtime uses to create new {@link Context}
* instances. A <code>ContextFactory</code> can also notify listeners
Expand Down Expand Up @@ -114,6 +121,8 @@ public class ContextFactory {
private boolean disabledListening;
TypeWrappers typeWrappers;
Remapper remapper = DefaultRemapper.INSTANCE;
final List<CustomJavaToJsWrapperProviderHolder<?>> customScriptableWrappers = new ArrayList<>();
final Map<Class<?>, CustomJavaToJsWrapperProvider> customScriptableWrapperCache = new HashMap<>();

/**
* Listener of {@link Context} creation and release events.
Expand Down
38 changes: 26 additions & 12 deletions common/src/main/java/dev/latvian/mods/rhino/NativeJavaList.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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.util.ArrayList;
import java.util.Arrays;
Expand All @@ -18,10 +20,18 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public class NativeJavaList extends NativeJavaObject {
private final List list;
private final Class<?> listType;
private final ValueUnwrapper valueUnwrapper;

public NativeJavaList(Scriptable scope, Object jo, List list) {
public NativeJavaList(Scriptable scope, Object jo, List list, @Nullable Class<?> listType, ValueUnwrapper valueUnwrapper) {
super(scope, jo, jo.getClass());
this.list = list;
this.listType = listType;
this.valueUnwrapper = valueUnwrapper;
}

public NativeJavaList(Scriptable scope, Object jo, List list) {
this(scope, jo, list, null, ValueUnwrapper.DEFAULT);
}

@Override
Expand All @@ -48,9 +58,7 @@ public boolean has(Symbol key, Scriptable start) {
@Override
public Object get(int index, Scriptable start) {
if (isWithValidIndex(index)) {
Context cx = Context.getContext();
Object obj = list.get(index);
return cx.getWrapFactory().wrap(cx, this, obj, obj.getClass());
return valueUnwrapper.unwrap(this, list.get(index));
}
return Undefined.instance;
}
Expand All @@ -66,7 +74,7 @@ public Object get(Symbol key, Scriptable start) {
@Override
public void put(int index, Scriptable start, Object value) {
if (isWithValidIndex(index)) {
list.set(index, Context.jsToJava(value, Object.class));
list.set(index, Context.jsToJava(value, listType));
return;
}
super.put(index, start, value);
Expand Down Expand Up @@ -121,9 +129,15 @@ private int getLength() {

private int push(Object[] args) {
if (args.length == 1) {
list.add(args[0]);
list.add(Context.jsToJava(args[0], listType));
} else if (args.length > 1) {
list.addAll(Arrays.asList(args));
Object[] args1 = new Object[args.length];

for (int i = 0; i < args.length; i++) {
args1[i] = Context.jsToJava(args[i], listType);
}

list.addAll(Arrays.asList(args1));
}

return list.size();
Expand All @@ -147,7 +161,7 @@ private Object shift() {

private int unshift(Object[] args) {
for (int i = args.length - 1; i >= 0; i--) {
list.add(0, args[i]);
list.add(0, Context.jsToJava(args[i], listType));
}

return list.size();
Expand Down Expand Up @@ -265,10 +279,10 @@ private Object reduce(Object[] args) {
}

BinaryOperator operator = (BinaryOperator) args[0];
Object o = list.get(0);
Object o = valueUnwrapper.unwrap(this, list.get(0));

for (int i = 1; i < list.size(); i++) {
o = operator.apply(o, list.get(i));
o = valueUnwrapper.unwrap(this, operator.apply(o, valueUnwrapper.unwrap(this, list.get(i))));
}

return o;
Expand All @@ -282,10 +296,10 @@ private Object reduceRight(Object[] args) {
}

BinaryOperator operator = (BinaryOperator) args[0];
Object o = list.get(0);
Object o = valueUnwrapper.unwrap(this, list.get(0));

for (int i = list.size() - 1; i >= 1; i--) {
o = operator.apply(o, list.get(i));
o = valueUnwrapper.unwrap(this, operator.apply(o, valueUnwrapper.unwrap(this, list.get(i))));
}

return o;
Expand Down
24 changes: 14 additions & 10 deletions common/src/main/java/dev/latvian/mods/rhino/NativeJavaMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package dev.latvian.mods.rhino;

import dev.latvian.mods.rhino.util.Deletable;
import dev.latvian.mods.rhino.util.ValueUnwrapper;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -14,18 +15,25 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public class NativeJavaMap extends NativeJavaObject {
private final Map map;
private final Class<?> mapValueType;
private final ValueUnwrapper valueUnwrapper;

public NativeJavaMap(Scriptable scope, Object jo, Map map) {
public NativeJavaMap(Scriptable scope, Object jo, Map map, Class<?> mapValueType, ValueUnwrapper valueUnwrapper) {
super(scope, jo, jo.getClass());
this.map = map;
this.mapValueType = mapValueType;
this.valueUnwrapper = valueUnwrapper;
}

public NativeJavaMap(Scriptable scope, Object jo, Map map) {
this(scope, jo, map, Object.class, ValueUnwrapper.DEFAULT);
}

@Override
public String getClassName() {
return "JavaMap";
}


@Override
public boolean has(String name, Scriptable start) {
if (map.containsKey(name)) {
Expand All @@ -45,31 +53,27 @@ public boolean has(int index, Scriptable start) {
@Override
public Object get(String name, Scriptable start) {
if (map.containsKey(name)) {
Context cx = Context.getContext();
Object obj = map.get(name);
return cx.getWrapFactory().wrap(cx, this, obj, obj.getClass());
return valueUnwrapper.unwrap(this, map.get(name));
}
return super.get(name, start);
}

@Override
public Object get(int index, Scriptable start) {
if (map.containsKey(index)) {
Context cx = Context.getContext();
Object obj = map.get(index);
return cx.getWrapFactory().wrap(cx, this, obj, obj.getClass());
return valueUnwrapper.unwrap(this, map.get(index));
}
return super.get(index, start);
}

@Override
public void put(String name, Scriptable start, Object value) {
map.put(name, Context.jsToJava(value, Object.class));
map.put(name, Context.jsToJava(value, mapValueType));
}

@Override
public void put(int index, Scriptable start, Object value) {
map.put(index, Context.jsToJava(value, Object.class));
map.put(index, Context.jsToJava(value, mapValueType));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ private static int getJSTypeCode(Object value) {
* Type-munging for field setting and method invocation.
* Conforms to LC3 specification
*/
static Object coerceTypeImpl(@Nullable TypeWrappers typeWrappers, Class<?> type, Object value) {
static Object coerceTypeImpl(Context cx, @Nullable TypeWrappers typeWrappers, Class<?> type, Object value) {
if (value == null || value.getClass() == type) {
return value;
}
Expand Down Expand Up @@ -578,8 +578,7 @@ static Object coerceTypeImpl(@Nullable TypeWrappers typeWrappers, Class<?> type,
if (type == ScriptRuntime.StringClass) {
return ScriptRuntime.toString(value);
} else if (type == ScriptRuntime.ObjectClass) {
Context context = Context.getCurrentContext();
if ((context != null) && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) {
if (cx.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) {
//to process numbers like 2.0 as 2 without decimal place
long roundedValue = Math.round(toDouble(value));
if (roundedValue == toDouble(value)) {
Expand Down Expand Up @@ -657,7 +656,7 @@ static Object coerceTypeImpl(@Nullable TypeWrappers typeWrappers, Class<?> type,
Object Result = Array.newInstance(arrayType, (int) length);
for (int i = 0; i < length; ++i) {
try {
Array.set(Result, i, coerceTypeImpl(typeWrappers, arrayType, array.get(i, array)));
Array.set(Result, i, coerceTypeImpl(cx, typeWrappers, arrayType, array.get(i, array)));
} catch (EvaluatorException ee) {
return reportConversionError(value, type);
}
Expand All @@ -677,6 +676,7 @@ static Object coerceTypeImpl(@Nullable TypeWrappers typeWrappers, Class<?> type,
}
}


return value;
}

Expand Down
8 changes: 7 additions & 1 deletion common/src/main/java/dev/latvian/mods/rhino/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2585,7 +2585,7 @@ private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn) throws IOExc
for (; ; ) {
int tt = peekToken();
switch (tt) {
case Token.DOT:
case Token.DOT, Token.HOOK:
lineno = ts.lineno;
pn = propertyAccess(tt, pn);
pn.setLineno(lineno);
Expand Down Expand Up @@ -2667,6 +2667,12 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException {
codeBug();
}

boolean optionalChaining = tt == Token.HOOK && matchToken(Token.DOT, false);

if (tt == Token.HOOK && !optionalChaining) {
reportError("msg.no.dot.after.hook");
}

int lineno = ts.lineno, dotPos = ts.tokenBeg;
consumeToken();

Expand Down
16 changes: 12 additions & 4 deletions common/src/main/java/dev/latvian/mods/rhino/WrapFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

package dev.latvian.mods.rhino;

import dev.latvian.mods.rhino.util.CustomJavaObjectWrapper;
import dev.latvian.mods.rhino.util.CustomJavaToJsWrapper;
import dev.latvian.mods.rhino.util.JavaSetWrapper;

import java.util.List;
Expand Down Expand Up @@ -115,9 +115,17 @@ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) {
* @return the wrapped value which shall not be null
*/
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class<?> staticType) {
if (javaObject instanceof CustomJavaObjectWrapper w) {
return w.wrapAsJavaObject(cx, scope, staticType);
} else if (javaObject instanceof Map map) {
if (javaObject instanceof CustomJavaToJsWrapper w) {
return w.convertJavaToJs(cx, scope, staticType);
}

CustomJavaToJsWrapper w = cx.wrapCustomJavaToJs(javaObject);

if (w != null) {
return w.convertJavaToJs(cx, scope, staticType);
}

if (javaObject instanceof Map map) {
return new NativeJavaMap(scope, map, map);
} else if (javaObject instanceof List list) {
return new NativeJavaList(scope, list, list);
Expand Down
Loading

0 comments on commit dc7fe52

Please sign in to comment.