Scripting: Remove ExecutableScript (#34154)
This commit removes the legacy ExecutableScript, which was no longer used except in tests. All uses have previously been converted to script contexts.
This commit is contained in:
parent
128ea1b7b5
commit
47cbae9b26
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.script.expression;
|
||||
|
||||
import org.apache.lucene.expressions.Expression;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.GeneralScriptException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A bridge to evaluate an {@link Expression} against a map of variables in the context
|
||||
* of an {@link ExecutableScript}.
|
||||
*/
|
||||
public class ExpressionExecutableScript implements ExecutableScript {
|
||||
public final Expression expression;
|
||||
public final Map<String, ReplaceableConstDoubleValues> functionValuesMap;
|
||||
public final ReplaceableConstDoubleValues[] functionValuesArray;
|
||||
|
||||
public ExpressionExecutableScript(Expression expression, Map<String, Object> vars) {
|
||||
this.expression = expression;
|
||||
int functionValuesLength = expression.variables.length;
|
||||
|
||||
if (vars.size() != functionValuesLength) {
|
||||
throw new GeneralScriptException("Error using " + expression + ". " +
|
||||
"The number of variables in an executable expression script [" +
|
||||
functionValuesLength + "] must match the number of variables in the variable map" +
|
||||
" [" + vars.size() + "].");
|
||||
}
|
||||
|
||||
functionValuesArray = new ReplaceableConstDoubleValues[functionValuesLength];
|
||||
functionValuesMap = new HashMap<>();
|
||||
|
||||
for (int functionValuesIndex = 0; functionValuesIndex < functionValuesLength; ++functionValuesIndex) {
|
||||
String variableName = expression.variables[functionValuesIndex];
|
||||
functionValuesArray[functionValuesIndex] = new ReplaceableConstDoubleValues();
|
||||
functionValuesMap.put(variableName, functionValuesArray[functionValuesIndex]);
|
||||
}
|
||||
|
||||
for (String varsName : vars.keySet()) {
|
||||
setNextVar(varsName, vars.get(varsName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextVar(String name, Object value) {
|
||||
if (functionValuesMap.containsKey(name)) {
|
||||
if (value instanceof Number) {
|
||||
double doubleValue = ((Number)value).doubleValue();
|
||||
functionValuesMap.get(name).setValue(doubleValue);
|
||||
} else {
|
||||
throw new GeneralScriptException("Error using " + expression + ". " +
|
||||
"Executable expressions scripts can only process numbers." +
|
||||
" The variable [" + name + "] is not a number.");
|
||||
}
|
||||
} else {
|
||||
throw new GeneralScriptException("Error using " + expression + ". " +
|
||||
"The variable [" + name + "] does not exist in the executable expressions script.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
try {
|
||||
return expression.evaluate(functionValuesArray);
|
||||
} catch (Exception exception) {
|
||||
throw new GeneralScriptException("Error evaluating " + expression, exception);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,6 @@ import org.elasticsearch.index.mapper.MapperService;
|
|||
import org.elasticsearch.script.BucketAggregationScript;
|
||||
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
||||
import org.elasticsearch.script.ClassPermission;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.FilterScript;
|
||||
import org.elasticsearch.script.ScoreScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
@ -112,9 +111,6 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
|
|||
if (context.instanceClazz.equals(SearchScript.class)) {
|
||||
SearchScript.Factory factory = (p, lookup) -> newSearchScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
ExecutableScript.Factory factory = (p) -> new ExpressionExecutableScript(expr, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(BucketAggregationScript.class)) {
|
||||
return context.factoryClazz.cast(newBucketAggregationScriptFactory(expr));
|
||||
} else if (context.instanceClazz.equals(BucketAggregationSelectorScript.class)) {
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
package org.elasticsearch.script.expression;
|
||||
|
||||
import org.apache.lucene.expressions.Expression;
|
||||
import org.apache.lucene.expressions.js.JavascriptCompiler;
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
|
@ -33,7 +31,6 @@ import org.elasticsearch.index.query.QueryBuilders;
|
|||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.GeneralScriptException;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.SearchHits;
|
||||
|
@ -504,68 +501,6 @@ public class MoreExpressionTests extends ESIntegTestCase {
|
|||
message.contains("text variable"), equalTo(true));
|
||||
}
|
||||
|
||||
// series of unit test for using expressions as executable scripts
|
||||
public void testExecutableScripts() throws Exception {
|
||||
assumeTrue("test creates classes directly, cannot run with security manager", System.getSecurityManager() == null);
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("a", 2.5);
|
||||
vars.put("b", 3);
|
||||
vars.put("xyz", -1);
|
||||
|
||||
Expression expr = JavascriptCompiler.compile("a+b+xyz");
|
||||
|
||||
ExpressionExecutableScript ees = new ExpressionExecutableScript(expr, vars);
|
||||
assertEquals((Double) ees.run(), 4.5, 0.001);
|
||||
|
||||
ees.setNextVar("b", -2.5);
|
||||
assertEquals((Double) ees.run(), -1, 0.001);
|
||||
|
||||
ees.setNextVar("a", -2.5);
|
||||
ees.setNextVar("b", -2.5);
|
||||
ees.setNextVar("xyz", -2.5);
|
||||
assertEquals((Double) ees.run(), -7.5, 0.001);
|
||||
|
||||
String message;
|
||||
|
||||
try {
|
||||
vars = new HashMap<>();
|
||||
vars.put("a", 1);
|
||||
ees = new ExpressionExecutableScript(expr, vars);
|
||||
ees.run();
|
||||
fail("An incorrect number of variables were allowed to be used in an expression.");
|
||||
} catch (GeneralScriptException se) {
|
||||
message = se.getMessage();
|
||||
assertThat(message + " should have contained number of variables", message.contains("number of variables"), equalTo(true));
|
||||
}
|
||||
|
||||
try {
|
||||
vars = new HashMap<>();
|
||||
vars.put("a", 1);
|
||||
vars.put("b", 3);
|
||||
vars.put("c", -1);
|
||||
ees = new ExpressionExecutableScript(expr, vars);
|
||||
ees.run();
|
||||
fail("A variable was allowed to be set that does not exist in the expression.");
|
||||
} catch (GeneralScriptException se) {
|
||||
message = se.getMessage();
|
||||
assertThat(message + " should have contained does not exist in", message.contains("does not exist in"), equalTo(true));
|
||||
}
|
||||
|
||||
try {
|
||||
vars = new HashMap<>();
|
||||
vars.put("a", 1);
|
||||
vars.put("b", 3);
|
||||
vars.put("xyz", "hello");
|
||||
ees = new ExpressionExecutableScript(expr, vars);
|
||||
ees.run();
|
||||
fail("A non-number was allowed to be use in the expression.");
|
||||
} catch (GeneralScriptException se) {
|
||||
message = se.getMessage();
|
||||
assertThat(message + " should have contained process numbers", message.contains("process numbers"), equalTo(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// test to make sure expressions are not allowed to be used as update scripts
|
||||
public void testInvalidUpdateScript() throws Exception {
|
||||
try {
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.painless.Compiler.Loader;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptEngine;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
|
@ -102,7 +101,7 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
|||
|
||||
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
|
||||
ScriptContext<?> context = entry.getKey();
|
||||
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
if (context.instanceClazz.equals(SearchScript.class)) {
|
||||
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, null, null,
|
||||
PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
|
||||
} else {
|
||||
|
@ -154,23 +153,6 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
|
|||
return needsScore;
|
||||
}
|
||||
};
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
Constructor<?> constructor = compile(compiler, scriptName, scriptSource, params);
|
||||
|
||||
ExecutableScript.Factory factory = new ExecutableScript.Factory() {
|
||||
@Override
|
||||
public ExecutableScript newInstance(Map<String, Object> params) {
|
||||
try {
|
||||
// a new instance is required for the class bindings model to work correctly
|
||||
GenericElasticsearchScript newInstance = (GenericElasticsearchScript)constructor.newInstance();
|
||||
return new ScriptImpl(newInstance, params, null, null);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalArgumentException("internal error");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else {
|
||||
// Check we ourselves are not being called by unprivileged code.
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.lookup.LeafSearchLookup;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
@ -31,7 +30,7 @@ import java.util.function.DoubleSupplier;
|
|||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* ScriptImpl can be used as either an {@link ExecutableScript} or a {@link SearchScript}
|
||||
* ScriptImpl can be used as a {@link SearchScript}
|
||||
* to run a previously compiled Painless script.
|
||||
*/
|
||||
final class ScriptImpl extends SearchScript {
|
||||
|
|
|
@ -67,7 +67,8 @@ public class BasicAPITests extends ScriptTestCase {
|
|||
ctx.put("_source", _source);
|
||||
params.put("ctx", ctx);
|
||||
|
||||
assertEquals("testvalue", exec("ctx._source['load'].5 = ctx._source['load'].remove('load5')", params, true));
|
||||
assertEquals("testvalue", exec("params.ctx._source['load'].5 = params.ctx._source['load'].remove('load5')",
|
||||
params, true));
|
||||
}
|
||||
|
||||
/** Test loads and stores with a list */
|
||||
|
|
|
@ -262,19 +262,19 @@ public class BasicStatementTests extends ScriptTestCase {
|
|||
"for (int i = 0; i < array.length; i++) { sum += array[i] } return sum",
|
||||
Collections.emptyMap(),
|
||||
Collections.singletonMap(CompilerSettings.MAX_LOOP_COUNTER, "0"),
|
||||
null, true
|
||||
true
|
||||
));
|
||||
assertEquals(6L, exec("long sum = 0; long[] array = new long[] { 1, 2, 3 };" +
|
||||
"int i = 0; while (i < array.length) { sum += array[i++] } return sum",
|
||||
Collections.emptyMap(),
|
||||
Collections.singletonMap(CompilerSettings.MAX_LOOP_COUNTER, "0"),
|
||||
null, true
|
||||
true
|
||||
));
|
||||
assertEquals(6L, exec("long sum = 0; long[] array = new long[] { 1, 2, 3 };" +
|
||||
"int i = 0; do { sum += array[i++] } while (i < array.length); return sum",
|
||||
Collections.emptyMap(),
|
||||
Collections.singletonMap(CompilerSettings.MAX_LOOP_COUNTER, "0"),
|
||||
null, true
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,46 +19,51 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BindingsTests extends ScriptTestCase {
|
||||
|
||||
public abstract static class BindingsTestScript {
|
||||
public static final String[] PARAMETERS = { "test", "bound" };
|
||||
public abstract int execute(int test, int bound);
|
||||
public interface Factory {
|
||||
BindingsTestScript newInstance();
|
||||
}
|
||||
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("bindings_test", Factory.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = super.scriptContexts();
|
||||
contexts.put(BindingsTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public void testBasicBinding() {
|
||||
assertEquals(15, exec("testAddWithState(4, 5, 6, 0.0)"));
|
||||
}
|
||||
|
||||
public void testRepeatedBinding() {
|
||||
String script = "testAddWithState(4, 5, params.test, 0.0)";
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
ExecutableScript.Factory factory = scriptEngine.compile(null, script, ExecutableScript.CONTEXT, Collections.emptyMap());
|
||||
ExecutableScript executableScript = factory.newInstance(params);
|
||||
String script = "testAddWithState(4, 5, test, 0.0)";
|
||||
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
|
||||
BindingsTestScript executableScript = factory.newInstance();
|
||||
|
||||
executableScript.setNextVar("test", 5);
|
||||
assertEquals(14, executableScript.run());
|
||||
|
||||
executableScript.setNextVar("test", 4);
|
||||
assertEquals(13, executableScript.run());
|
||||
|
||||
executableScript.setNextVar("test", 7);
|
||||
assertEquals(16, executableScript.run());
|
||||
assertEquals(14, executableScript.execute(5, 0));
|
||||
assertEquals(13, executableScript.execute(4, 0));
|
||||
assertEquals(16, executableScript.execute(7, 0));
|
||||
}
|
||||
|
||||
public void testBoundBinding() {
|
||||
String script = "testAddWithState(4, params.bound, params.test, 0.0)";
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
ExecutableScript.Factory factory = scriptEngine.compile(null, script, ExecutableScript.CONTEXT, Collections.emptyMap());
|
||||
ExecutableScript executableScript = factory.newInstance(params);
|
||||
String script = "testAddWithState(4, bound, test, 0.0)";
|
||||
BindingsTestScript.Factory factory = scriptEngine.compile(null, script, BindingsTestScript.CONTEXT, Collections.emptyMap());
|
||||
BindingsTestScript executableScript = factory.newInstance();
|
||||
|
||||
executableScript.setNextVar("test", 5);
|
||||
executableScript.setNextVar("bound", 1);
|
||||
assertEquals(10, executableScript.run());
|
||||
|
||||
executableScript.setNextVar("test", 4);
|
||||
executableScript.setNextVar("bound", 2);
|
||||
assertEquals(9, executableScript.run());
|
||||
assertEquals(10, executableScript.execute(5, 1));
|
||||
assertEquals(9, executableScript.execute(4, 2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
@ -45,7 +44,6 @@ public class NeedsScoreTests extends ESSingleNodeTestCase {
|
|||
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(ExecutableScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
PainlessScriptEngine service = new PainlessScriptEngine(Settings.EMPTY, contexts);
|
||||
|
||||
QueryShardContext shardContext = index.newQueryShardContext(0, null, () -> 0, null);
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Tests for special reserved words such as _score */
|
||||
public class ReservedWordTests extends ScriptTestCase {
|
||||
|
||||
/** check that we can't declare a variable of _score, its really reserved! */
|
||||
public void testScoreVar() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int _score = 5; return _score;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [_score] is already defined"));
|
||||
}
|
||||
|
||||
/** check that we can't write to _score, its read-only! */
|
||||
public void testScoreStore() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("_score = 5; return _score;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [_score] is read-only"));
|
||||
}
|
||||
|
||||
/** check that we can't declare a variable of doc, its really reserved! */
|
||||
public void testDocVar() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int doc = 5; return doc;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [doc] is already defined"));
|
||||
}
|
||||
|
||||
/** check that we can't write to doc, its read-only! */
|
||||
public void testDocStore() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("doc = 5; return doc;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [doc] is read-only"));
|
||||
}
|
||||
|
||||
/** check that we can't declare a variable of ctx, its really reserved! */
|
||||
public void testCtxVar() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int ctx = 5; return ctx;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [ctx] is already defined"));
|
||||
}
|
||||
|
||||
/** check that we can't write to ctx, its read-only! */
|
||||
public void testCtxStore() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("ctx = 5; return ctx;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [ctx] is read-only"));
|
||||
}
|
||||
|
||||
/** check that we can modify its contents though */
|
||||
public void testCtxStoreMap() {
|
||||
assertEquals(5, exec("ctx.foo = 5; return ctx.foo;", Collections.singletonMap("ctx", new HashMap<String,Object>()), true));
|
||||
}
|
||||
|
||||
/** check that we can't declare a variable of _value, its really reserved! */
|
||||
public void testAggregationValueVar() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("int _value = 5; return _value;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [_value] is already defined"));
|
||||
}
|
||||
|
||||
/** check that we can't write to _value, its read-only! */
|
||||
public void testAggregationValueStore() {
|
||||
IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("_value = 5; return _value;");
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Variable [_value] is read-only"));
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class ScoreTests extends ScriptTestCase {
|
||||
|
||||
/** Most of a dummy scorer impl that requires overriding just score(). */
|
||||
abstract class MockScorer extends Scorable {
|
||||
@Override
|
||||
public int docID() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void testScoreWorks() {
|
||||
assertEquals(2.5, exec("_score", Collections.emptyMap(), Collections.emptyMap(),
|
||||
new MockScorer() {
|
||||
@Override
|
||||
public float score() {
|
||||
return 2.5f;
|
||||
}
|
||||
},
|
||||
true));
|
||||
}
|
||||
|
||||
public void testScoreNotUsed() {
|
||||
assertEquals(3.5, exec("3.5", Collections.emptyMap(), Collections.emptyMap(),
|
||||
new MockScorer() {
|
||||
@Override
|
||||
public float score() {
|
||||
throw new AssertionError("score() should not be called");
|
||||
}
|
||||
},
|
||||
true));
|
||||
}
|
||||
|
||||
public void testScoreCached() {
|
||||
assertEquals(9.0, exec("_score + _score", Collections.emptyMap(), Collections.emptyMap(),
|
||||
new MockScorer() {
|
||||
private boolean used = false;
|
||||
@Override
|
||||
public float score() {
|
||||
if (used == false) {
|
||||
return 4.5f;
|
||||
}
|
||||
throw new AssertionError("score() should not be called twice");
|
||||
}
|
||||
},
|
||||
true));
|
||||
}
|
||||
}
|
|
@ -19,10 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -73,37 +70,4 @@ public class ScriptEngineTests extends ScriptTestCase {
|
|||
|
||||
assertEquals("value1", exec("return params.l.3.prop1;", vars, true));
|
||||
}
|
||||
|
||||
public void testChangingVarsCrossExecution1() {
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
Map<String, Object> ctx = new HashMap<>();
|
||||
vars.put("ctx", ctx);
|
||||
|
||||
ExecutableScript.Factory factory =
|
||||
scriptEngine.compile(null, "return ctx.value;", ExecutableScript.CONTEXT, Collections.emptyMap());
|
||||
ExecutableScript script = factory.newInstance(vars);
|
||||
|
||||
ctx.put("value", 1);
|
||||
Object o = script.run();
|
||||
assertEquals(1, ((Number) o).intValue());
|
||||
|
||||
ctx.put("value", 2);
|
||||
o = script.run();
|
||||
assertEquals(2, ((Number) o).intValue());
|
||||
}
|
||||
|
||||
public void testChangingVarsCrossExecution2() {
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
ExecutableScript.Factory factory =
|
||||
scriptEngine.compile(null, "return params['value'];", ExecutableScript.CONTEXT, Collections.emptyMap());
|
||||
ExecutableScript script = factory.newInstance(vars);
|
||||
|
||||
script.setNextVar("value", 1);
|
||||
Object value = script.run();
|
||||
assertEquals(1, ((Number)value).intValue());
|
||||
|
||||
script.setNextVar("value", 2);
|
||||
value = script.run();
|
||||
assertEquals(2, ((Number)value).intValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,24 +20,23 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.elasticsearch.common.lucene.ScorerAware;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.painless.antlr.Walker;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookup;
|
||||
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.painless.PainlessExecuteAction.PainlessTestScript;
|
||||
import static org.elasticsearch.painless.node.SSource.MainMethodReserved;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
|
@ -69,7 +68,7 @@ public abstract class ScriptTestCase extends ESTestCase {
|
|||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(ExecutableScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(PainlessTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
@ -87,11 +86,11 @@ public abstract class ScriptTestCase extends ESTestCase {
|
|||
public Object exec(String script, Map<String, Object> vars, boolean picky) {
|
||||
Map<String,String> compilerSettings = new HashMap<>();
|
||||
compilerSettings.put(CompilerSettings.INITIAL_CALL_SITE_DEPTH, random().nextBoolean() ? "0" : "10");
|
||||
return exec(script, vars, compilerSettings, null, picky);
|
||||
return exec(script, vars, compilerSettings, picky);
|
||||
}
|
||||
|
||||
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
|
||||
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, Scorable scorer, boolean picky) {
|
||||
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, boolean picky) {
|
||||
// test for ambiguity errors before running the actual script if picky is true
|
||||
if (picky) {
|
||||
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(PAINLESS_LOOKUP, GenericElasticsearchScript.class);
|
||||
|
@ -102,12 +101,9 @@ public abstract class ScriptTestCase extends ESTestCase {
|
|||
scriptClassInfo, new MainMethodReserved(), getTestName(), script, pickySettings, PAINLESS_LOOKUP, null);
|
||||
}
|
||||
// test actual script execution
|
||||
ExecutableScript.Factory factory = scriptEngine.compile(null, script, ExecutableScript.CONTEXT, compileParams);
|
||||
ExecutableScript executableScript = factory.newInstance(vars);
|
||||
if (scorer != null) {
|
||||
((ScorerAware)executableScript).setScorer(scorer);
|
||||
}
|
||||
return executableScript.run();
|
||||
PainlessTestScript.Factory factory = scriptEngine.compile(null, script, PainlessTestScript.CONTEXT, compileParams);
|
||||
PainlessTestScript testScript = factory.newInstance(vars == null ? Collections.emptyMap() : vars);
|
||||
return testScript.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -233,7 +233,7 @@ public class StringTests extends ScriptTestCase {
|
|||
ctx.put("_id", "somerandomid");
|
||||
params.put("ctx", ctx);
|
||||
|
||||
assertEquals("somerandomid.somerandomid", exec("ctx._id += '.' + ctx._id", params, false));
|
||||
assertEquals("somerandomid.somerandomid", exec("params.ctx._id += '.' + params.ctx._id", params, false));
|
||||
assertEquals("somerandomid.somerandomid", exec("String x = 'somerandomid'; x += '.' + x"));
|
||||
assertEquals("somerandomid.somerandomid", exec("def x = 'somerandomid'; x += '.' + x"));
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
|||
|
||||
public void testBogusParameter() {
|
||||
IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
|
||||
exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"), null, true);
|
||||
exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"), true);
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter"));
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
|||
public void testRCurlyNotDelim() {
|
||||
IllegalArgumentException e = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
// We don't want PICKY here so we get the normal error message
|
||||
exec("def i = 1} return 1", emptyMap(), emptyMap(), null, false);
|
||||
exec("def i = 1} return 1", emptyMap(), emptyMap(), false);
|
||||
});
|
||||
assertEquals("unexpected token ['}'] was expecting one of [{<EOF>, ';'}].", e.getMessage());
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
|||
|
||||
public void testCanNotOverrideRegexEnabled() {
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> exec("", null, singletonMap(CompilerSettings.REGEX_ENABLED.getKey(), "true"), null, false));
|
||||
() -> exec("", null, singletonMap(CompilerSettings.REGEX_ENABLED.getKey(), "true"), false));
|
||||
assertEquals("[painless.regex.enabled] can only be set on node startup.", e.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,10 @@
|
|||
package org.elasticsearch.index.reindex;
|
||||
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.OpType;
|
||||
import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.OpType;
|
||||
import org.elasticsearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.UpdateScript;
|
||||
import org.junit.Before;
|
||||
|
@ -62,8 +61,6 @@ public abstract class AbstractAsyncBulkByScrollActionScriptTestCase<
|
|||
scriptBody.accept(ctx);
|
||||
}
|
||||
};;
|
||||
ExecutableScript simpleExecutableScript = new SimpleExecutableScript(scriptBody);
|
||||
when(scriptService.compile(any(), eq(ExecutableScript.CONTEXT))).thenReturn(params -> simpleExecutableScript);
|
||||
when(scriptService.compile(any(), eq(UpdateScript.CONTEXT))).thenReturn(factory);
|
||||
AbstractAsyncBulkByScrollAction<Request> action = action(scriptService, request().setScript(mockScript("")));
|
||||
RequestWrapper<?> result = action.buildScriptApplier().apply(AbstractAsyncBulkByScrollAction.wrap(index), doc);
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.reindex;
|
||||
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public class SimpleExecutableScript implements ExecutableScript {
|
||||
private final Consumer<Map<String, Object>> script;
|
||||
private Map<String, Object> ctx;
|
||||
|
||||
public SimpleExecutableScript(Consumer<Map<String, Object>> script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
script.accept(ctx);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setNextVar(String name, Object value) {
|
||||
if ("ctx".equals(name)) {
|
||||
ctx = (Map<String, Object>) value;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported var [" + name + "]");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.script;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An executable script, can't be used concurrently.
|
||||
*/
|
||||
public interface ExecutableScript {
|
||||
|
||||
/**
|
||||
* Sets a runtime script parameter.
|
||||
* <p>
|
||||
* Note that this method may be slow, involving put() and get() calls
|
||||
* to a hashmap or similar.
|
||||
* @param name parameter name
|
||||
* @param value parameter value
|
||||
*/
|
||||
void setNextVar(String name, Object value);
|
||||
|
||||
/**
|
||||
* Executes the script.
|
||||
*/
|
||||
Object run();
|
||||
|
||||
interface Factory {
|
||||
ExecutableScript newInstance(Map<String, Object> params);
|
||||
}
|
||||
|
||||
ScriptContext<Factory> CONTEXT = new ScriptContext<>("executable", Factory.class);
|
||||
}
|
|
@ -45,7 +45,6 @@ public class ScriptModule {
|
|||
ScoreScript.CONTEXT,
|
||||
SearchScript.SCRIPT_SORT_CONTEXT,
|
||||
TermsSetQueryScript.CONTEXT,
|
||||
ExecutableScript.CONTEXT,
|
||||
UpdateScript.CONTEXT,
|
||||
BucketAggregationScript.CONTEXT,
|
||||
BucketAggregationSelectorScript.CONTEXT,
|
||||
|
|
|
@ -41,7 +41,7 @@ import java.util.Map;
|
|||
* <li>Call one of the {@code run} methods: {@link #run()}, {@link #runAsDouble()}, or {@link #runAsLong()}</li>
|
||||
* </ol>
|
||||
*/
|
||||
public abstract class SearchScript implements ScorerAware, ExecutableScript {
|
||||
public abstract class SearchScript implements ScorerAware {
|
||||
|
||||
/** The generic runtime parameters for the script. */
|
||||
private final Map<String, Object> params;
|
||||
|
@ -112,7 +112,6 @@ public abstract class SearchScript implements ScorerAware, ExecutableScript {
|
|||
setNextVar("_value", value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextVar(String field, Object value) {}
|
||||
|
||||
/** Return the result as a long. This is used by aggregation scripts over long fields. */
|
||||
|
@ -120,7 +119,6 @@ public abstract class SearchScript implements ScorerAware, ExecutableScript {
|
|||
throw new UnsupportedOperationException("runAsLong is not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
return runAsDouble();
|
||||
}
|
||||
|
|
|
@ -105,11 +105,9 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
}
|
||||
};
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
ExecutableScript.Factory factory = mockCompiled::createExecutableScript;
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(IngestScript.class)) {
|
||||
IngestScript.Factory factory = parameters -> new IngestScript(parameters) {
|
||||
IngestScript.Factory factory = vars ->
|
||||
new IngestScript(vars) {
|
||||
@Override
|
||||
public void execute(Map<String, Object> ctx) {
|
||||
script.apply(ctx);
|
||||
|
@ -167,13 +165,14 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(TemplateScript.class)) {
|
||||
TemplateScript.Factory factory = vars -> {
|
||||
// TODO: need a better way to implement all these new contexts
|
||||
// this is just a shim to act as an executable script just as before
|
||||
ExecutableScript execScript = mockCompiled.createExecutableScript(vars);
|
||||
Map<String, Object> varsWithParams = new HashMap<>();
|
||||
if (vars != null) {
|
||||
varsWithParams.put("params", vars);
|
||||
}
|
||||
return new TemplateScript(vars) {
|
||||
@Override
|
||||
public String execute() {
|
||||
return (String) execScript.run();
|
||||
return (String) script.apply(varsWithParams);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -231,19 +230,6 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
return name;
|
||||
}
|
||||
|
||||
public ExecutableScript createExecutableScript(Map<String, Object> params) {
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
if (options != null) {
|
||||
context.putAll(options); // TODO: remove this once scripts know to look for options under options key
|
||||
context.put("options", options);
|
||||
}
|
||||
if (params != null) {
|
||||
context.putAll(params); // TODO: remove this once scripts know to look for params under params key
|
||||
context.put("params", params);
|
||||
}
|
||||
return new MockExecutableScript(context, script != null ? script : ctx -> source);
|
||||
}
|
||||
|
||||
public SearchScript.LeafFactory createSearchScript(Map<String, Object> params, SearchLookup lookup) {
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
if (options != null) {
|
||||
|
@ -294,27 +280,6 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
}
|
||||
}
|
||||
|
||||
public class MockExecutableScript implements ExecutableScript {
|
||||
|
||||
private final Function<Map<String, Object>, Object> script;
|
||||
private final Map<String, Object> vars;
|
||||
|
||||
public MockExecutableScript(Map<String, Object> vars, Function<Map<String, Object>, Object> script) {
|
||||
this.vars = vars;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextVar(String name, Object value) {
|
||||
vars.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
return script.apply(vars);
|
||||
}
|
||||
}
|
||||
|
||||
public class MockSearchScript implements SearchScript.LeafFactory {
|
||||
|
||||
private final Function<Map<String, Object>, Object> script;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.core.monitoring.test;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.MockScriptEngine;
|
||||
import org.elasticsearch.script.MockScriptPlugin;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
|
@ -45,9 +44,7 @@ public class MockPainlessScriptEngine extends MockScriptEngine {
|
|||
@Override
|
||||
public <T> T compile(String name, String script, ScriptContext<T> context, Map<String, String> options) {
|
||||
MockCompiledScript compiledScript = new MockCompiledScript(name, options, script, p -> script);
|
||||
if (context.instanceClazz.equals(ExecutableScript.class)) {
|
||||
return context.factoryClazz.cast((ExecutableScript.Factory) compiledScript::createExecutableScript);
|
||||
} else if (context.instanceClazz.equals(SearchScript.class)) {
|
||||
if (context.instanceClazz.equals(SearchScript.class)) {
|
||||
return context.factoryClazz.cast((SearchScript.Factory) compiledScript::createSearchScript);
|
||||
}
|
||||
throw new IllegalArgumentException("mock painless does not know how to handle context [" + context.name + "]");
|
||||
|
|
Loading…
Reference in New Issue