Upstream Merge

This commit is contained in:
MattBDev
2020-01-10 22:32:12 -05:00
parent b2be1ea9fb
commit 0d2fff2cd2
81 changed files with 2528 additions and 23695 deletions

View File

@ -26,4 +26,4 @@ public interface CompiledExpression {
Double execute(ExecutionData executionData);
}
}

View File

@ -48,4 +48,4 @@ public class ExecutionData {
public SetMultimap<String, MethodHandle> getFunctions() {
return requireNonNull(functions, "Cannot use functions in a constant");
}
}
}

View File

@ -35,14 +35,6 @@ import static com.sk89q.worldedit.antlr.ExpressionLexer.ID;
public class ExpressionHelper {
/**
* The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
*/
public static final String WRAPPED_CONSTANT = "<wrapped constant>";
private ExpressionHelper() {
}
public static void check(boolean condition, ParserRuleContext ctx, String message) {
if (!condition) {
throw evalException(ctx, message);
@ -59,8 +51,8 @@ public class ExpressionHelper {
public static EvaluationException evalException(Token token, String message) {
return new EvaluationException(
getErrorPosition(token),
message
getErrorPosition(token),
message
);
}
@ -68,8 +60,6 @@ public class ExpressionHelper {
check(iterations <= 256, ctx, "Loop exceeded 256 iterations");
}
// Special argument handle names
public static void checkTimeout() {
if (Thread.interrupted()) {
throw new ExpressionTimeoutException("Calculations exceeded time limit.");
@ -97,15 +87,21 @@ public class ExpressionHelper {
}
// We matched no function, fail with appropriate message.
String possibleCounts = matchingFns.stream()
.map(mh -> mh.isVarargsCollector()
? (mh.type().parameterCount() - 1) + "+"
: String.valueOf(mh.type().parameterCount()))
.collect(Collectors.joining("/"));
.map(mh -> mh.isVarargsCollector()
? (mh.type().parameterCount() - 1) + "+"
: String.valueOf(mh.type().parameterCount()))
.collect(Collectors.joining("/"));
throw evalException(ctx, "Incorrect number of arguments for function '" + fnName + "', " +
"expected " + possibleCounts + ", " +
"got " + ctx.args.size());
"expected " + possibleCounts + ", " +
"got " + ctx.args.size());
}
// Special argument handle names
/**
* The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
*/
public static final String WRAPPED_CONSTANT = "<wrapped constant>";
/**
* If this argument needs a handle, returns the name of the handle needed. Otherwise, returns
* {@code null}. If {@code arg} isn't a valid handle reference, throws.
@ -118,7 +114,7 @@ public class ExpressionHelper {
if (pType == LocalSlot.Variable.class) {
// MUST be an id
check(id.isPresent(), arg,
"Function '" + fnName + "' requires a variable in parameter " + i);
"Function '" + fnName + "' requires a variable in parameter " + i);
return id.get();
} else if (pType == LocalSlot.class) {
return id.orElse(WRAPPED_CONSTANT);
@ -128,7 +124,7 @@ public class ExpressionHelper {
private static Optional<String> tryResolveId(ParserRuleContext arg) {
Optional<ExpressionParser.WrappedExprContext> wrappedExprContext =
tryAs(arg, ExpressionParser.WrappedExprContext.class);
tryAs(arg, ExpressionParser.WrappedExprContext.class);
if (wrappedExprContext.isPresent()) {
return tryResolveId(wrappedExprContext.get().expression());
}
@ -139,8 +135,8 @@ public class ExpressionHelper {
}
private static <T extends ParserRuleContext> Optional<T> tryAs(
ParserRuleContext ctx,
Class<T> rule
ParserRuleContext ctx,
Class<T> rule
) {
if (rule.isInstance(ctx)) {
return Optional.of(rule.cast(ctx));
@ -155,4 +151,7 @@ public class ExpressionHelper {
return tryAs(ctxs.get(0), rule);
}
}
private ExpressionHelper() {
}
}

View File

@ -22,8 +22,8 @@ package com.sk89q.worldedit.internal.expression;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.primitives.Doubles;
import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
import com.sk89q.worldedit.math.Vector3;
@ -61,7 +61,7 @@ final class Functions {
// clean up all the functions
return ImmutableSetMultimap.copyOf(
Multimaps.transformValues(map, Functions::clean)
Multimaps.transformValues(map, Functions::clean)
);
}
@ -71,7 +71,7 @@ final class Functions {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
DOUBLE_VALUE = lookup.findVirtual(Number.class, "doubleValue",
methodType(double.class));
methodType(double.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
@ -83,7 +83,7 @@ final class Functions {
if (handle.type().returnType() != Double.class) {
// Ensure that the handle returns a Double, even if originally a Number
checkState(Number.class.isAssignableFrom(handle.type().returnType()),
"Function does not return a number");
"Function does not return a number");
handle = handle.asType(handle.type().changeReturnType(Number.class));
handle = filterReturnValue(handle, DOUBLE_VALUE);
}

View File

@ -22,7 +22,11 @@ package com.sk89q.worldedit.internal.expression.invoke;
import com.google.common.collect.SetMultimap;
import com.sk89q.worldedit.antlr.ExpressionBaseVisitor;
import com.sk89q.worldedit.antlr.ExpressionParser;
import com.sk89q.worldedit.internal.expression.*;
import com.sk89q.worldedit.internal.expression.BreakException;
import com.sk89q.worldedit.internal.expression.EvaluationException;
import com.sk89q.worldedit.internal.expression.ExecutionData;
import com.sk89q.worldedit.internal.expression.ExpressionHelper;
import com.sk89q.worldedit.internal.expression.LocalSlot;
import it.unimi.dsi.fastutil.doubles.Double2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
import org.antlr.v4.runtime.CommonToken;
@ -40,9 +44,35 @@ import java.util.function.DoubleBinaryOperator;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static com.sk89q.worldedit.antlr.ExpressionLexer.*;
import static com.sk89q.worldedit.antlr.ExpressionLexer.ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE;
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE_ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.EQUAL;
import static com.sk89q.worldedit.antlr.ExpressionLexer.EXCLAMATION_MARK;
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN_OR_EQUAL;
import static com.sk89q.worldedit.antlr.ExpressionLexer.INCREMENT;
import static com.sk89q.worldedit.antlr.ExpressionLexer.LEFT_SHIFT;
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN_OR_EQUAL;
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS;
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS_ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO;
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO_ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.NEAR;
import static com.sk89q.worldedit.antlr.ExpressionLexer.NOT_EQUAL;
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS;
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS_ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.POWER_ASSIGN;
import static com.sk89q.worldedit.antlr.ExpressionLexer.RIGHT_SHIFT;
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES;
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES_ASSIGN;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.WRAPPED_CONSTANT;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.*;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.CALL_BINARY_OP;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.DOUBLE_TO_BOOL;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.IS_NULL;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NEW_LS_CONSTANT;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NULL_DOUBLE;
import static java.lang.invoke.MethodType.methodType;
/**
@ -50,26 +80,6 @@ import static java.lang.invoke.MethodType.methodType;
*/
class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
private static final MethodHandle BREAK_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
.bindTo(BreakException.BREAK));
private static final MethodHandle CONTINUE_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
.bindTo(BreakException.CONTINUE));
private static final double[] factorials = new double[171];
/**
* Method handle (ExecutionData)Double, returns null.
*/
private static final MethodHandle DEFAULT_RESULT =
ExpressionHandles.dropData(MethodHandles.constant(Double.class, null));
static {
factorials[0] = 1;
for (int i = 1; i < factorials.length; ++i) {
factorials[i] = factorials[i - 1] * i;
}
}
/*
* General idea is that we don't need to pass around variables, they're all in ExecutionData.
* We do need to pass that around, so most MethodHandles will be of the type
@ -82,43 +92,11 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
this.functions = functions;
}
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
// Make sure maxUlps is non-negative and small enough that the
// default NAN won't compare as equal to anything.
//assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
long aLong = Double.doubleToRawLongBits(a);
// Make aLong lexicographically ordered as a twos-complement long
if (aLong < 0) aLong = 0x8000000000000000L - aLong;
long bLong = Double.doubleToRawLongBits(b);
// Make bLong lexicographically ordered as a twos-complement long
if (bLong < 0) bLong = 0x8000000000000000L - bLong;
final long longDiff = Math.abs(aLong - bLong);
return longDiff <= maxUlps;
}
private static double factorial(double x) throws EvaluationException {
final int n = (int) x;
if (n < 0) {
return 0;
}
if (n >= factorials.length) {
return Double.POSITIVE_INFINITY;
}
return factorials[n];
}
private Token extractToken(ParserRuleContext ctx) {
List<TerminalNode> children = ctx.children.stream()
.filter(TerminalNode.class::isInstance)
.map(TerminalNode.class::cast)
.collect(Collectors.toList());
.filter(TerminalNode.class::isInstance)
.map(TerminalNode.class::cast)
.collect(Collectors.toList());
ExpressionHelper.check(children.size() == 1, ctx, "Expected exactly one token, got " + children.size());
return children.get(0).getSymbol();
}
@ -133,19 +111,19 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
private void checkHandle(MethodHandle mh, ParserRuleContext ctx) {
ExpressionHelper.check(mh.type().equals(ExpressionHandles.COMPILED_EXPRESSION_SIG), ctx,
"Incorrect type returned from handler for " + ctx.getClass());
"Incorrect type returned from handler for " + ctx.getClass());
}
private MethodHandle evaluateForNamedValue(ParserRuleContext ctx, String name) {
MethodHandle guard = MethodHandles.guardWithTest(
// if result is null
IS_NULL.asType(methodType(boolean.class, Double.class)),
// throw appropriate exception, dropping `result` argument
MethodHandles.dropArguments(
ExpressionHandles.throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class
),
// else return the argument we were passed
MethodHandles.identity(Double.class)
// if result is null
IS_NULL.asType(methodType(boolean.class, Double.class)),
// throw appropriate exception, dropping `result` argument
MethodHandles.dropArguments(
ExpressionHandles.throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class
),
// else return the argument we were passed
MethodHandles.identity(Double.class)
);
// now pass `result` into `guard`
MethodHandle result = evaluate(ctx).handle;
@ -161,7 +139,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
value = value.asType(value.type().unwrap());
// Pass `value` into converter, returns (ExecutionData)boolean;
return MethodHandles.collectArguments(
DOUBLE_TO_BOOL, 0, value
DOUBLE_TO_BOOL, 0, value
);
}
@ -170,9 +148,9 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
ParserRuleContext falseBranch) {
// easiest one of the bunch
return MethodHandles.guardWithTest(
evaluateBoolean(condition),
trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle,
falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle
evaluateBoolean(condition),
trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle,
falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle
);
}
@ -189,39 +167,46 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override
public MethodHandle visitWhileStatement(ExpressionParser.WhileStatementContext ctx) {
return ExpressionHandles.whileLoop(
evaluateBoolean(ctx.condition),
evaluate(ctx.body)
evaluateBoolean(ctx.condition),
evaluate(ctx.body)
);
}
@Override
public MethodHandle visitDoStatement(ExpressionParser.DoStatementContext ctx) {
return ExpressionHandles.doWhileLoop(
evaluateBoolean(ctx.condition),
evaluate(ctx.body)
evaluateBoolean(ctx.condition),
evaluate(ctx.body)
);
}
@Override
public MethodHandle visitForStatement(ExpressionParser.ForStatementContext ctx) {
return ExpressionHandles.forLoop(
evaluate(ctx.init).handle,
evaluateBoolean(ctx.condition),
evaluate(ctx.body),
evaluate(ctx.update).handle
evaluate(ctx.init).handle,
evaluateBoolean(ctx.condition),
evaluate(ctx.body),
evaluate(ctx.update).handle
);
}
@Override
public MethodHandle visitSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
return ExpressionHandles.simpleForLoop(
evaluateForValue(ctx.first),
evaluateForValue(ctx.last),
ctx.counter,
evaluate(ctx.body)
evaluateForValue(ctx.first),
evaluateForValue(ctx.last),
ctx.counter,
evaluate(ctx.body)
);
}
private static final MethodHandle BREAK_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
.bindTo(BreakException.BREAK));
private static final MethodHandle CONTINUE_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
.bindTo(BreakException.CONTINUE));
@Override
public MethodHandle visitBreakStatement(ExpressionParser.BreakStatementContext ctx) {
return BREAK_STATEMENT;
@ -309,7 +294,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
return value;
case MINUS:
return ExpressionHandles.call(data ->
-(double) ExpressionHandles.standardInvoke(value, data)
-(double) ExpressionHandles.standardInvoke(value, data)
);
}
throw ExpressionHelper.evalException(ctx, "Invalid text for plus/minus expr: " + ctx.op.getText());
@ -319,7 +304,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
public MethodHandle visitNotExpr(ExpressionParser.NotExprContext ctx) {
MethodHandle expr = evaluateBoolean(ctx.expr);
return ExpressionHandles.call(data ->
ExpressionHandles.boolToDouble(!(boolean) ExpressionHandles.standardInvoke(expr, data))
ExpressionHandles.boolToDouble(!(boolean) ExpressionHandles.standardInvoke(expr, data))
);
}
@ -331,7 +316,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// - Convert to long from double value
// - Convert from Object to Double to double.
return ExpressionHandles.call(data ->
(double) ~(long) (double) ExpressionHandles.standardInvoke(expr, data)
(double) ~(long) (double) ExpressionHandles.standardInvoke(expr, data)
);
}
@ -340,11 +325,11 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle left = evaluateBoolean(ctx.left);
MethodHandle right = evaluateForValue(ctx.right);
return MethodHandles.guardWithTest(
left,
right,
ExpressionHandles.dropData(
MethodHandles.constant(Double.class, ExpressionHandles.boolToDouble(false))
)
left,
right,
ExpressionHandles.dropData(
MethodHandles.constant(Double.class, ExpressionHandles.boolToDouble(false))
)
);
}
@ -355,20 +340,20 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// Inject left as primary condition, on failure take right with data parameter
// logic = (Double,ExecutionData)Double
MethodHandle logic = MethodHandles.guardWithTest(
// data arg dropped implicitly
DOUBLE_TO_BOOL,
// drop data arg
MethodHandles.dropArguments(
MethodHandles.identity(Double.class), 1, ExecutionData.class
),
// drop left arg, call right
MethodHandles.dropArguments(
right, 0, Double.class
)
// data arg dropped implicitly
DOUBLE_TO_BOOL,
// drop data arg
MethodHandles.dropArguments(
MethodHandles.identity(Double.class), 1, ExecutionData.class
),
// drop left arg, call right
MethodHandles.dropArguments(
right, 0, Double.class
)
);
// mixed = (ExecutionData,ExecutionData)Double
MethodHandle mixed = MethodHandles.collectArguments(
logic, 0, left
logic, 0, left
);
// Deduplicate ExecutionData
return ExpressionHandles.dedupData(mixed);
@ -381,8 +366,8 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle mhRight = evaluateForValue(right);
// Map two data args to two double args, then evaluate op
MethodHandle doubleData = MethodHandles.filterArguments(
CALL_BINARY_OP.bindTo(op), 0,
mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap())
CALL_BINARY_OP.bindTo(op), 0,
mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap())
);
doubleData = doubleData.asType(doubleData.type().wrap());
return ExpressionHandles.dedupData(doubleData);
@ -474,16 +459,57 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
});
}
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
// Make sure maxUlps is non-negative and small enough that the
// default NAN won't compare as equal to anything.
//assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
long aLong = Double.doubleToRawLongBits(a);
// Make aLong lexicographically ordered as a twos-complement long
if (aLong < 0) aLong = 0x8000000000000000L - aLong;
long bLong = Double.doubleToRawLongBits(b);
// Make bLong lexicographically ordered as a twos-complement long
if (bLong < 0) bLong = 0x8000000000000000L - bLong;
final long longDiff = Math.abs(aLong - bLong);
return longDiff <= maxUlps;
}
@Override
public MethodHandle visitPostfixExpr(ExpressionParser.PostfixExprContext ctx) {
MethodHandle value = evaluateForValue(ctx.expr);
if (ctx.op.getType() == EXCLAMATION_MARK) {
return ExpressionHandles.call(data ->
factorial((double) ExpressionHandles.standardInvoke(value, data))
factorial((double) ExpressionHandles.standardInvoke(value, data))
);
}
throw ExpressionHelper.evalException(ctx,
"Invalid text for post-unary expr: " + ctx.op.getText());
"Invalid text for post-unary expr: " + ctx.op.getText());
}
private static final double[] factorials = new double[171];
static {
factorials[0] = 1;
for (int i = 1; i < factorials.length; ++i) {
factorials[i] = factorials[i - 1] * i;
}
}
private static double factorial(double x) throws EvaluationException {
final int n = (int) x;
if (n < 0) {
return 0;
}
if (n >= factorials.length) {
return Double.POSITIVE_INFINITY;
}
return factorials[n];
}
@Override
@ -522,7 +548,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
break;
default:
throw ExpressionHelper.evalException(ctx, "Invalid text for assign expr: " +
ctx.assignmentOperator().getText());
ctx.assignmentOperator().getText());
}
}
variable.setValue(value);
@ -551,7 +577,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// Collapse every data into one argument
int[] permutation = new int[arguments.length];
return MethodHandles.permuteArguments(
manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation
manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation
);
}
@ -567,7 +593,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle filter = evaluateForValue(arg);
filter = filter.asType(filter.type().unwrap());
return MethodHandles.collectArguments(
NEW_LS_CONSTANT, 0, filter
NEW_LS_CONSTANT, 0, filter
);
}
// small hack
@ -580,7 +606,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
public MethodHandle visitConstantExpression(ExpressionParser.ConstantExpressionContext ctx) {
try {
return ExpressionHandles.dropData(
MethodHandles.constant(Double.class, Double.parseDouble(ctx.getText()))
MethodHandles.constant(Double.class, Double.parseDouble(ctx.getText()))
);
} catch (NumberFormatException e) {
// Rare, but might happen, e.g. if too many digits
@ -594,6 +620,12 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
return ExpressionHandles.call(data -> ExpressionHandles.getSlotValue(data, source));
}
/**
* Method handle (ExecutionData)Double, returns null.
*/
private static final MethodHandle DEFAULT_RESULT =
ExpressionHandles.dropData(MethodHandles.constant(Double.class, null));
@Override
protected MethodHandle defaultResult() {
return DEFAULT_RESULT;
@ -641,13 +673,13 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
}
// Add a dummy Double parameter to the end
MethodHandle dummyDouble = MethodHandles.dropArguments(
result, 1, Double.class
result, 1, Double.class
);
// Have oldResult turn it from data->Double
MethodHandle doubledData = MethodHandles.collectArguments(
dummyDouble, 1, oldResult
dummyDouble, 1, oldResult
);
// Deduplicate the `data` parameter
return ExpressionHandles.dedupData(doubledData);
}
}
}

View File

@ -31,4 +31,4 @@ class ExecNode {
this.ctx = ctx;
this.handle = handle;
}
}
}

View File

@ -38,7 +38,7 @@ public class ExpressionCompiler {
private static final String CE_EXECUTE = "execute";
private static final MethodType HANDLE_TO_CE =
methodType(CompiledExpression.class, MethodHandle.class);
methodType(CompiledExpression.class, MethodHandle.class);
private static final MethodHandle HANDLE_TO_CE_CONVERTER;
@ -46,17 +46,17 @@ public class ExpressionCompiler {
MethodHandle handleInvoker = MethodHandles.invoker(ExpressionHandles.COMPILED_EXPRESSION_SIG);
try {
HANDLE_TO_CE_CONVERTER = LambdaMetafactory.metafactory(
MethodHandles.lookup(),
// Implementing CompiledExpression.execute
CE_EXECUTE,
// Take a handle, to be converted to CompiledExpression
HANDLE_TO_CE,
// Raw signature for SAM type
ExpressionHandles.COMPILED_EXPRESSION_SIG,
// Handle to call the captured handle.
handleInvoker,
// Actual signature at invoke time
ExpressionHandles.COMPILED_EXPRESSION_SIG
MethodHandles.lookup(),
// Implementing CompiledExpression.execute
CE_EXECUTE,
// Take a handle, to be converted to CompiledExpression
HANDLE_TO_CE,
// Raw signature for SAM type
ExpressionHandles.COMPILED_EXPRESSION_SIG,
// Handle to call the captured handle.
handleInvoker,
// Actual signature at invoke time
ExpressionHandles.COMPILED_EXPRESSION_SIG
).dynamicInvoker().asType(HANDLE_TO_CE);
} catch (LambdaConversionException e) {
throw new IllegalStateException("Failed to load ExpressionCompiler MetaFactory", e);
@ -67,7 +67,7 @@ public class ExpressionCompiler {
SetMultimap<String, MethodHandle> functions) {
MethodHandle invokable = root.accept(new CompilingVisitor(functions));
return (CompiledExpression) ExpressionHandles.safeInvoke(
HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable)
HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable)
);
}
}
}

View File

@ -20,7 +20,12 @@
package com.sk89q.worldedit.internal.expression.invoke;
import com.google.common.base.Throwables;
import com.sk89q.worldedit.internal.expression.*;
import com.sk89q.worldedit.internal.expression.BreakException;
import com.sk89q.worldedit.internal.expression.CompiledExpression;
import com.sk89q.worldedit.internal.expression.EvaluationException;
import com.sk89q.worldedit.internal.expression.ExecutionData;
import com.sk89q.worldedit.internal.expression.ExpressionHelper;
import com.sk89q.worldedit.internal.expression.LocalSlot;
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
import it.unimi.dsi.fastutil.doubles.Double2ObjectMaps;
import org.antlr.v4.runtime.ParserRuleContext;
@ -34,18 +39,22 @@ import java.util.Objects;
import java.util.function.DoubleBinaryOperator;
import java.util.function.Supplier;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.*;
import static java.lang.invoke.MethodHandles.*;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkIterations;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkTimeout;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.getErrorPosition;
import static java.lang.invoke.MethodHandles.collectArguments;
import static java.lang.invoke.MethodHandles.constant;
import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.insertArguments;
import static java.lang.invoke.MethodHandles.permuteArguments;
import static java.lang.invoke.MethodHandles.throwException;
import static java.lang.invoke.MethodType.methodType;
class ExpressionHandles {
static final MethodType COMPILED_EXPRESSION_SIG = methodType(Double.class, ExecutionData.class);
static final MethodHandle IS_NULL;
static final MethodHandle DOUBLE_TO_BOOL;
static final MethodHandle CALL_BINARY_OP;
static final MethodHandle NEW_LS_CONSTANT;
static final MethodHandle NULL_DOUBLE = dropData(constant(Double.class, null));
private static final MethodHandle EVAL_EXCEPTION_CONSTR;
private static final MethodHandle CALL_EXPRESSION;
private static final MethodHandle GET_VARIABLE;
@ -54,43 +63,52 @@ class ExpressionHandles {
private static final MethodHandle SIMPLE_FOR_LOOP_IMPL;
private static final MethodHandle SWITCH_IMPL;
static final MethodHandle IS_NULL;
static final MethodHandle DOUBLE_TO_BOOL;
static final MethodHandle CALL_BINARY_OP;
static final MethodHandle NEW_LS_CONSTANT;
static final MethodHandle NULL_DOUBLE = dropData(constant(Double.class, null));
static {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
EVAL_EXCEPTION_CONSTR = lookup.findConstructor(
EvaluationException.class, methodType(void.class, int.class, String.class));
EvaluationException.class, methodType(void.class, int.class, String.class));
CALL_EXPRESSION = lookup.findVirtual(
CompiledExpression.class, "execute",
methodType(Double.class, ExecutionData.class));
CompiledExpression.class, "execute",
methodType(Double.class, ExecutionData.class));
GET_VARIABLE = lookup.findStatic(ExpressionHandles.class, "getVariable",
methodType(LocalSlot.Variable.class, ExecutionData.class, Token.class));
methodType(LocalSlot.Variable.class, ExecutionData.class, Token.class));
WHILE_FOR_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class,
"whileForLoopImpl",
methodType(Double.class, ExecutionData.class, MethodHandle.class,
MethodHandle.class, ExecNode.class, MethodHandle.class));
"whileForLoopImpl",
methodType(Double.class, ExecutionData.class, MethodHandle.class,
MethodHandle.class, ExecNode.class, MethodHandle.class));
DO_WHILE_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class, "doWhileLoopImpl",
methodType(Double.class, ExecutionData.class, MethodHandle.class, ExecNode.class));
methodType(Double.class, ExecutionData.class, MethodHandle.class, ExecNode.class));
SIMPLE_FOR_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class, "simpleForLoopImpl",
methodType(Double.class, ExecutionData.class, MethodHandle.class,
MethodHandle.class, Token.class, ExecNode.class));
methodType(Double.class, ExecutionData.class, MethodHandle.class,
MethodHandle.class, Token.class, ExecNode.class));
SWITCH_IMPL = lookup.findStatic(ExpressionHandles.class, "switchImpl",
methodType(Double.class, ExecutionData.class, Double2ObjectMap.class,
MethodHandle.class, ExecNode.class));
methodType(Double.class, ExecutionData.class, Double2ObjectMap.class,
MethodHandle.class, ExecNode.class));
IS_NULL = lookup.findStatic(Objects.class, "isNull",
methodType(boolean.class, Object.class));
methodType(boolean.class, Object.class));
DOUBLE_TO_BOOL = lookup.findStatic(ExpressionHandles.class, "doubleToBool",
methodType(boolean.class, double.class));
methodType(boolean.class, double.class));
CALL_BINARY_OP = lookup.findVirtual(DoubleBinaryOperator.class, "applyAsDouble",
methodType(double.class, double.class, double.class));
methodType(double.class, double.class, double.class));
NEW_LS_CONSTANT = lookup.findConstructor(LocalSlot.Constant.class,
methodType(void.class, double.class));
methodType(void.class, double.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private ExpressionHandles() {
@FunctionalInterface
interface Invokable {
Object invoke(MethodHandle handle) throws Throwable;
}
static Object safeInvoke(MethodHandle handle, Invokable invokable) {
@ -116,22 +134,22 @@ class ExpressionHandles {
static MethodHandle dedupData(MethodHandle doubleData) {
return permuteArguments(
doubleData, COMPILED_EXPRESSION_SIG,
0, 0
doubleData, COMPILED_EXPRESSION_SIG,
0, 0
);
}
static LocalSlot.Variable initVariable(ExecutionData data, Token nameToken) {
String name = nameToken.getText();
return data.getSlots().initVariable(name)
.orElseThrow(() -> ExpressionHelper.evalException(
nameToken, "Cannot overwrite non-variable '" + name + "'"
));
.orElseThrow(() -> ExpressionHelper.evalException(
nameToken, "Cannot overwrite non-variable '" + name + "'"
));
}
private static Supplier<EvaluationException> varNotInitException(Token nameToken) {
return () -> ExpressionHelper.evalException(
nameToken, "'" + nameToken.getText() + "' is not initialized yet"
nameToken, "'" + nameToken.getText() + "' is not initialized yet"
);
}
@ -142,10 +160,10 @@ class ExpressionHandles {
static LocalSlot.Variable getVariable(ExecutionData data, Token nameToken) {
String name = nameToken.getText();
LocalSlot slot = data.getSlots().getSlot(name)
.orElseThrow(varNotInitException(nameToken));
.orElseThrow(varNotInitException(nameToken));
if (!(slot instanceof LocalSlot.Variable)) {
throw ExpressionHelper.evalException(
nameToken, "'" + name + "' is not a variable"
nameToken, "'" + name + "' is not a variable"
);
}
return (LocalSlot.Variable) slot;
@ -154,7 +172,7 @@ class ExpressionHandles {
static double getSlotValue(ExecutionData data, Token nameToken) {
String name = nameToken.getText();
return data.getSlots().getSlotValue(name)
.orElseThrow(varNotInitException(nameToken));
.orElseThrow(varNotInitException(nameToken));
}
/**
@ -163,7 +181,7 @@ class ExpressionHandles {
*/
private static MethodHandle evalException(ParserRuleContext ctx, String message) {
return insertArguments(EVAL_EXCEPTION_CONSTR, 0,
getErrorPosition(ctx.start), message);
getErrorPosition(ctx.start), message);
}
/**
@ -173,9 +191,9 @@ class ExpressionHandles {
static MethodHandle throwEvalException(ParserRuleContext ctx, String message) {
// replace arg0 of `throw` with `evalException`
return collectArguments(
throwException(Double.class, EvaluationException.class),
0,
evalException(ctx, message)
throwException(Double.class, EvaluationException.class),
0,
evalException(ctx, message)
);
}
@ -196,7 +214,7 @@ class ExpressionHandles {
static MethodHandle whileLoop(MethodHandle condition, ExecNode body) {
return insertArguments(WHILE_FOR_LOOP_IMPL, 1,
null, condition, body, null);
null, condition, body, null);
}
static MethodHandle forLoop(MethodHandle init,
@ -204,7 +222,7 @@ class ExpressionHandles {
ExecNode body,
MethodHandle update) {
return insertArguments(WHILE_FOR_LOOP_IMPL, 1,
init, condition, body, update);
init, condition, body, update);
}
private static Double whileForLoopImpl(ExecutionData data,
@ -264,7 +282,7 @@ class ExpressionHandles {
Token counter,
ExecNode body) {
return insertArguments(SIMPLE_FOR_LOOP_IMPL, 1,
first, last, counter, body);
first, last, counter, body);
}
private static Double simpleForLoopImpl(ExecutionData data,
@ -331,9 +349,7 @@ class ExpressionHandles {
return evaluated;
}
@FunctionalInterface
interface Invokable {
Object invoke(MethodHandle handle) throws Throwable;
private ExpressionHandles() {
}
}
}

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.internal.util;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
/**
* An explicit substring. Provides the range from which it was taken.
*/