Merge pull request #18410 from rmuir/painless_parser_performance_bug
painless: fix insanely slow compilation
This commit is contained in:
commit
ee6d29b342
|
@ -189,7 +189,7 @@ POST hockey/player/1/_update
|
||||||
{
|
{
|
||||||
"script": {
|
"script": {
|
||||||
"lang": "painless",
|
"lang": "painless",
|
||||||
"inline": "ctx._source.last = params.last ctx._source.nick = params.nick",
|
"inline": "ctx._source.last = params.last; ctx._source.nick = params.nick",
|
||||||
"params": {
|
"params": {
|
||||||
"last": "gaudreau",
|
"last": "gaudreau",
|
||||||
"nick": "hockey"
|
"nick": "hockey"
|
||||||
|
|
|
@ -28,15 +28,15 @@ source
|
||||||
statement
|
statement
|
||||||
: IF LP expression RP block ( ELSE block )? # if
|
: IF LP expression RP block ( ELSE block )? # if
|
||||||
| WHILE LP expression RP ( block | empty ) # while
|
| WHILE LP expression RP ( block | empty ) # while
|
||||||
| DO block WHILE LP expression RP SEMICOLON? # do
|
| DO block WHILE LP expression RP ( SEMICOLON | EOF ) # do
|
||||||
| FOR LP initializer? SEMICOLON expression? SEMICOLON afterthought? RP ( block | empty ) # for
|
| FOR LP initializer? SEMICOLON expression? SEMICOLON afterthought? RP ( block | empty ) # for
|
||||||
| declaration SEMICOLON? # decl
|
| declaration ( SEMICOLON | EOF ) # decl
|
||||||
| CONTINUE SEMICOLON? # continue
|
| CONTINUE ( SEMICOLON | EOF ) # continue
|
||||||
| BREAK SEMICOLON? # break
|
| BREAK ( SEMICOLON | EOF ) # break
|
||||||
| RETURN expression SEMICOLON? # return
|
| RETURN expression ( SEMICOLON | EOF ) # return
|
||||||
| TRY block trap+ # try
|
| TRY block trap+ # try
|
||||||
| THROW expression SEMICOLON? # throw
|
| THROW expression ( SEMICOLON | EOF ) # throw
|
||||||
| expression SEMICOLON? # expr
|
| expression ( SEMICOLON | EOF ) # expr
|
||||||
;
|
;
|
||||||
|
|
||||||
block
|
block
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -19,161 +19,60 @@
|
||||||
|
|
||||||
package org.elasticsearch.painless;
|
package org.elasticsearch.painless;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class NoSemiColonTests extends ScriptTestCase {
|
public class NoSemiColonTests extends ScriptTestCase {
|
||||||
|
|
||||||
public void testIfStatement() {
|
|
||||||
assertEquals(1, exec("int x = 5 if (x == 5) return 1 return 0"));
|
|
||||||
assertEquals(0, exec("int x = 4 if (x == 5) return 1 else return 0"));
|
|
||||||
assertEquals(2, exec("int x = 4 if (x == 5) return 1 else if (x == 4) return 2 else return 0"));
|
|
||||||
assertEquals(1, exec("int x = 4 if (x == 5) return 1 else if (x == 4) return 1 else return 0"));
|
|
||||||
|
|
||||||
assertEquals(3, exec(
|
|
||||||
"int x = 5\n" +
|
|
||||||
"if (x == 5) {\n" +
|
|
||||||
" int y = 2\n" +
|
|
||||||
" \n" +
|
|
||||||
" if (y == 2) {\n" +
|
|
||||||
" x = 3\n" +
|
|
||||||
" }\n" +
|
|
||||||
" \n" +
|
|
||||||
"}\n" +
|
|
||||||
"\n" +
|
|
||||||
"return x\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWhileStatement() {
|
|
||||||
|
|
||||||
assertEquals("aaaaaa", exec("String c = \"a\" int x while (x < 5) { ++x c += \"a\" } return c"));
|
|
||||||
|
|
||||||
Object value = exec(
|
|
||||||
" byte[][] b = new byte[5][5] \n" +
|
|
||||||
" byte x = 0, y \n" +
|
|
||||||
" \n" +
|
|
||||||
" while (x < 5) { \n" +
|
|
||||||
" y = 0 \n" +
|
|
||||||
" \n" +
|
|
||||||
" while (y < 5) { \n" +
|
|
||||||
" b[x][y] = (byte)(x*y) \n" +
|
|
||||||
" ++y \n" +
|
|
||||||
" } \n" +
|
|
||||||
" \n" +
|
|
||||||
" ++x \n" +
|
|
||||||
" } \n" +
|
|
||||||
" \n" +
|
|
||||||
" return b \n");
|
|
||||||
|
|
||||||
byte[][] b = (byte[][])value;
|
|
||||||
|
|
||||||
for (byte x = 0; x < 5; ++x) {
|
|
||||||
for (byte y = 0; y < 5; ++y) {
|
|
||||||
assertEquals(x*y, b[x][y]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDoWhileStatement() {
|
|
||||||
assertEquals("aaaaaa", exec("String c = \"a\" int x do { c += \"a\"; ++x } while (x < 5) return c"));
|
|
||||||
|
|
||||||
Object value = exec(
|
|
||||||
" long[][] l = new long[5][5] \n" +
|
|
||||||
" long x = 0, y \n" +
|
|
||||||
" \n" +
|
|
||||||
" do { \n" +
|
|
||||||
" y = 0 \n" +
|
|
||||||
" \n" +
|
|
||||||
" do { \n" +
|
|
||||||
" l[(int)x][(int)y] = x*y; \n" +
|
|
||||||
" ++y \n" +
|
|
||||||
" } while (y < 5) \n" +
|
|
||||||
" \n" +
|
|
||||||
" ++x \n" +
|
|
||||||
" } while (x < 5) \n" +
|
|
||||||
" \n" +
|
|
||||||
" return l \n");
|
|
||||||
|
|
||||||
long[][] l = (long[][])value;
|
|
||||||
|
|
||||||
for (long x = 0; x < 5; ++x) {
|
|
||||||
for (long y = 0; y < 5; ++y) {
|
|
||||||
assertEquals(x*y, l[(int)x][(int)y]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testForStatement() {
|
|
||||||
assertEquals("aaaaaa", exec("String c = \"a\" for (int x = 0; x < 5; ++x) c += \"a\" return c"));
|
|
||||||
|
|
||||||
Object value = exec(
|
|
||||||
" int[][] i = new int[5][5] \n" +
|
|
||||||
" for (int x = 0; x < 5; ++x) { \n" +
|
|
||||||
" for (int y = 0; y < 5; ++y) { \n" +
|
|
||||||
" i[x][y] = x*y \n" +
|
|
||||||
" } \n" +
|
|
||||||
" } \n" +
|
|
||||||
" \n" +
|
|
||||||
" return i \n");
|
|
||||||
|
|
||||||
int[][] i = (int[][])value;
|
|
||||||
|
|
||||||
for (int x = 0; x < 5; ++x) {
|
|
||||||
for (int y = 0; y < 5; ++y) {
|
|
||||||
assertEquals(x*y, i[x][y]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDeclarationStatement() {
|
public void testDeclarationStatement() {
|
||||||
assertEquals((byte)2, exec("byte a = 2 return a"));
|
assertEquals((byte)2, exec("byte a = 2; return a"));
|
||||||
assertEquals((short)2, exec("short a = 2 return a"));
|
assertEquals((short)2, exec("short a = 2; return a"));
|
||||||
assertEquals((char)2, exec("char a = 2 return a"));
|
assertEquals((char)2, exec("char a = 2; return a"));
|
||||||
assertEquals(2, exec("int a = 2 return a"));
|
assertEquals(2, exec("int a = 2; return a"));
|
||||||
assertEquals(2L, exec("long a = 2 return a"));
|
assertEquals(2L, exec("long a = 2; return a"));
|
||||||
assertEquals(2F, exec("float a = 2 return a"));
|
assertEquals(2F, exec("float a = 2; return a"));
|
||||||
assertEquals(2.0, exec("double a = 2 return a"));
|
assertEquals(2.0, exec("double a = 2; return a"));
|
||||||
assertEquals(false, exec("boolean a = false return a"));
|
assertEquals(false, exec("boolean a = false; return a"));
|
||||||
assertEquals("string", exec("String a = \"string\" return a"));
|
assertEquals("string", exec("String a = \"string\"; return a"));
|
||||||
assertEquals(HashMap.class, exec("Map<String, Object> a = new HashMap<String, Object>() return a").getClass());
|
assertEquals(HashMap.class, exec("Map<String, Object> a = new HashMap<String, Object>(); return a").getClass());
|
||||||
|
|
||||||
assertEquals(byte[].class, exec("byte[] a = new byte[1] return a").getClass());
|
assertEquals(byte[].class, exec("byte[] a = new byte[1]; return a").getClass());
|
||||||
assertEquals(short[].class, exec("short[] a = new short[1] return a").getClass());
|
assertEquals(short[].class, exec("short[] a = new short[1]; return a").getClass());
|
||||||
assertEquals(char[].class, exec("char[] a = new char[1] return a").getClass());
|
assertEquals(char[].class, exec("char[] a = new char[1]; return a").getClass());
|
||||||
assertEquals(int[].class, exec("int[] a = new int[1] return a").getClass());
|
assertEquals(int[].class, exec("int[] a = new int[1]; return a").getClass());
|
||||||
assertEquals(long[].class, exec("long[] a = new long[1] return a").getClass());
|
assertEquals(long[].class, exec("long[] a = new long[1]; return a").getClass());
|
||||||
assertEquals(float[].class, exec("float[] a = new float[1] return a").getClass());
|
assertEquals(float[].class, exec("float[] a = new float[1]; return a").getClass());
|
||||||
assertEquals(double[].class, exec("double[] a = new double[1] return a").getClass());
|
assertEquals(double[].class, exec("double[] a = new double[1]; return a").getClass());
|
||||||
assertEquals(boolean[].class, exec("boolean[] a = new boolean[1] return a").getClass());
|
assertEquals(boolean[].class, exec("boolean[] a = new boolean[1]; return a").getClass());
|
||||||
assertEquals(String[].class, exec("String[] a = new String[1] return a").getClass());
|
assertEquals(String[].class, exec("String[] a = new String[1]; return a").getClass());
|
||||||
assertEquals(Map[].class, exec("Map<String,Object>[] a = new Map<String,Object>[1] return a").getClass());
|
assertEquals(Map[].class, exec("Map<String,Object>[] a = new Map<String,Object>[1]; return a").getClass());
|
||||||
|
|
||||||
assertEquals(byte[][].class, exec("byte[][] a = new byte[1][2] return a").getClass());
|
assertEquals(byte[][].class, exec("byte[][] a = new byte[1][2]; return a").getClass());
|
||||||
assertEquals(short[][][].class, exec("short[][][] a = new short[1][2][3] return a").getClass());
|
assertEquals(short[][][].class, exec("short[][][] a = new short[1][2][3]; return a").getClass());
|
||||||
assertEquals(char[][][][].class, exec("char[][][][] a = new char[1][2][3][4] return a").getClass());
|
assertEquals(char[][][][].class, exec("char[][][][] a = new char[1][2][3][4]; return a").getClass());
|
||||||
assertEquals(int[][][][][].class, exec("int[][][][][] a = new int[1][2][3][4][5] return a").getClass());
|
assertEquals(int[][][][][].class, exec("int[][][][][] a = new int[1][2][3][4][5]; return a").getClass());
|
||||||
assertEquals(long[][].class, exec("long[][] a = new long[1][2] return a").getClass());
|
assertEquals(long[][].class, exec("long[][] a = new long[1][2]; return a").getClass());
|
||||||
assertEquals(float[][][].class, exec("float[][][] a = new float[1][2][3] return a").getClass());
|
assertEquals(float[][][].class, exec("float[][][] a = new float[1][2][3]; return a").getClass());
|
||||||
assertEquals(double[][][][].class, exec("double[][][][] a = new double[1][2][3][4] return a").getClass());
|
assertEquals(double[][][][].class, exec("double[][][][] a = new double[1][2][3][4]; return a").getClass());
|
||||||
assertEquals(boolean[][][][][].class, exec("boolean[][][][][] a = new boolean[1][2][3][4][5] return a").getClass());
|
assertEquals(boolean[][][][][].class, exec("boolean[][][][][] a = new boolean[1][2][3][4][5]; return a").getClass());
|
||||||
assertEquals(String[][].class, exec("String[][] a = new String[1][2] return a").getClass());
|
assertEquals(String[][].class, exec("String[][] a = new String[1][2]; return a").getClass());
|
||||||
assertEquals(Map[][][].class, exec("Map<String,Object>[][][] a = new Map<String,Object>[1][2][3] return a").getClass());
|
assertEquals(Map[][][].class, exec("Map<String,Object>[][][] a = new Map<String,Object>[1][2][3]; return a").getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testContinueStatement() {
|
public void testExpression() {
|
||||||
assertEquals(9, exec("int x = 0, y = 0 while (x < 10) { ++x if (x == 1) continue ++y } return y"));
|
assertEquals(10, exec("10"));
|
||||||
}
|
assertEquals(10, exec("5 + 5"));
|
||||||
|
assertEquals(10, exec("5 + 5"));
|
||||||
public void testBreakStatement() {
|
assertEquals(10, exec("params.param == 'yes' ? 10 : 5", Collections.singletonMap("param", "yes")));
|
||||||
assertEquals(4, exec("int x = 0, y = 0 while (x < 10) { ++x if (x == 5) break ++y } return y"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public void testReturnStatement() {
|
public void testReturnStatement() {
|
||||||
assertEquals(10, exec("return 10"));
|
assertEquals(10, exec("return 10"));
|
||||||
assertEquals(5, exec("int x = 5 return x"));
|
assertEquals(5, exec("int x = 5; return x"));
|
||||||
assertEquals(4, exec("int[] x = new int[2] x[1] = 4 return x[1]"));
|
assertEquals(4, exec("int[] x = new int[2]; x[1] = 4; return x[1]"));
|
||||||
assertEquals(5, ((short[])exec("short[] s = new short[3] s[1] = 5 return s"))[1]);
|
assertEquals(5, ((short[])exec("short[] s = new short[3]; s[1] = 5; return s"))[1]);
|
||||||
assertEquals(10, ((Map)exec("Map<String,Object> s = new HashMap< String,Object>() s.put(\"x\", 10) return s")).get("x"));
|
assertEquals(10, ((Map)exec("Map<String,Object> s = new HashMap< String,Object>(); s.put(\"x\", 10); return s")).get("x"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
package org.elasticsearch.painless;
|
package org.elasticsearch.painless;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class StringTests extends ScriptTestCase {
|
public class StringTests extends ScriptTestCase {
|
||||||
|
|
||||||
public void testAppend() {
|
public void testAppend() {
|
||||||
|
@ -63,6 +65,21 @@ public class StringTests extends ScriptTestCase {
|
||||||
assertEquals("cat" + "cat", exec("String s = 'cat'; return s + s;"));
|
assertEquals("cat" + "cat", exec("String s = 'cat'; return s + s;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAppendMultiple() {
|
||||||
|
assertEquals("cat" + true + "abc" + null, exec("String s = \"cat\"; return s + true + 'abc' + null;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAppendMany() {
|
||||||
|
StringBuilder script = new StringBuilder("String s = \"cat\"; return s");
|
||||||
|
StringBuilder result = new StringBuilder("cat");
|
||||||
|
for (int i = 0; i < 200 /* indy limit */ + 10; i++) {
|
||||||
|
final String s = String.format(Locale.ROOT, "%03d", i);
|
||||||
|
script.append(" + '").append(s).append("'.toString()");
|
||||||
|
result.append(s);
|
||||||
|
}
|
||||||
|
assertEquals(result.toString(), exec(script.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
public void testStringAPI() {
|
public void testStringAPI() {
|
||||||
assertEquals("", exec("return new String();"));
|
assertEquals("", exec("return new String();"));
|
||||||
assertEquals('x', exec("String s = \"x\"; return s.charAt(0);"));
|
assertEquals('x', exec("String s = \"x\"; return s.charAt(0);"));
|
||||||
|
@ -127,8 +144,8 @@ public class StringTests extends ScriptTestCase {
|
||||||
assertEquals("c", exec("return (String)(char)\"c\""));
|
assertEquals("c", exec("return (String)(char)\"c\""));
|
||||||
assertEquals("c", exec("return (String)(char)'c'"));
|
assertEquals("c", exec("return (String)(char)'c'"));
|
||||||
|
|
||||||
assertEquals('c', exec("String s = \"c\" (char)s"));
|
assertEquals('c', exec("String s = \"c\"; (char)s"));
|
||||||
assertEquals('c', exec("String s = 'c' (char)s"));
|
assertEquals('c', exec("String s = 'c'; (char)s"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals("cc", exec("return (String)(char)\"cc\""));
|
assertEquals("cc", exec("return (String)(char)\"cc\""));
|
||||||
|
@ -145,14 +162,14 @@ public class StringTests extends ScriptTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals('c', exec("String s = \"cc\" (char)s"));
|
assertEquals('c', exec("String s = \"cc\"; (char)s"));
|
||||||
fail();
|
fail();
|
||||||
} catch (final ClassCastException cce) {
|
} catch (final ClassCastException cce) {
|
||||||
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [char]."));
|
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [char]."));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals('c', exec("String s = 'cc' (char)s"));
|
assertEquals('c', exec("String s = 'cc'; (char)s"));
|
||||||
fail();
|
fail();
|
||||||
} catch (final ClassCastException cce) {
|
} catch (final ClassCastException cce) {
|
||||||
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [char]."));
|
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [char]."));
|
||||||
|
@ -163,8 +180,8 @@ public class StringTests extends ScriptTestCase {
|
||||||
assertEquals("c", exec("return (String)(Character)\"c\""));
|
assertEquals("c", exec("return (String)(Character)\"c\""));
|
||||||
assertEquals("c", exec("return (String)(Character)'c'"));
|
assertEquals("c", exec("return (String)(Character)'c'"));
|
||||||
|
|
||||||
assertEquals('c', exec("String s = \"c\" (Character)s"));
|
assertEquals('c', exec("String s = \"c\"; (Character)s"));
|
||||||
assertEquals('c', exec("String s = 'c' (Character)s"));
|
assertEquals('c', exec("String s = 'c'; (Character)s"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals("cc", exec("return (String)(Character)\"cc\""));
|
assertEquals("cc", exec("return (String)(Character)\"cc\""));
|
||||||
|
@ -181,14 +198,14 @@ public class StringTests extends ScriptTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals('c', exec("String s = \"cc\" (Character)s"));
|
assertEquals('c', exec("String s = \"cc\"; (Character)s"));
|
||||||
fail();
|
fail();
|
||||||
} catch (final ClassCastException cce) {
|
} catch (final ClassCastException cce) {
|
||||||
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [Character]."));
|
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [Character]."));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertEquals('c', exec("String s = 'cc' (Character)s"));
|
assertEquals('c', exec("String s = 'cc'; (Character)s"));
|
||||||
fail();
|
fail();
|
||||||
} catch (final ClassCastException cce) {
|
} catch (final ClassCastException cce) {
|
||||||
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [Character]."));
|
assertTrue(cce.getMessage().contains("Cannot cast [String] with length greater than one to [Character]."));
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
"The maximum number of statements that can be executed in a loop has been reached."));
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
|
||||||
expected = expectThrows(PainlessError.class, () -> {
|
expected = expectThrows(PainlessError.class, () -> {
|
||||||
exec("while (true) {int y = 5}");
|
exec("while (true) {int y = 5;}");
|
||||||
});
|
});
|
||||||
assertTrue(expected.getMessage().contains(
|
assertTrue(expected.getMessage().contains(
|
||||||
"The maximum number of statements that can be executed in a loop has been reached."));
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
@ -116,7 +116,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
"The maximum number of statements that can be executed in a loop has been reached."));
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
|
||||||
expected = expectThrows(PainlessError.class, () -> {
|
expected = expectThrows(PainlessError.class, () -> {
|
||||||
exec("for (;;) {int x = 5}");
|
exec("for (;;) {int x = 5;}");
|
||||||
fail("should have hit PainlessError");
|
fail("should have hit PainlessError");
|
||||||
});
|
});
|
||||||
assertTrue(expected.getMessage().contains(
|
assertTrue(expected.getMessage().contains(
|
||||||
|
@ -130,7 +130,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
"The maximum number of statements that can be executed in a loop has been reached."));
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
|
||||||
RuntimeException parseException = expectThrows(RuntimeException.class, () -> {
|
RuntimeException parseException = expectThrows(RuntimeException.class, () -> {
|
||||||
exec("try { int x } catch (PainlessError error) {}");
|
exec("try { int x; } catch (PainlessError error) {}");
|
||||||
fail("should have hit ParseException");
|
fail("should have hit ParseException");
|
||||||
});
|
});
|
||||||
assertTrue(parseException.getMessage().contains("Not a type [PainlessError]."));
|
assertTrue(parseException.getMessage().contains("Not a type [PainlessError]."));
|
||||||
|
|
Loading…
Reference in New Issue