/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.painless; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.painless.api.Augmentation; import java.util.HashMap; import java.util.Map; /** * Settings to use when compiling a script. */ public final class CompilerSettings { /** * Are regexes enabled? If {@code true}, regexes are enabled and unlimited by the limit factor. If {@code false}, they are completely * disabled. If {@code use-limit}, the default, regexes are enabled but limited in complexity according to the * {@code script.painless.regex.limit-factor} setting. */ public static final Setting REGEX_ENABLED = new Setting<>( "script.painless.regex.enabled", RegexEnabled.LIMITED.value, RegexEnabled::parse, Property.NodeScope ); /** * How complex can a regex be? This is the number of characters that can be considered expressed as a multiple of string length. */ public static final Setting REGEX_LIMIT_FACTOR = Setting.intSetting( "script.painless.regex.limit-factor", 6, 1, Property.NodeScope ); /** * Constant to be used when specifying the maximum loop counter when compiling a script. */ public static final String MAX_LOOP_COUNTER = "max_loop_counter"; /** * Constant to be used for enabling additional internal compilation checks (slower). */ public static final String PICKY = "picky"; /** * Hack to set the initial "depth" for the {@link DefBootstrap.PIC} and {@link DefBootstrap.MIC}. Only used for testing: do not * overwrite. */ public static final String INITIAL_CALL_SITE_DEPTH = "initialCallSiteDepth"; /** * The maximum number of statements allowed to be run in a loop. * For now the number is set fairly high to accommodate users * doing large update queries. */ private int maxLoopCounter = 1000000; /** * Whether to throw exception on ambiguity or other internal parsing issues. This option * makes things slower too, it is only for debugging. */ private boolean picky = false; /** * For testing. Do not use. */ private int initialCallSiteDepth = 0; private int testInject0 = 2; private int testInject1 = 4; private int testInject2 = 6; /** * Are regexes enabled? Defaults to using the factor setting. */ private RegexEnabled regexesEnabled = RegexEnabled.LIMITED; /** * How complex can regexes be? Expressed as a multiple of the input string. */ private int regexLimitFactor = 0; /** * Returns the value for the cumulative total number of statements that can be made in all loops * in a script before an exception is thrown. This attempts to prevent infinite loops. Note if * the counter is set to 0, no loop counter will be written. */ public int getMaxLoopCounter() { return maxLoopCounter; } /** * Set the cumulative total number of statements that can be made in all loops. * @see #getMaxLoopCounter */ public void setMaxLoopCounter(int max) { this.maxLoopCounter = max; } /** * Returns true if the compiler should be picky. This means it runs slower and enables additional * runtime checks, throwing an exception if there are ambiguities in the grammar or other low level * parsing problems. */ public boolean isPicky() { return picky; } /** * Set to true if compilation should be picky. * @see #isPicky */ public void setPicky(boolean picky) { this.picky = picky; } /** * Returns initial call site depth. This means we pretend we've already seen N different types, * to better exercise fallback code in tests. */ public int getInitialCallSiteDepth() { return initialCallSiteDepth; } /** * For testing megamorphic fallbacks. Do not use. * @see #getInitialCallSiteDepth() */ public void setInitialCallSiteDepth(int depth) { this.initialCallSiteDepth = depth; } /** * Are regexes enabled? */ public RegexEnabled areRegexesEnabled() { return regexesEnabled; } /** * Are regexes enabled or limited? */ public void setRegexesEnabled(RegexEnabled regexesEnabled) { this.regexesEnabled = regexesEnabled; } /** * What is the limitation on regex complexity? How many multiples of input length can a regular expression consider? */ public void setRegexLimitFactor(int regexLimitFactor) { this.regexLimitFactor = regexLimitFactor; } /** * What is the limit factor for regexes? */ public int getRegexLimitFactor() { return regexLimitFactor; } /** * Get compiler settings as a map. This is used to inject compiler settings into augmented methods with the {@code @inject_constant} * annotation. */ public Map asMap() { int regexLimitFactor = this.regexLimitFactor; if (regexesEnabled == RegexEnabled.TRUE) { regexLimitFactor = Augmentation.UNLIMITED_PATTERN_FACTOR; } else if (regexesEnabled == RegexEnabled.FALSE) { regexLimitFactor = Augmentation.DISABLED_PATTERN_FACTOR; } Map map = new HashMap<>(); map.put("regex_limit_factor", regexLimitFactor); // for testing only map.put("testInject0", testInject0); map.put("testInject1", testInject1); map.put("testInject2", testInject2); return map; } /** * Options for {@code script.painless.regex.enabled} setting. */ public enum RegexEnabled { TRUE("true"), FALSE("false"), LIMITED("limited"); final String value; RegexEnabled(String value) { this.value = value; } /** * Parse string value, necessary because `valueOf` would require strings to be upper case. */ public static RegexEnabled parse(String value) { if (TRUE.value.equals(value)) { return TRUE; } else if (FALSE.value.equals(value)) { return FALSE; } else if (LIMITED.value.equals(value)) { return LIMITED; } throw new IllegalArgumentException( "invalid value [" + value + "] must be one of [" + TRUE.value + "," + FALSE.value + "," + LIMITED.value + "]" ); } } }