Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package org.cyclops.integratedscripting.evaluate.translation.translator;

import lombok.SneakyThrows;
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
import org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;
import org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeNbt;
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
import org.cyclops.integrateddynamics.core.evaluate.variable.Variable;
import org.cyclops.integratedscripting.api.evaluate.translation.IEvaluationExceptionFactory;
import org.cyclops.integratedscripting.evaluate.translation.ValueTranslators;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyExecutable;

import javax.annotation.Nullable;

/**
* A Graal proxy executable for operator values.
* @author rubensworks
Expand All @@ -33,10 +40,38 @@ public ValueTypeOperator.ValueOperator getValue() {
@SneakyThrows
@Override
public Object execute(Value... args) {
IOperator operator = value.getRawValue();
IValueType[] inputTypes = operator.getInputTypes();
IVariable<IValue>[] variables = new IVariable[args.length];
for (int i = 0; i < args.length; i++) {
variables[i] = new Variable<>(ValueTranslators.REGISTRY.translateFromGraal(context, args[i], exceptionFactory));
variables[i] = new Variable<>(translateArgument(args[i], i < inputTypes.length ? inputTypes[i] : null));
}
return ValueTranslators.REGISTRY.translateToGraal(context, operator.evaluate(variables), exceptionFactory);
}

/**
* Translate a single argument of this operator.
*
* Only compound tags keep their NBT type when they are translated to a script,
* all other tags become plain script values, and an absent NBT value becomes null.
* These are translated back to NBT here, so that they can be passed to operators again.
*
* @param arg A script value.
* @param inputType The value type the operator expects for this argument, if known.
* @return The translated value.
* @throws EvaluationException If translation failed.
*/
protected IValue translateArgument(Value arg, @Nullable IValueType<?> inputType) throws EvaluationException {
if (inputType == ValueTypes.NBT) {
if (arg.isNull()) {
return ValueTypeNbt.ValueNbt.of();
}
IValue value = ValueTranslators.REGISTRY.translateFromGraal(context, arg, exceptionFactory);
if (value.getType() != ValueTypes.NBT) {
return ValueTypeNbt.ValueNbt.of(ValueTranslators.REGISTRY.translateToNbt(context, value, exceptionFactory));
}
return value;
}
return ValueTranslators.REGISTRY.translateToGraal(context, value.getRawValue().evaluate(variables), exceptionFactory);
return ValueTranslators.REGISTRY.translateFromGraal(context, arg, exceptionFactory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.cyclops.integratedscripting.evaluate.translation;

import net.minecraft.DetectedVersion;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.Bootstrap;
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
import org.cyclops.integrateddynamics.core.evaluate.operator.Operators;
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeListProxyFactories;
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeNbt;
import org.cyclops.integratedscripting.api.evaluate.translation.IEvaluationExceptionFactory;
import org.cyclops.integratedscripting.evaluate.ScriptHelpers;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

/**
* Tests for passing values to operators that expect NBT.
*
* NBT values other than compound tags reach scripts as plain JS values,
* so they must be converted back to NBT when they are passed into an operator again.
*
* @author rubensworks
*/
public class NbtOperatorArgumentsJavaScriptTests {

static {
SharedConstants.setVersion(DetectedVersion.BUILT_IN);
Bootstrap.bootStrap();
}

private static Context CTX = null;
private static IEvaluationExceptionFactory EF = ScriptHelpers.getDummyEvaluationExceptionFactory();

@BeforeClass
public static void beforeAll() throws EvaluationException {
ValueTypeListProxyFactories.load();
Operators.load();
ValueTranslators.load();

CTX = ScriptHelpers.createPopulatedContext(null);
}

private static Value nbtValue() throws EvaluationException {
CompoundTag levels = new CompoundTag();
levels.putInt("a", 5);
levels.putInt("b", 1);
levels.putInt("c", 7);
CompoundTag tag = new CompoundTag();
tag.put("levels", levels);
return ValueTranslators.REGISTRY.translateToGraal(CTX, ValueTypeNbt.ValueNbt.of(tag), EF);
}

@Test
public void testListTagMatchPassedBackToNbtOperator() throws EvaluationException {
// A filter expression matches into a list tag, which reaches the script as a plain array.
Value function = CTX.eval("js", """
(function (nbt) {
const matched = idContext.ops.stringNbtPathMatchFirst('["levels"][?(@ >= 3)]', nbt);
return [Array.isArray(matched), idContext.ops.nbtAsTagList(matched).length];
})
""");
Value result = function.execute(nbtValue());
assertThat(result.getArrayElement(0).asBoolean(), is(true));
assertThat(result.getArrayElement(1).asInt(), is(2));
}

@Test
public void testArrayPassedToNbtOperator() {
Value function = CTX.eval("js", """
(function () {
return idContext.ops.nbtAsTagList([1, 2, 3]).length;
})
""");
assertThat(function.execute().asInt(), is(3));
}

@Test
public void testNullPassedToNbtOperator() throws EvaluationException {
// An NBT path that matches nothing yields null, which stays usable as an NBT value.
Value function = CTX.eval("js", """
(function (nbt) {
const matched = idContext.ops.stringNbtPathMatchFirst('["absent"]', nbt);
return [matched === null, idContext.ops.nbtAsTagList(matched).length];
})
""");
Value result = function.execute(nbtValue());
assertThat(result.getArrayElement(0).asBoolean(), is(true));
assertThat(result.getArrayElement(1).asInt(), is(0));
}

@Test
public void testCompoundTagPassedBackToNbtOperator() throws EvaluationException {
// Compound tags were already passed back unchanged, and must keep working.
Value function = CTX.eval("js", """
(function (nbt) {
const levels = idContext.ops.stringNbtPathMatchFirst('["levels"]', nbt);
return idContext.ops.nbtSize(levels);
})
""");
assertThat(function.execute(nbtValue()).asInt(), is(3));
}

@Test
public void testNonNbtOperatorArgumentsAreUnaffected() {
Value function = CTX.eval("js", """
(function () {
return idContext.ops.stringLength("abcd");
})
""");
assertThat(function.execute().asInt(), is(4));
}
}
Loading