From faee2323ab538bbae5e2f10513b26ebc5d50c797 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Sun, 12 Jun 2016 12:53:37 -0400 Subject: [PATCH] add array ctor refs --- .../src/main/antlr/PainlessParser.g4 | 2 +- .../painless/antlr/PainlessParser.java | 8 ++++--- .../elasticsearch/painless/antlr/Walker.java | 23 +++++++++++++++++-- .../elasticsearch/painless/node/EChain.java | 6 +++++ .../painless/node/SFunction.java | 11 +++++++-- .../elasticsearch/painless/ArrayTests.java | 4 ++++ .../painless/FunctionRefTests.java | 16 ++++++++++++- 7 files changed, 61 insertions(+), 9 deletions(-) diff --git a/modules/lang-painless/src/main/antlr/PainlessParser.g4 b/modules/lang-painless/src/main/antlr/PainlessParser.g4 index 7aec0db02c7..74d400da431 100644 --- a/modules/lang-painless/src/main/antlr/PainlessParser.g4 +++ b/modules/lang-painless/src/main/antlr/PainlessParser.g4 @@ -209,7 +209,7 @@ classFuncref // reference to a constructor, e.g. ArrayList::new // currently limited to simple non-array types constructorFuncref - : TYPE REF NEW + : decltype REF NEW ; // reference to an instance method, e.g. object::toString diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java index d8a07970afe..ae080fbb8aa 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java @@ -2969,7 +2969,9 @@ class PainlessParser extends Parser { } public static class ConstructorFuncrefContext extends ParserRuleContext { - public TerminalNode TYPE() { return getToken(PainlessParser.TYPE, 0); } + public DecltypeContext decltype() { + return getRuleContext(DecltypeContext.class,0); + } public TerminalNode REF() { return getToken(PainlessParser.REF, 0); } public TerminalNode NEW() { return getToken(PainlessParser.NEW, 0); } public ConstructorFuncrefContext(ParserRuleContext parent, int invokingState) { @@ -2990,7 +2992,7 @@ class PainlessParser extends Parser { enterOuterAlt(_localctx, 1); { setState(460); - match(TYPE); + decltype(); setState(461); match(REF); setState(462); @@ -3350,7 +3352,7 @@ class PainlessParser extends Parser { "\u01c4\u01c9\5\66\34\2\u01c5\u01c9\58\35\2\u01c6\u01c9\5:\36\2\u01c7\u01c9"+ "\5<\37\2\u01c8\u01c4\3\2\2\2\u01c8\u01c5\3\2\2\2\u01c8\u01c6\3\2\2\2\u01c8"+ "\u01c7\3\2\2\2\u01c9\65\3\2\2\2\u01ca\u01cb\7L\2\2\u01cb\u01cc\7\64\2"+ - "\2\u01cc\u01cd\7M\2\2\u01cd\67\3\2\2\2\u01ce\u01cf\7L\2\2\u01cf\u01d0"+ + "\2\u01cc\u01cd\7M\2\2\u01cd\67\3\2\2\2\u01ce\u01cf\5\26\f\2\u01cf\u01d0"+ "\7\64\2\2\u01d0\u01d1\7\26\2\2\u01d19\3\2\2\2\u01d2\u01d3\7M\2\2\u01d3"+ "\u01d4\7\64\2\2\u01d4\u01d5\7M\2\2\u01d5;\3\2\2\2\u01d6\u01d7\7\32\2\2"+ "\u01d7\u01d8\7\64\2\2\u01d8\u01d9\7M\2\2\u01d9=\3\2\2\2*AGZ]iq~\u0082"+ diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 3b6e0d47dee..6f00a337386 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -148,6 +148,7 @@ import org.elasticsearch.painless.node.SWhile; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Deque; import java.util.List; @@ -166,6 +167,7 @@ public final class Walker extends PainlessParserBaseVisitor { private final String sourceText; private final Deque reserved = new ArrayDeque<>(); + private final List synthetic = new ArrayList<>(); private Walker(String sourceName, String sourceText, CompilerSettings settings) { this.settings = settings; @@ -229,6 +231,8 @@ public final class Walker extends PainlessParserBaseVisitor { for (StatementContext statement : ctx.statement()) { statements.add((AStatement)visit(statement)); } + + functions.addAll(synthetic); return new SSource(sourceName, sourceText, (ExecuteReserved)reserved.pop(), location(ctx), functions, statements); } @@ -255,7 +259,7 @@ public final class Walker extends PainlessParserBaseVisitor { statements.add((AStatement)visit(statement)); } - return new SFunction((FunctionReserved)reserved.pop(), location(ctx), rtnType, name, paramTypes, paramNames, statements); + return new SFunction((FunctionReserved)reserved.pop(), location(ctx), rtnType, name, paramTypes, paramNames, statements, false); } @Override @@ -974,7 +978,22 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitConstructorFuncref(ConstructorFuncrefContext ctx) { - return new EFunctionRef(location(ctx), ctx.TYPE().getText(), ctx.NEW().getText()); + if (!ctx.decltype().LBRACE().isEmpty()) { + // array constructors are special: we need to make a synthetic method + // taking integer as argument and returning a new instance, and return a ref to that. + Location location = location(ctx); + String arrayType = ctx.decltype().getText(); + SReturn code = new SReturn(location, + new EChain(location, + new LNewArray(location, arrayType, Arrays.asList( + new EChain(location, + new LVariable(location, "size")))))); + String name = "lambda$" + synthetic.size(); + synthetic.add(new SFunction(new FunctionReserved(), location, arrayType, name, + Arrays.asList("int"), Arrays.asList("size"), Arrays.asList(code), true)); + return new EFunctionRef(location(ctx), "this", name); + } + return new EFunctionRef(location(ctx), ctx.decltype().getText(), ctx.NEW().getText()); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java index e21a1dd3134..4c5a44d1fac 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java @@ -29,6 +29,7 @@ import org.elasticsearch.painless.Operation; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; +import java.util.Arrays; import java.util.List; /** @@ -46,6 +47,11 @@ public final class EChain extends AExpression { Type promote = null; Cast there = null; Cast back = null; + + /** Creates a new simple EChain */ + public EChain(Location location, ALink link) { + this(location, Arrays.asList(link), false, false, null, null); + } public EChain(Location location, List links, boolean pre, boolean post, Operation operation, AExpression expression) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index a422e3b9d3d..57d80a51ea9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -50,6 +50,7 @@ public class SFunction extends AStatement { final List paramTypeStrs; final List paramNameStrs; final List statements; + final boolean synthetic; Type rtnType = null; List parameters = new ArrayList<>(); @@ -58,7 +59,8 @@ public class SFunction extends AStatement { Locals locals = null; public SFunction(FunctionReserved reserved, Location location, - String rtnType, String name, List paramTypes, List paramNames, List statements) { + String rtnType, String name, List paramTypes, + List paramNames, List statements, boolean synthetic) { super(location); this.reserved = reserved; @@ -67,6 +69,7 @@ public class SFunction extends AStatement { this.paramTypeStrs = Collections.unmodifiableList(paramTypes); this.paramNameStrs = Collections.unmodifiableList(paramNames); this.statements = Collections.unmodifiableList(statements); + this.synthetic = synthetic; } void generate() { @@ -138,7 +141,11 @@ public class SFunction extends AStatement { /** Writes the function to given ClassWriter. */ void write (ClassWriter writer, BitSet statements) { - final MethodWriter function = new MethodWriter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, method.method, writer, statements); + int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; + if (synthetic) { + access |= Opcodes.ACC_SYNTHETIC; + } + final MethodWriter function = new MethodWriter(access, method.method, writer, statements); write(function); function.endMethod(); } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java index 8dabacdd5f9..397a8856231 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayTests.java @@ -70,6 +70,10 @@ public class ArrayTests extends ScriptTestCase { assertEquals(1, exec("int x; def y = new def[1]; x = y[0] = 1; return x;")); } + public void testArrayVariable() { + assertEquals(1, exec("int x = 1; int[] y = new int[x]; return y.length")); + } + public void testForLoop() { assertEquals(999*1000/2, exec("def a = new int[1000]; for (int x = 0; x < a.length; x++) { a[x] = x; } "+ "int total = 0; for (int x = 0; x < a.length; x++) { total += a[x]; } return total;")); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java index a332ed7dd30..8ed2f3261cf 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java @@ -56,7 +56,21 @@ public class FunctionRefTests extends ScriptTestCase { "DoubleSummaryStatistics::combine); " + "return stats.getSum()")); } - + + public void testArrayCtorMethodRef() { + assertEquals(1.0D, + exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "def[] array = l.stream().toArray(Double[]::new);" + + "return array[0];")); + } + + public void testArrayCtorMethodRefDef() { + assertEquals(1.0D, + exec("def l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "def[] array = l.stream().toArray(Double[]::new);" + + "return array[0];")); + } + public void testCapturingMethodReference() { assertEquals("5", exec("Integer x = Integer.valueOf(5); return Optional.empty().orElseGet(x::toString);")); assertEquals("[]", exec("List l = new ArrayList(); return Optional.empty().orElseGet(l::toString);"));