Painless: Decouple PainlessLookupBuilder and Whitelists (#32346)

Implements a static function in PainlessLookupBuilder that contains all the logic related
to Whitelist.  PainlessLookupBuilder is available for use in loading from methods beyond
Whitelist now.
This commit is contained in:
Jack Conradson 2018-07-25 10:52:01 -07:00 committed by GitHub
parent 8067250fbe
commit 853aa0afb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 75 additions and 92 deletions

View File

@ -103,10 +103,10 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
ScriptContext<?> context = entry.getKey();
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class,
new PainlessLookupBuilder(entry.getValue()).build()));
PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
} else {
contextsToCompilers.put(context, new Compiler(context.instanceClazz,
new PainlessLookupBuilder(entry.getValue()).build()));
PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
}
}

View File

@ -126,14 +126,55 @@ public class PainlessLookupBuilder {
private static final Pattern METHOD_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$");
private static final Pattern FIELD_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$");
private final List<Whitelist> whitelists;
public static PainlessLookup buildFromWhitelists(List<Whitelist> whitelists) {
PainlessLookupBuilder painlessLookupBuilder = new PainlessLookupBuilder();
String origin = "internal error";
try {
for (Whitelist whitelist : whitelists) {
for (WhitelistClass whitelistClass : whitelist.whitelistStructs) {
origin = whitelistClass.origin;
painlessLookupBuilder.addPainlessClass(
whitelist.javaClassLoader, whitelistClass.javaClassName, whitelistClass.onlyFQNJavaClassName == false);
}
}
for (Whitelist whitelist : whitelists) {
for (WhitelistClass whitelistClass : whitelist.whitelistStructs) {
String targetCanonicalClassName = whitelistClass.javaClassName.replace('$', '.');
for (WhitelistConstructor whitelistConstructor : whitelistClass.whitelistConstructors) {
origin = whitelistConstructor.origin;
painlessLookupBuilder.addPainlessConstructor(
targetCanonicalClassName, whitelistConstructor.painlessParameterTypeNames);
}
for (WhitelistMethod whitelistMethod : whitelistClass.whitelistMethods) {
origin = whitelistMethod.origin;
painlessLookupBuilder.addPainlessMethod(
whitelist.javaClassLoader, targetCanonicalClassName, whitelistMethod.javaAugmentedClassName,
whitelistMethod.javaMethodName, whitelistMethod.painlessReturnTypeName,
whitelistMethod.painlessParameterTypeNames);
}
for (WhitelistField whitelistField : whitelistClass.whitelistFields) {
origin = whitelistField.origin;
painlessLookupBuilder.addPainlessField(
targetCanonicalClassName, whitelistField.javaFieldName, whitelistField.painlessFieldTypeName);
}
}
}
} catch (Exception exception) {
throw new IllegalArgumentException("error loading whitelist(s) " + origin, exception);
}
return painlessLookupBuilder.build();
}
private final Map<String, Class<?>> canonicalClassNamesToClasses;
private final Map<Class<?>, PainlessClassBuilder> classesToPainlessClassBuilders;
public PainlessLookupBuilder(List<Whitelist> whitelists) {
this.whitelists = whitelists;
public PainlessLookupBuilder() {
canonicalClassNamesToClasses = new HashMap<>();
classesToPainlessClassBuilders = new HashMap<>();
@ -666,60 +707,6 @@ public class PainlessLookupBuilder {
}
public PainlessLookup build() {
String origin = "internal error";
try {
// first iteration collects all the Painless type names that
// are used for validation during the second iteration
for (Whitelist whitelist : whitelists) {
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
PainlessClassBuilder painlessStruct =
classesToPainlessClassBuilders.get(canonicalClassNamesToClasses.get(painlessTypeName));
if (painlessStruct != null && painlessStruct.clazz.getName().equals(whitelistStruct.javaClassName) == false) {
throw new IllegalArgumentException("struct [" + painlessStruct.name + "] cannot represent multiple classes " +
"[" + painlessStruct.clazz.getName() + "] and [" + whitelistStruct.javaClassName + "]");
}
origin = whitelistStruct.origin;
addPainlessClass(
whitelist.javaClassLoader, whitelistStruct.javaClassName, whitelistStruct.onlyFQNJavaClassName == false);
painlessStruct = classesToPainlessClassBuilders.get(canonicalClassNamesToClasses.get(painlessTypeName));
classesToPainlessClassBuilders.put(painlessStruct.clazz, painlessStruct);
}
}
// second iteration adds all the constructors, methods, and fields that will
// be available in Painless along with validating they exist and all their types have
// been white-listed during the first iteration
for (Whitelist whitelist : whitelists) {
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
for (WhitelistConstructor whitelistConstructor : whitelistStruct.whitelistConstructors) {
origin = whitelistConstructor.origin;
addPainlessConstructor(painlessTypeName, whitelistConstructor.painlessParameterTypeNames);
}
for (WhitelistMethod whitelistMethod : whitelistStruct.whitelistMethods) {
origin = whitelistMethod.origin;
addPainlessMethod(whitelist.javaClassLoader, painlessTypeName, whitelistMethod.javaAugmentedClassName,
whitelistMethod.javaMethodName, whitelistMethod.painlessReturnTypeName,
whitelistMethod.painlessParameterTypeNames);
}
for (WhitelistField whitelistField : whitelistStruct.whitelistFields) {
origin = whitelistField.origin;
addPainlessField(painlessTypeName, whitelistField.javaFieldName, whitelistField.painlessFieldTypeName);
}
}
}
} catch (Exception exception) {
throw new IllegalArgumentException("error loading whitelist(s) " + origin, exception);
}
copyPainlessClassMembers();
cacheRuntimeHandles();
setFunctionalInterfaceMethods();

View File

@ -20,7 +20,6 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.test.ESTestCase;
public class AnalyzerCasterTests extends ESTestCase {

View File

@ -19,14 +19,14 @@
package org.elasticsearch.painless;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
import org.elasticsearch.painless.spi.Whitelist;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.containsString;
@ -38,7 +38,7 @@ import static org.hamcrest.Matchers.startsWith;
*/
public class BaseClassTests extends ScriptTestCase {
private final PainlessLookup painlessLookup = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
private final PainlessLookup painlessLookup = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
public abstract static class Gets {

View File

@ -2,6 +2,9 @@ package org.elasticsearch.painless;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
* Licensed to Elasticsearch under one or more contributor
@ -22,10 +25,6 @@ import java.util.Collections;
* under the License.
*/
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BasicStatementTests extends ScriptTestCase {
public void testIfStatement() {

View File

@ -37,7 +37,7 @@ import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.not;
public class DebugTests extends ScriptTestCase {
private final PainlessLookup painlessLookup = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
private final PainlessLookup painlessLookup = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
public void testExplain() {
// Debug.explain can explain an object

View File

@ -40,7 +40,7 @@ final class Debugger {
PrintWriter outputWriter = new PrintWriter(output);
Textifier textifier = new Textifier();
try {
new Compiler(iface, new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build())
new Compiler(iface, PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS))
.compile("<debugging>", source, settings, textifier);
} catch (RuntimeException e) {
textifier.print(outputWriter);

View File

@ -19,6 +19,11 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.test.ESTestCase;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -27,13 +32,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.test.ESTestCase;
public class DefBootstrapTests extends ESTestCase {
private final PainlessLookup painlessLookup = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
private final PainlessLookup painlessLookup = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
/** calls toString() on integers, twice */
public void testOneType() throws Throwable {

View File

@ -21,7 +21,6 @@ package org.elasticsearch.painless;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;

View File

@ -52,7 +52,7 @@ import static java.util.stream.Collectors.toList;
*/
public class PainlessDocGenerator {
private static final PainlessLookup PAINLESS_LOOKUP = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
private static final PainlessLookup PAINLESS_LOOKUP = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class);
private static final Comparator<PainlessField> FIELD_NAME = comparing(f -> f.name);
private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.name);

View File

@ -92,7 +92,7 @@ public abstract class ScriptTestCase extends ESTestCase {
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, Scorer scorer, boolean picky) {
// test for ambiguity errors before running the actual script if picky is true
if (picky) {
PainlessLookup painlessLookup = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
PainlessLookup painlessLookup = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, GenericElasticsearchScript.class);
CompilerSettings pickySettings = new CompilerSettings();
pickySettings.setPicky(true);

View File

@ -22,8 +22,8 @@ package org.elasticsearch.painless;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Scorer;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.script.ScriptedMetricAggContexts;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptedMetricAggContexts;
import java.util.ArrayList;
import java.util.Collections;

View File

@ -20,7 +20,6 @@
package org.elasticsearch.painless;
import junit.framework.AssertionFailedError;
import org.apache.lucene.util.Constants;
import org.elasticsearch.script.ScriptException;

View File

@ -20,21 +20,21 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.CompilerSettings;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessField;
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.FeatureTest;
import org.elasticsearch.painless.GenericElasticsearchScript;
import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.Operation;
import org.elasticsearch.painless.ScriptClassInfo;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.antlr.Walker;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.lookup.PainlessField;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
@ -49,7 +49,7 @@ import static org.elasticsearch.painless.node.SSource.MainMethodReserved;
* Tests {@link Object#toString} implementations on all extensions of {@link ANode}.
*/
public class NodeToStringTests extends ESTestCase {
private final PainlessLookup painlessLookup = new PainlessLookupBuilder(Whitelist.BASE_WHITELISTS).build();
private final PainlessLookup painlessLookup = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
public void testEAssignment() {
assertToString(