diff --git a/modules/lang-painless/src/main/antlr/PainlessParser.g4 b/modules/lang-painless/src/main/antlr/PainlessParser.g4 index 31b6a9445ea..2e22cd21a2e 100644 --- a/modules/lang-painless/src/main/antlr/PainlessParser.g4 +++ b/modules/lang-painless/src/main/antlr/PainlessParser.g4 @@ -83,6 +83,7 @@ decltype funcref : TYPE REF ( ID | NEW ) + | ID REF ID ; declvar diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 1edaddea2c1..e60cbef79cf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -212,6 +212,8 @@ public final class Def { * until it finds a matching whitelisted method. If one is not found, it throws an exception. * Otherwise it returns a handle to the matching method. *

+ * @param lookup caller's lookup + * @param callSiteType callsite's type * @param receiverClass Class of the object to invoke the method on. * @param name Name of the method. * @param args args passed to callsite @@ -220,31 +222,103 @@ public final class Def { * @throws LambdaConversionException if a method reference cannot be converted to an functional interface * @throws IllegalArgumentException if no matching whitelisted method was found. */ - static MethodHandle lookupMethod(Lookup lookup, Class receiverClass, String name, - Object args[], long recipe) throws LambdaConversionException { - Method method = lookupMethodInternal(receiverClass, name, args.length - 1); + static MethodHandle lookupMethod(Lookup lookup, MethodType callSiteType, + Class receiverClass, String name, Object args[], long recipe) throws LambdaConversionException { + // simple case: no lambdas + if (recipe == 0) { + return lookupMethodInternal(receiverClass, name, args.length - 1).handle; + } + + // otherwise: first we have to compute the "real" arity. This is because we have extra arguments: + // e.g. f(a, g(x), b, h(y), i()) looks like f(a, g, x, b, h, y, i). + int arity = args.length - 1; + for (int i = 0; i < args.length; i++) { + if ((recipe & (1L << (i - 1))) != 0) { + String signature = (String) args[i]; + int numCaptures = Integer.parseInt(signature.substring(signature.indexOf(',')+1)); + arity -= numCaptures; + } + } + + // lookup the method with the proper arity, then we know everything (e.g. interface types of parameters). + // based on these we can finally link any remaining lambdas that were deferred. + Method method = lookupMethodInternal(receiverClass, name, arity); MethodHandle handle = method.handle; - if (recipe != 0) { - MethodHandle filters[] = new MethodHandle[args.length]; - for (int i = 0; i < args.length; i++) { - // its a functional reference, replace the argument with an impl - if ((recipe & (1L << (i - 1))) != 0) { - filters[i] = lookupReference(lookup, method.arguments.get(i - 1), (String) args[i]); + int replaced = 0; + for (int i = 1; i < args.length; i++) { + // its a functional reference, replace the argument with an impl + if ((recipe & (1L << (i - 1))) != 0) { + // decode signature of form 'type.call,2' + String signature = (String) args[i]; + int separator = signature.indexOf('.'); + int separator2 = signature.indexOf(','); + String type = signature.substring(1, separator); + String call = signature.substring(separator+1, separator2); + int numCaptures = Integer.parseInt(signature.substring(separator2+1)); + Class captures[] = new Class[numCaptures]; + for (int capture = 0; capture < captures.length; capture++) { + captures[capture] = callSiteType.parameterType(i + 1 + capture); } + MethodHandle filter; + Definition.Type interfaceType = method.arguments.get(i - 1 - replaced); + if (signature.charAt(0) == 'S') { + // the implementation is strongly typed, now that we know the interface type, + // we have everything. + filter = lookupReferenceInternal(lookup, + interfaceType, + type, + call, + captures); + } else if (signature.charAt(0) == 'D') { + // the interface type is now known, but we need to get the implementation. + // this is dynamically based on the receiver type (and cached separately, underneath + // this cache). It won't blow up since we never nest here (just references) + MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures); + CallSite nested = DefBootstrap.bootstrap(lookup, + call, + nestedType, + DefBootstrap.REFERENCE, + interfaceType.name); + filter = nested.dynamicInvoker(); + } else { + throw new AssertionError(); + } + // the filter now ignores the signature (placeholder) on the stack + filter = MethodHandles.dropArguments(filter, 0, String.class); + handle = MethodHandles.collectArguments(handle, i, filter); + i += numCaptures; + replaced += numCaptures; } - handle = MethodHandles.filterArguments(handle, 0, filters); } return handle; } + /** + * Returns an implementation of interfaceClass that calls receiverClass.name + *

+ * This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known, + * so we simply need to lookup the matching implementation method based on receiver type. + */ + static MethodHandle lookupReference(Lookup lookup, String interfaceClass, + Class receiverClass, String name) throws LambdaConversionException { + Definition.Type interfaceType = Definition.getType(interfaceClass); + Method interfaceMethod = interfaceType.struct.getFunctionalMethod(); + if (interfaceMethod == null) { + throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); + } + int arity = interfaceMethod.arguments.size(); + Method implMethod = lookupMethodInternal(receiverClass, name, arity); + return lookupReferenceInternal(lookup, interfaceType, implMethod.owner.name, implMethod.name, receiverClass); + } + /** Returns a method handle to an implementation of clazz, given method reference signature * @throws LambdaConversionException if a method reference cannot be converted to an functional interface */ - private static MethodHandle lookupReference(Lookup lookup, Definition.Type clazz, String signature) throws LambdaConversionException { - int separator = signature.indexOf('.'); - FunctionRef ref = new FunctionRef(clazz, signature.substring(0, separator), signature.substring(separator+1)); + private static MethodHandle lookupReferenceInternal(Lookup lookup, Definition.Type clazz, String type, + String call, Class... captures) throws LambdaConversionException { + FunctionRef ref = new FunctionRef(clazz, type, call, captures); final CallSite callSite; if (ref.needsBridges()) { callSite = LambdaMetafactory.altMetafactory(lookup, @@ -265,9 +339,7 @@ public final class Def { ref.samMethodType, 0); } - // we could actually invoke and cache here (in non-capturing cases), but this is not a speedup. - MethodHandle factory = callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz)); - return MethodHandles.dropArguments(factory, 0, Object.class); + return callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz, captures)); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java index 5dc773672a7..040008c0d62 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java @@ -60,6 +60,8 @@ public final class DefBootstrap { public static final int ARRAY_STORE = 4; /** static bootstrap parameter indicating a dynamic iteration, e.g. for (x : y) */ public static final int ITERATOR = 5; + /** static bootstrap parameter indicating a dynamic method reference, e.g. foo::bar */ + public static final int REFERENCE = 6; /** * CallSite that implements the polymorphic inlining cache (PIC). @@ -71,17 +73,15 @@ public final class DefBootstrap { private final Lookup lookup; private final String name; private final int flavor; - private final long recipe; + private final Object[] args; int depth; // pkg-protected for testing - PIC(Lookup lookup, String name, MethodType type, int flavor, long recipe) { + PIC(Lookup lookup, String name, MethodType type, int flavor, Object[] args) { super(type); this.lookup = lookup; this.name = name; this.flavor = flavor; - this.recipe = recipe; - assert recipe == 0 || flavor == METHOD_CALL; - assert Long.bitCount(recipe) <= type.parameterCount(); + this.args = args; final MethodHandle fallback = FALLBACK.bindTo(this) .asCollector(Object[].class, type.parameterCount()) @@ -101,10 +101,10 @@ public final class DefBootstrap { /** * Does a slow lookup against the whitelist. */ - private MethodHandle lookup(int flavor, Class clazz, String name, Object[] args, long recipe) throws Throwable { + private MethodHandle lookup(int flavor, Class clazz, String name, Object[] args) throws Throwable { switch(flavor) { case METHOD_CALL: - return Def.lookupMethod(lookup, clazz, name, args, recipe); + return Def.lookupMethod(lookup, type(), clazz, name, args, (Long) this.args[0]); case LOAD: return Def.lookupGetter(clazz, name); case STORE: @@ -115,6 +115,8 @@ public final class DefBootstrap { return Def.lookupArrayStore(clazz); case ITERATOR: return Def.lookupIterator(clazz); + case REFERENCE: + return Def.lookupReference(lookup, (String) this.args[0], clazz, name); default: throw new AssertionError(); } } @@ -128,7 +130,7 @@ public final class DefBootstrap { final MethodType type = type(); final Object receiver = args[0]; final Class receiverClass = receiver.getClass(); - final MethodHandle target = lookup(flavor, receiverClass, name, args, recipe).asType(type); + final MethodHandle target = lookup(flavor, receiverClass, name, args).asType(type); if (depth >= MAX_DEPTH) { // revert to a vtable call @@ -170,8 +172,36 @@ public final class DefBootstrap { *

* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic */ - public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor, long recipe) { - return new PIC(lookup, name, type, flavor, recipe); + public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor, Object... args) { + // validate arguments + switch(flavor) { + case METHOD_CALL: + if (args.length != 1) { + throw new BootstrapMethodError("Invalid number of parameters for method call"); + } + if (args[0] instanceof Long == false) { + throw new BootstrapMethodError("Illegal parameter for method call: " + args[0]); + } + long recipe = (Long) args[0]; + if (Long.bitCount(recipe) > type.parameterCount()) { + throw new BootstrapMethodError("Illegal recipe for method call: too many bits"); + } + break; + case REFERENCE: + if (args.length != 1) { + throw new BootstrapMethodError("Invalid number of parameters for reference call"); + } + if (args[0] instanceof String == false) { + throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]); + } + break; + default: + if (args.length > 0) { + throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor); + } + break; + } + return new PIC(lookup, name, type, flavor, args); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java index a8d4a803361..8a91bd7d7fd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java @@ -890,8 +890,7 @@ public final class Definition { throw new AssertionError(e); } } - owner.methods.put(methodKey, - new Method(method.name, owner, method.rtn, method.arguments, method.method, method.modifiers, method.handle)); + owner.methods.put(methodKey, method); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java index bef83daaad3..603023e61fe 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java @@ -1,5 +1,7 @@ package org.elasticsearch.painless; +import java.util.function.Function; + /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with @@ -63,4 +65,9 @@ public class FeatureTest { public static boolean overloadedStatic(boolean whatToReturn) { return whatToReturn; } + + /** method taking two functions! */ + public Object twoFunctionsOfX(Function f, Function g) { + return f.apply(g.apply(x)); + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java index 03b6cc604c9..fb0175cc97e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java @@ -53,8 +53,9 @@ public class FunctionRef { * @param expected interface type to implement. * @param type the left hand side of a method reference expression * @param call the right hand side of a method reference expression + * @param captures captured arguments */ - public FunctionRef(Definition.Type expected, String type, String call) { + public FunctionRef(Definition.Type expected, String type, String call, Class... captures) { boolean isCtorReference = "new".equals(call); // check its really a functional interface // for e.g. Comparable @@ -66,7 +67,7 @@ public class FunctionRef { // e.g. compareTo invokedName = method.name; // e.g. (Object)Comparator - invokedType = MethodType.methodType(expected.clazz); + invokedType = MethodType.methodType(expected.clazz, captures); // e.g. (Object,Object)int interfaceMethodType = method.handle.type().dropParameterTypes(0, 1); // lookup requested method @@ -80,7 +81,15 @@ public class FunctionRef { Definition.Method staticImpl = struct.staticMethods.get(new Definition.MethodKey(call, method.arguments.size())); if (staticImpl == null) { // otherwise a virtual impl - impl = struct.methods.get(new Definition.MethodKey(call, method.arguments.size()-1)); + final int arity; + if (captures.length > 0) { + // receiver captured + arity = method.arguments.size(); + } else { + // receiver passed + arity = method.arguments.size() - 1; + } + impl = struct.methods.get(new Definition.MethodKey(call, arity)); } else { impl = staticImpl; } @@ -98,12 +107,19 @@ public class FunctionRef { } else { tag = Opcodes.H_INVOKEVIRTUAL; } - implMethodASM = new Handle(tag, struct.type.getInternalName(), impl.name, impl.method.getDescriptor()); + if (impl.owner.clazz.isInterface()) { + implMethodASM = new Handle(tag, struct.type.getInternalName(), impl.name, impl.method.getDescriptor()); + } else { + implMethodASM = new Handle(tag, impl.owner.type.getInternalName(), impl.name, impl.method.getDescriptor()); + } implMethod = impl.handle; if (isCtorReference) { samMethodType = MethodType.methodType(interfaceMethodType.returnType(), impl.handle.type().parameterArray()); } else if (Modifier.isStatic(impl.modifiers)) { samMethodType = impl.handle.type(); + } else if (captures.length > 0) { + // drop the receiver, we capture it + samMethodType = impl.handle.type().dropParameterTypes(0, 1); } else { // ensure the receiver type is exact and not a superclass type samMethodType = impl.handle.type().changeParameterType(0, struct.clazz); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java index 923c2e3f1e8..641a5582ab8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java @@ -22,7 +22,6 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Type; -import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java index 8fdc48e19ba..b303089b339 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java @@ -119,7 +119,7 @@ final class ScriptImpl implements ExecutableScript, LeafSearchScript { public Object run() { try { return executable.execute(variables, scorer, doc, aggregationValue); - } catch (PainlessError | BootstrapMethodError | Exception t) { + } catch (PainlessError | BootstrapMethodError | IllegalAccessError | Exception t) { throw convertToScriptException(t); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 03879a0a3a0..4ec835b02c5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -68,7 +68,7 @@ public final class WriterConstants { /** dynamic callsite bootstrap signature */ public final static MethodType DEF_BOOTSTRAP_TYPE = - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, long.class); + MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, Object[].class); public final static Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(DefBootstrap.class), "bootstrap", DEF_BOOTSTRAP_TYPE.toMethodDescriptorString()); 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 acaf00a5765..5eca024e812 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 @@ -1181,7 +1181,10 @@ class PainlessParser extends Parser { public static class FuncrefContext extends ParserRuleContext { public TerminalNode TYPE() { return getToken(PainlessParser.TYPE, 0); } public TerminalNode REF() { return getToken(PainlessParser.REF, 0); } - public TerminalNode ID() { return getToken(PainlessParser.ID, 0); } + public List ID() { return getTokens(PainlessParser.ID); } + public TerminalNode ID(int i) { + return getToken(PainlessParser.ID, i); + } public TerminalNode NEW() { return getToken(PainlessParser.NEW, 0); } public FuncrefContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -1199,19 +1202,37 @@ class PainlessParser extends Parser { enterRule(_localctx, 22, RULE_funcref); int _la; try { - enterOuterAlt(_localctx, 1); - { - setState(201); - match(TYPE); - setState(202); - match(REF); - setState(203); - _la = _input.LA(1); - if ( !(_la==NEW || _la==ID) ) { - _errHandler.recoverInline(this); - } else { - consume(); - } + setState(207); + switch (_input.LA(1)) { + case TYPE: + enterOuterAlt(_localctx, 1); + { + setState(201); + match(TYPE); + setState(202); + match(REF); + setState(203); + _la = _input.LA(1); + if ( !(_la==NEW || _la==ID) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + break; + case ID: + enterOuterAlt(_localctx, 2); + { + setState(204); + match(ID); + setState(205); + match(REF); + setState(206); + match(ID); + } + break; + default: + throw new NoViableAltException(this); } } catch (RecognitionException re) { @@ -1249,15 +1270,15 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(205); + setState(209); match(ID); - setState(208); + setState(212); _la = _input.LA(1); if (_la==ASSIGN) { { - setState(206); + setState(210); match(ASSIGN); - setState(207); + setState(211); expression(0); } } @@ -1301,17 +1322,17 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(210); - match(CATCH); - setState(211); - match(LP); - setState(212); - match(TYPE); - setState(213); - match(ID); setState(214); - match(RP); + match(CATCH); setState(215); + match(LP); + setState(216); + match(TYPE); + setState(217); + match(ID); + setState(218); + match(RP); + setState(219); block(); } } @@ -1347,7 +1368,7 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(217); + setState(221); _la = _input.LA(1); if ( !(_la==EOF || _la==SEMICOLON) ) { _errHandler.recoverInline(this); @@ -1516,24 +1537,24 @@ class PainlessParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(228); - switch ( getInterpreter().adaptivePredict(_input,18,_ctx) ) { + setState(232); + switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) { case 1: { _localctx = new AssignmentContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(220); + setState(224); chain(true); - setState(221); + setState(225); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ASSIGN) | (1L << AADD) | (1L << ASUB) | (1L << AMUL) | (1L << ADIV) | (1L << AREM) | (1L << AAND) | (1L << AXOR) | (1L << AOR) | (1L << ALSH) | (1L << ARSH) | (1L << AUSH))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(222); + setState(226); expression(1); ((AssignmentContext)_localctx).s = false; } @@ -1543,37 +1564,37 @@ class PainlessParser extends Parser { _localctx = new SingleContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(225); + setState(229); ((SingleContext)_localctx).u = unary(false); ((SingleContext)_localctx).s = ((SingleContext)_localctx).u.s; } break; } _ctx.stop = _input.LT(-1); - setState(289); + setState(293); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,20,_ctx); + _alt = getInterpreter().adaptivePredict(_input,21,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(287); - switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) { + setState(291); + switch ( getInterpreter().adaptivePredict(_input,20,_ctx) ) { case 1: { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(230); + setState(234); if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)"); - setState(231); + setState(235); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIV) | (1L << REM))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(232); + setState(236); expression(13); ((BinaryContext)_localctx).s = false; } @@ -1582,16 +1603,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(235); + setState(239); if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)"); - setState(236); + setState(240); _la = _input.LA(1); if ( !(_la==ADD || _la==SUB) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(237); + setState(241); expression(12); ((BinaryContext)_localctx).s = false; } @@ -1600,16 +1621,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(240); + setState(244); if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); - setState(241); + setState(245); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LSH) | (1L << RSH) | (1L << USH))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(242); + setState(246); expression(11); ((BinaryContext)_localctx).s = false; } @@ -1618,16 +1639,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(245); + setState(249); if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); - setState(246); + setState(250); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LT) | (1L << LTE) | (1L << GT) | (1L << GTE))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(247); + setState(251); expression(10); ((CompContext)_localctx).s = false; } @@ -1636,16 +1657,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(250); + setState(254); if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)"); - setState(251); + setState(255); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << EQ) | (1L << EQR) | (1L << NE) | (1L << NER))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(252); + setState(256); expression(9); ((CompContext)_localctx).s = false; } @@ -1654,11 +1675,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(255); + setState(259); if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); - setState(256); + setState(260); match(BWAND); - setState(257); + setState(261); expression(8); ((BinaryContext)_localctx).s = false; } @@ -1667,11 +1688,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(260); + setState(264); if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); - setState(261); + setState(265); match(XOR); - setState(262); + setState(266); expression(7); ((BinaryContext)_localctx).s = false; } @@ -1680,11 +1701,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(265); + setState(269); if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); - setState(266); + setState(270); match(BWOR); - setState(267); + setState(271); expression(6); ((BinaryContext)_localctx).s = false; } @@ -1693,11 +1714,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(270); + setState(274); if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(271); + setState(275); match(BOOLAND); - setState(272); + setState(276); expression(5); ((BoolContext)_localctx).s = false; } @@ -1706,11 +1727,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(275); + setState(279); if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(276); + setState(280); match(BOOLOR); - setState(277); + setState(281); expression(4); ((BoolContext)_localctx).s = false; } @@ -1719,15 +1740,15 @@ class PainlessParser extends Parser { { _localctx = new ConditionalContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(280); - if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(281); - match(COND); - setState(282); - ((ConditionalContext)_localctx).e0 = expression(0); - setState(283); - match(COLON); setState(284); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(285); + match(COND); + setState(286); + ((ConditionalContext)_localctx).e0 = expression(0); + setState(287); + match(COLON); + setState(288); ((ConditionalContext)_localctx).e1 = expression(2); ((ConditionalContext)_localctx).s = ((ConditionalContext)_localctx).e0.s && ((ConditionalContext)_localctx).e1.s; } @@ -1735,9 +1756,9 @@ class PainlessParser extends Parser { } } } - setState(291); + setState(295); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,20,_ctx); + _alt = getInterpreter().adaptivePredict(_input,21,_ctx); } } } @@ -1882,22 +1903,22 @@ class PainlessParser extends Parser { enterRule(_localctx, 32, RULE_unary); int _la; try { - setState(321); - switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { + setState(325); + switch ( getInterpreter().adaptivePredict(_input,22,_ctx) ) { case 1: _localctx = new PreContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(292); + setState(296); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(293); + setState(297); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(294); + setState(298); chain(true); } break; @@ -1905,11 +1926,11 @@ class PainlessParser extends Parser { _localctx = new PostContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(295); + setState(299); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(296); + setState(300); chain(true); - setState(297); + setState(301); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); @@ -1922,9 +1943,9 @@ class PainlessParser extends Parser { _localctx = new ReadContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(299); + setState(303); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(300); + setState(304); chain(false); } break; @@ -1932,9 +1953,9 @@ class PainlessParser extends Parser { _localctx = new NumericContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(301); + setState(305); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(302); + setState(306); _la = _input.LA(1); if ( !(((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (OCTAL - 64)) | (1L << (HEX - 64)) | (1L << (INTEGER - 64)) | (1L << (DECIMAL - 64)))) != 0)) ) { _errHandler.recoverInline(this); @@ -1948,9 +1969,9 @@ class PainlessParser extends Parser { _localctx = new TrueContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(304); + setState(308); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(305); + setState(309); match(TRUE); ((TrueContext)_localctx).s = false; } @@ -1959,9 +1980,9 @@ class PainlessParser extends Parser { _localctx = new FalseContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(307); + setState(311); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(308); + setState(312); match(FALSE); ((FalseContext)_localctx).s = false; } @@ -1970,9 +1991,9 @@ class PainlessParser extends Parser { _localctx = new NullContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(310); + setState(314); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(311); + setState(315); match(NULL); ((NullContext)_localctx).s = false; } @@ -1981,16 +2002,16 @@ class PainlessParser extends Parser { _localctx = new OperatorContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(313); + setState(317); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(314); + setState(318); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L << SUB))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(315); + setState(319); unary(false); } break; @@ -1998,13 +2019,13 @@ class PainlessParser extends Parser { _localctx = new CastContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(316); + setState(320); match(LP); - setState(317); + setState(321); decltype(); - setState(318); + setState(322); match(RP); - setState(319); + setState(323); unary(_localctx.c); } break; @@ -2113,29 +2134,29 @@ class PainlessParser extends Parser { enterRule(_localctx, 34, RULE_chain); try { int _alt; - setState(357); - switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { + setState(361); + switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) { case 1: _localctx = new DynamicContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(323); - ((DynamicContext)_localctx).p = primary(_localctx.c); setState(327); + ((DynamicContext)_localctx).p = primary(_localctx.c); + setState(331); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(324); + setState(328); secondary(((DynamicContext)_localctx).p.s); } } } - setState(329); + setState(333); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); } } break; @@ -2143,25 +2164,25 @@ class PainlessParser extends Parser { _localctx = new StaticContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(330); + setState(334); decltype(); - setState(331); - dot(); setState(335); + dot(); + setState(339); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,23,_ctx); + _alt = getInterpreter().adaptivePredict(_input,24,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(332); + setState(336); secondary(true); } } } - setState(337); + setState(341); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,23,_ctx); + _alt = getInterpreter().adaptivePredict(_input,24,_ctx); } } break; @@ -2169,11 +2190,11 @@ class PainlessParser extends Parser { _localctx = new NewarrayContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(338); + setState(342); match(NEW); - setState(339); + setState(343); match(TYPE); - setState(344); + setState(348); _errHandler.sync(this); _alt = 1; do { @@ -2181,11 +2202,11 @@ class PainlessParser extends Parser { case 1: { { - setState(340); + setState(344); match(LBRACE); - setState(341); + setState(345); expression(0); - setState(342); + setState(346); match(RBRACE); } } @@ -2193,31 +2214,31 @@ class PainlessParser extends Parser { default: throw new NoViableAltException(this); } - setState(346); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,24,_ctx); - } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); - setState(355); - switch ( getInterpreter().adaptivePredict(_input,26,_ctx) ) { - case 1: - { - setState(348); - dot(); - setState(352); + setState(350); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,25,_ctx); + } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); + setState(359); + switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { + case 1: + { + setState(352); + dot(); + setState(356); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,26,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(349); + setState(353); secondary(true); } } } - setState(354); + setState(358); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,25,_ctx); + _alt = getInterpreter().adaptivePredict(_input,26,_ctx); } } break; @@ -2329,19 +2350,19 @@ class PainlessParser extends Parser { PrimaryContext _localctx = new PrimaryContext(_ctx, getState(), c); enterRule(_localctx, 36, RULE_primary); try { - setState(377); - switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) { + setState(381); + switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) { case 1: _localctx = new ExprprecContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(359); + setState(363); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(360); + setState(364); match(LP); - setState(361); + setState(365); ((ExprprecContext)_localctx).e = expression(0); - setState(362); + setState(366); match(RP); ((ExprprecContext)_localctx).s = ((ExprprecContext)_localctx).e.s; } @@ -2350,13 +2371,13 @@ class PainlessParser extends Parser { _localctx = new ChainprecContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(365); + setState(369); if (!( _localctx.c )) throw new FailedPredicateException(this, " $c "); - setState(366); + setState(370); match(LP); - setState(367); + setState(371); unary(true); - setState(368); + setState(372); match(RP); } break; @@ -2364,7 +2385,7 @@ class PainlessParser extends Parser { _localctx = new StringContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(370); + setState(374); match(STRING); } break; @@ -2372,7 +2393,7 @@ class PainlessParser extends Parser { _localctx = new VariableContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(371); + setState(375); match(ID); } break; @@ -2380,9 +2401,9 @@ class PainlessParser extends Parser { _localctx = new CalllocalContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(372); + setState(376); match(ID); - setState(373); + setState(377); arguments(); } break; @@ -2390,11 +2411,11 @@ class PainlessParser extends Parser { _localctx = new NewobjectContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(374); + setState(378); match(NEW); - setState(375); + setState(379); match(TYPE); - setState(376); + setState(380); arguments(); } break; @@ -2436,23 +2457,23 @@ class PainlessParser extends Parser { SecondaryContext _localctx = new SecondaryContext(_ctx, getState(), s); enterRule(_localctx, 38, RULE_secondary); try { - setState(383); - switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) { + setState(387); + switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(379); + setState(383); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(380); + setState(384); dot(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(381); + setState(385); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(382); + setState(386); brace(); } break; @@ -2510,17 +2531,17 @@ class PainlessParser extends Parser { enterRule(_localctx, 40, RULE_dot); int _la; try { - setState(390); - switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { + setState(394); + switch ( getInterpreter().adaptivePredict(_input,31,_ctx) ) { case 1: _localctx = new CallinvokeContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(385); + setState(389); match(DOT); - setState(386); + setState(390); match(DOTID); - setState(387); + setState(391); arguments(); } break; @@ -2528,9 +2549,9 @@ class PainlessParser extends Parser { _localctx = new FieldaccessContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(388); + setState(392); match(DOT); - setState(389); + setState(393); _la = _input.LA(1); if ( !(_la==DOTINTEGER || _la==DOTID) ) { _errHandler.recoverInline(this); @@ -2584,11 +2605,11 @@ class PainlessParser extends Parser { _localctx = new BraceaccessContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(392); + setState(396); match(LBRACE); - setState(393); + setState(397); expression(0); - setState(394); + setState(398); match(RBRACE); } } @@ -2635,34 +2656,34 @@ class PainlessParser extends Parser { enterOuterAlt(_localctx, 1); { { - setState(396); + setState(400); match(LP); - setState(405); - switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { + setState(409); + switch ( getInterpreter().adaptivePredict(_input,33,_ctx) ) { case 1: { - setState(397); + setState(401); argument(); - setState(402); + setState(406); _errHandler.sync(this); _la = _input.LA(1); while (_la==COMMA) { { { - setState(398); + setState(402); match(COMMA); - setState(399); + setState(403); argument(); } } - setState(404); + setState(408); _errHandler.sync(this); _la = _input.LA(1); } } break; } - setState(407); + setState(411); match(RP); } } @@ -2700,19 +2721,19 @@ class PainlessParser extends Parser { ArgumentContext _localctx = new ArgumentContext(_ctx, getState()); enterRule(_localctx, 46, RULE_argument); try { - setState(411); - switch ( getInterpreter().adaptivePredict(_input,33,_ctx) ) { + setState(415); + switch ( getInterpreter().adaptivePredict(_input,34,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(409); + setState(413); expression(0); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(410); + setState(414); funcref(); } break; @@ -2819,7 +2840,7 @@ class PainlessParser extends Parser { } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u01a0\4\2\t\2\4"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u01a4\4\2\t\2\4"+ "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ @@ -2833,144 +2854,146 @@ class PainlessParser extends Parser { "\5\3\5\5\5\u00a4\n\5\3\6\3\6\5\6\u00a8\n\6\3\7\3\7\7\7\u00ac\n\7\f\7\16"+ "\7\u00af\13\7\3\7\3\7\3\b\3\b\3\t\3\t\5\t\u00b7\n\t\3\n\3\n\3\13\3\13"+ "\3\13\3\13\7\13\u00bf\n\13\f\13\16\13\u00c2\13\13\3\f\3\f\3\f\7\f\u00c7"+ - "\n\f\f\f\16\f\u00ca\13\f\3\r\3\r\3\r\3\r\3\16\3\16\3\16\5\16\u00d3\n\16"+ - "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+ - "\3\21\3\21\3\21\3\21\5\21\u00e7\n\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ + "\n\f\f\f\16\f\u00ca\13\f\3\r\3\r\3\r\3\r\3\r\3\r\5\r\u00d2\n\r\3\16\3"+ + "\16\3\16\5\16\u00d7\n\16\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\5\21\u00eb\n\21\3\21\3\21"+ "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ - "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\7\21\u0122\n\21\f\21\16\21\u0125"+ - "\13\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\7\21"+ + "\u0126\n\21\f\21\16\21\u0129\13\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ - "\3\22\3\22\5\22\u0144\n\22\3\23\3\23\7\23\u0148\n\23\f\23\16\23\u014b"+ - "\13\23\3\23\3\23\3\23\7\23\u0150\n\23\f\23\16\23\u0153\13\23\3\23\3\23"+ - "\3\23\3\23\3\23\3\23\6\23\u015b\n\23\r\23\16\23\u015c\3\23\3\23\7\23\u0161"+ - "\n\23\f\23\16\23\u0164\13\23\5\23\u0166\n\23\5\23\u0168\n\23\3\24\3\24"+ - "\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24"+ - "\3\24\3\24\5\24\u017c\n\24\3\25\3\25\3\25\3\25\5\25\u0182\n\25\3\26\3"+ - "\26\3\26\3\26\3\26\5\26\u0189\n\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30"+ - "\3\30\7\30\u0193\n\30\f\30\16\30\u0196\13\30\5\30\u0198\n\30\3\30\3\30"+ - "\3\31\3\31\5\31\u019e\n\31\3\31\2\3 \32\2\4\6\b\n\f\16\20\22\24\26\30"+ - "\32\34\36 \"$&(*,.\60\2\16\4\2\26\26KK\3\3\r\r\3\2\66A\3\2\34\36\3\2\37"+ - " \3\2!#\3\2$\'\3\2(+\3\2\64\65\3\2BE\4\2\32\33\37 \3\2LM\u01c8\2\65\3"+ - "\2\2\2\4@\3\2\2\2\6E\3\2\2\2\b\u00a3\3\2\2\2\n\u00a7\3\2\2\2\f\u00a9\3"+ - "\2\2\2\16\u00b2\3\2\2\2\20\u00b6\3\2\2\2\22\u00b8\3\2\2\2\24\u00ba\3\2"+ - "\2\2\26\u00c3\3\2\2\2\30\u00cb\3\2\2\2\32\u00cf\3\2\2\2\34\u00d4\3\2\2"+ - "\2\36\u00db\3\2\2\2 \u00e6\3\2\2\2\"\u0143\3\2\2\2$\u0167\3\2\2\2&\u017b"+ - "\3\2\2\2(\u0181\3\2\2\2*\u0188\3\2\2\2,\u018a\3\2\2\2.\u018e\3\2\2\2\60"+ - "\u019d\3\2\2\2\62\64\5\4\3\2\63\62\3\2\2\2\64\67\3\2\2\2\65\63\3\2\2\2"+ - "\65\66\3\2\2\2\66;\3\2\2\2\67\65\3\2\2\28:\5\b\5\298\3\2\2\2:=\3\2\2\2"+ - ";9\3\2\2\2;<\3\2\2\2<>\3\2\2\2=;\3\2\2\2>?\7\2\2\3?\3\3\2\2\2@A\5\26\f"+ - "\2AB\7K\2\2BC\5\6\4\2CD\5\f\7\2D\5\3\2\2\2EQ\7\t\2\2FG\5\26\f\2GN\7K\2"+ - "\2HI\7\f\2\2IJ\5\26\f\2JK\7K\2\2KM\3\2\2\2LH\3\2\2\2MP\3\2\2\2NL\3\2\2"+ - "\2NO\3\2\2\2OR\3\2\2\2PN\3\2\2\2QF\3\2\2\2QR\3\2\2\2RS\3\2\2\2ST\7\n\2"+ - "\2T\7\3\2\2\2UV\7\16\2\2VW\7\t\2\2WX\5 \21\2XY\7\n\2\2Y]\5\n\6\2Z[\7\17"+ - "\2\2[^\5\n\6\2\\^\6\5\2\2]Z\3\2\2\2]\\\3\2\2\2^\u00a4\3\2\2\2_`\7\20\2"+ - "\2`a\7\t\2\2ab\5 \21\2be\7\n\2\2cf\5\n\6\2df\5\16\b\2ec\3\2\2\2ed\3\2"+ - "\2\2f\u00a4\3\2\2\2gh\7\21\2\2hi\5\f\7\2ij\7\20\2\2jk\7\t\2\2kl\5 \21"+ - "\2lm\7\n\2\2mn\5\36\20\2n\u00a4\3\2\2\2op\7\22\2\2pr\7\t\2\2qs\5\20\t"+ - "\2rq\3\2\2\2rs\3\2\2\2st\3\2\2\2tv\7\r\2\2uw\5 \21\2vu\3\2\2\2vw\3\2\2"+ - "\2wx\3\2\2\2xz\7\r\2\2y{\5\22\n\2zy\3\2\2\2z{\3\2\2\2{|\3\2\2\2|\177\7"+ - "\n\2\2}\u0080\5\n\6\2~\u0080\5\16\b\2\177}\3\2\2\2\177~\3\2\2\2\u0080"+ - "\u00a4\3\2\2\2\u0081\u0082\7\22\2\2\u0082\u0083\7\t\2\2\u0083\u0084\5"+ - "\26\f\2\u0084\u0085\7K\2\2\u0085\u0086\7\62\2\2\u0086\u0087\5 \21\2\u0087"+ - "\u0088\7\n\2\2\u0088\u0089\5\n\6\2\u0089\u00a4\3\2\2\2\u008a\u008b\5\24"+ - "\13\2\u008b\u008c\5\36\20\2\u008c\u00a4\3\2\2\2\u008d\u008e\7\23\2\2\u008e"+ - "\u00a4\5\36\20\2\u008f\u0090\7\24\2\2\u0090\u00a4\5\36\20\2\u0091\u0092"+ - "\7\25\2\2\u0092\u0093\5 \21\2\u0093\u0094\5\36\20\2\u0094\u00a4\3\2\2"+ - "\2\u0095\u0096\7\27\2\2\u0096\u0098\5\f\7\2\u0097\u0099\5\34\17\2\u0098"+ - "\u0097\3\2\2\2\u0099\u009a\3\2\2\2\u009a\u0098\3\2\2\2\u009a\u009b\3\2"+ - "\2\2\u009b\u00a4\3\2\2\2\u009c\u009d\7\31\2\2\u009d\u009e\5 \21\2\u009e"+ - "\u009f\5\36\20\2\u009f\u00a4\3\2\2\2\u00a0\u00a1\5 \21\2\u00a1\u00a2\5"+ - "\36\20\2\u00a2\u00a4\3\2\2\2\u00a3U\3\2\2\2\u00a3_\3\2\2\2\u00a3g\3\2"+ - "\2\2\u00a3o\3\2\2\2\u00a3\u0081\3\2\2\2\u00a3\u008a\3\2\2\2\u00a3\u008d"+ - "\3\2\2\2\u00a3\u008f\3\2\2\2\u00a3\u0091\3\2\2\2\u00a3\u0095\3\2\2\2\u00a3"+ - "\u009c\3\2\2\2\u00a3\u00a0\3\2\2\2\u00a4\t\3\2\2\2\u00a5\u00a8\5\f\7\2"+ - "\u00a6\u00a8\5\b\5\2\u00a7\u00a5\3\2\2\2\u00a7\u00a6\3\2\2\2\u00a8\13"+ - "\3\2\2\2\u00a9\u00ad\7\5\2\2\u00aa\u00ac\5\b\5\2\u00ab\u00aa\3\2\2\2\u00ac"+ - "\u00af\3\2\2\2\u00ad\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b0\3\2"+ - "\2\2\u00af\u00ad\3\2\2\2\u00b0\u00b1\7\6\2\2\u00b1\r\3\2\2\2\u00b2\u00b3"+ - "\7\r\2\2\u00b3\17\3\2\2\2\u00b4\u00b7\5\24\13\2\u00b5\u00b7\5 \21\2\u00b6"+ - "\u00b4\3\2\2\2\u00b6\u00b5\3\2\2\2\u00b7\21\3\2\2\2\u00b8\u00b9\5 \21"+ - "\2\u00b9\23\3\2\2\2\u00ba\u00bb\5\26\f\2\u00bb\u00c0\5\32\16\2\u00bc\u00bd"+ - "\7\f\2\2\u00bd\u00bf\5\32\16\2\u00be\u00bc\3\2\2\2\u00bf\u00c2\3\2\2\2"+ - "\u00c0\u00be\3\2\2\2\u00c0\u00c1\3\2\2\2\u00c1\25\3\2\2\2\u00c2\u00c0"+ - "\3\2\2\2\u00c3\u00c8\7J\2\2\u00c4\u00c5\7\7\2\2\u00c5\u00c7\7\b\2\2\u00c6"+ - "\u00c4\3\2\2\2\u00c7\u00ca\3\2\2\2\u00c8\u00c6\3\2\2\2\u00c8\u00c9\3\2"+ - "\2\2\u00c9\27\3\2\2\2\u00ca\u00c8\3\2\2\2\u00cb\u00cc\7J\2\2\u00cc\u00cd"+ - "\7\63\2\2\u00cd\u00ce\t\2\2\2\u00ce\31\3\2\2\2\u00cf\u00d2\7K\2\2\u00d0"+ - "\u00d1\7\66\2\2\u00d1\u00d3\5 \21\2\u00d2\u00d0\3\2\2\2\u00d2\u00d3\3"+ - "\2\2\2\u00d3\33\3\2\2\2\u00d4\u00d5\7\30\2\2\u00d5\u00d6\7\t\2\2\u00d6"+ - "\u00d7\7J\2\2\u00d7\u00d8\7K\2\2\u00d8\u00d9\7\n\2\2\u00d9\u00da\5\f\7"+ - "\2\u00da\35\3\2\2\2\u00db\u00dc\t\3\2\2\u00dc\37\3\2\2\2\u00dd\u00de\b"+ - "\21\1\2\u00de\u00df\5$\23\2\u00df\u00e0\t\4\2\2\u00e0\u00e1\5 \21\3\u00e1"+ - "\u00e2\b\21\1\2\u00e2\u00e7\3\2\2\2\u00e3\u00e4\5\"\22\2\u00e4\u00e5\b"+ - "\21\1\2\u00e5\u00e7\3\2\2\2\u00e6\u00dd\3\2\2\2\u00e6\u00e3\3\2\2\2\u00e7"+ - "\u0123\3\2\2\2\u00e8\u00e9\f\16\2\2\u00e9\u00ea\t\5\2\2\u00ea\u00eb\5"+ - " \21\17\u00eb\u00ec\b\21\1\2\u00ec\u0122\3\2\2\2\u00ed\u00ee\f\r\2\2\u00ee"+ - "\u00ef\t\6\2\2\u00ef\u00f0\5 \21\16\u00f0\u00f1\b\21\1\2\u00f1\u0122\3"+ - "\2\2\2\u00f2\u00f3\f\f\2\2\u00f3\u00f4\t\7\2\2\u00f4\u00f5\5 \21\r\u00f5"+ - "\u00f6\b\21\1\2\u00f6\u0122\3\2\2\2\u00f7\u00f8\f\13\2\2\u00f8\u00f9\t"+ - "\b\2\2\u00f9\u00fa\5 \21\f\u00fa\u00fb\b\21\1\2\u00fb\u0122\3\2\2\2\u00fc"+ - "\u00fd\f\n\2\2\u00fd\u00fe\t\t\2\2\u00fe\u00ff\5 \21\13\u00ff\u0100\b"+ - "\21\1\2\u0100\u0122\3\2\2\2\u0101\u0102\f\t\2\2\u0102\u0103\7,\2\2\u0103"+ - "\u0104\5 \21\n\u0104\u0105\b\21\1\2\u0105\u0122\3\2\2\2\u0106\u0107\f"+ - "\b\2\2\u0107\u0108\7-\2\2\u0108\u0109\5 \21\t\u0109\u010a\b\21\1\2\u010a"+ - "\u0122\3\2\2\2\u010b\u010c\f\7\2\2\u010c\u010d\7.\2\2\u010d\u010e\5 \21"+ - "\b\u010e\u010f\b\21\1\2\u010f\u0122\3\2\2\2\u0110\u0111\f\6\2\2\u0111"+ - "\u0112\7/\2\2\u0112\u0113\5 \21\7\u0113\u0114\b\21\1\2\u0114\u0122\3\2"+ - "\2\2\u0115\u0116\f\5\2\2\u0116\u0117\7\60\2\2\u0117\u0118\5 \21\6\u0118"+ - "\u0119\b\21\1\2\u0119\u0122\3\2\2\2\u011a\u011b\f\4\2\2\u011b\u011c\7"+ - "\61\2\2\u011c\u011d\5 \21\2\u011d\u011e\7\62\2\2\u011e\u011f\5 \21\4\u011f"+ - "\u0120\b\21\1\2\u0120\u0122\3\2\2\2\u0121\u00e8\3\2\2\2\u0121\u00ed\3"+ - "\2\2\2\u0121\u00f2\3\2\2\2\u0121\u00f7\3\2\2\2\u0121\u00fc\3\2\2\2\u0121"+ - "\u0101\3\2\2\2\u0121\u0106\3\2\2\2\u0121\u010b\3\2\2\2\u0121\u0110\3\2"+ - "\2\2\u0121\u0115\3\2\2\2\u0121\u011a\3\2\2\2\u0122\u0125\3\2\2\2\u0123"+ - "\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124!\3\2\2\2\u0125\u0123\3\2\2\2"+ - "\u0126\u0127\6\22\16\3\u0127\u0128\t\n\2\2\u0128\u0144\5$\23\2\u0129\u012a"+ - "\6\22\17\3\u012a\u012b\5$\23\2\u012b\u012c\t\n\2\2\u012c\u0144\3\2\2\2"+ - "\u012d\u012e\6\22\20\3\u012e\u0144\5$\23\2\u012f\u0130\6\22\21\3\u0130"+ - "\u0131\t\13\2\2\u0131\u0144\b\22\1\2\u0132\u0133\6\22\22\3\u0133\u0134"+ - "\7G\2\2\u0134\u0144\b\22\1\2\u0135\u0136\6\22\23\3\u0136\u0137\7H\2\2"+ - "\u0137\u0144\b\22\1\2\u0138\u0139\6\22\24\3\u0139\u013a\7I\2\2\u013a\u0144"+ - "\b\22\1\2\u013b\u013c\6\22\25\3\u013c\u013d\t\f\2\2\u013d\u0144\5\"\22"+ - "\2\u013e\u013f\7\t\2\2\u013f\u0140\5\26\f\2\u0140\u0141\7\n\2\2\u0141"+ - "\u0142\5\"\22\2\u0142\u0144\3\2\2\2\u0143\u0126\3\2\2\2\u0143\u0129\3"+ - "\2\2\2\u0143\u012d\3\2\2\2\u0143\u012f\3\2\2\2\u0143\u0132\3\2\2\2\u0143"+ - "\u0135\3\2\2\2\u0143\u0138\3\2\2\2\u0143\u013b\3\2\2\2\u0143\u013e\3\2"+ - "\2\2\u0144#\3\2\2\2\u0145\u0149\5&\24\2\u0146\u0148\5(\25\2\u0147\u0146"+ - "\3\2\2\2\u0148\u014b\3\2\2\2\u0149\u0147\3\2\2\2\u0149\u014a\3\2\2\2\u014a"+ - "\u0168\3\2\2\2\u014b\u0149\3\2\2\2\u014c\u014d\5\26\f\2\u014d\u0151\5"+ - "*\26\2\u014e\u0150\5(\25\2\u014f\u014e\3\2\2\2\u0150\u0153\3\2\2\2\u0151"+ - "\u014f\3\2\2\2\u0151\u0152\3\2\2\2\u0152\u0168\3\2\2\2\u0153\u0151\3\2"+ - "\2\2\u0154\u0155\7\26\2\2\u0155\u015a\7J\2\2\u0156\u0157\7\7\2\2\u0157"+ - "\u0158\5 \21\2\u0158\u0159\7\b\2\2\u0159\u015b\3\2\2\2\u015a\u0156\3\2"+ - "\2\2\u015b\u015c\3\2\2\2\u015c\u015a\3\2\2\2\u015c\u015d\3\2\2\2\u015d"+ - "\u0165\3\2\2\2\u015e\u0162\5*\26\2\u015f\u0161\5(\25\2\u0160\u015f\3\2"+ - "\2\2\u0161\u0164\3\2\2\2\u0162\u0160\3\2\2\2\u0162\u0163\3\2\2\2\u0163"+ - "\u0166\3\2\2\2\u0164\u0162\3\2\2\2\u0165\u015e\3\2\2\2\u0165\u0166\3\2"+ - "\2\2\u0166\u0168\3\2\2\2\u0167\u0145\3\2\2\2\u0167\u014c\3\2\2\2\u0167"+ - "\u0154\3\2\2\2\u0168%\3\2\2\2\u0169\u016a\6\24\26\3\u016a\u016b\7\t\2"+ - "\2\u016b\u016c\5 \21\2\u016c\u016d\7\n\2\2\u016d\u016e\b\24\1\2\u016e"+ - "\u017c\3\2\2\2\u016f\u0170\6\24\27\3\u0170\u0171\7\t\2\2\u0171\u0172\5"+ - "\"\22\2\u0172\u0173\7\n\2\2\u0173\u017c\3\2\2\2\u0174\u017c\7F\2\2\u0175"+ - "\u017c\7K\2\2\u0176\u0177\7K\2\2\u0177\u017c\5.\30\2\u0178\u0179\7\26"+ - "\2\2\u0179\u017a\7J\2\2\u017a\u017c\5.\30\2\u017b\u0169\3\2\2\2\u017b"+ - "\u016f\3\2\2\2\u017b\u0174\3\2\2\2\u017b\u0175\3\2\2\2\u017b\u0176\3\2"+ - "\2\2\u017b\u0178\3\2\2\2\u017c\'\3\2\2\2\u017d\u017e\6\25\30\3\u017e\u0182"+ - "\5*\26\2\u017f\u0180\6\25\31\3\u0180\u0182\5,\27\2\u0181\u017d\3\2\2\2"+ - "\u0181\u017f\3\2\2\2\u0182)\3\2\2\2\u0183\u0184\7\13\2\2\u0184\u0185\7"+ - "M\2\2\u0185\u0189\5.\30\2\u0186\u0187\7\13\2\2\u0187\u0189\t\r\2\2\u0188"+ - "\u0183\3\2\2\2\u0188\u0186\3\2\2\2\u0189+\3\2\2\2\u018a\u018b\7\7\2\2"+ - "\u018b\u018c\5 \21\2\u018c\u018d\7\b\2\2\u018d-\3\2\2\2\u018e\u0197\7"+ - "\t\2\2\u018f\u0194\5\60\31\2\u0190\u0191\7\f\2\2\u0191\u0193\5\60\31\2"+ - "\u0192\u0190\3\2\2\2\u0193\u0196\3\2\2\2\u0194\u0192\3\2\2\2\u0194\u0195"+ - "\3\2\2\2\u0195\u0198\3\2\2\2\u0196\u0194\3\2\2\2\u0197\u018f\3\2\2\2\u0197"+ - "\u0198\3\2\2\2\u0198\u0199\3\2\2\2\u0199\u019a\7\n\2\2\u019a/\3\2\2\2"+ - "\u019b\u019e\5 \21\2\u019c\u019e\5\30\r\2\u019d\u019b\3\2\2\2\u019d\u019c"+ - "\3\2\2\2\u019e\61\3\2\2\2$\65;NQ]ervz\177\u009a\u00a3\u00a7\u00ad\u00b6"+ - "\u00c0\u00c8\u00d2\u00e6\u0121\u0123\u0143\u0149\u0151\u015c\u0162\u0165"+ - "\u0167\u017b\u0181\u0188\u0194\u0197\u019d"; + "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\5\22\u0148\n\22\3\23\3\23\7\23"+ + "\u014c\n\23\f\23\16\23\u014f\13\23\3\23\3\23\3\23\7\23\u0154\n\23\f\23"+ + "\16\23\u0157\13\23\3\23\3\23\3\23\3\23\3\23\3\23\6\23\u015f\n\23\r\23"+ + "\16\23\u0160\3\23\3\23\7\23\u0165\n\23\f\23\16\23\u0168\13\23\5\23\u016a"+ + "\n\23\5\23\u016c\n\23\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24"+ + "\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\5\24\u0180\n\24\3\25\3\25\3\25"+ + "\3\25\5\25\u0186\n\25\3\26\3\26\3\26\3\26\3\26\5\26\u018d\n\26\3\27\3"+ + "\27\3\27\3\27\3\30\3\30\3\30\3\30\7\30\u0197\n\30\f\30\16\30\u019a\13"+ + "\30\5\30\u019c\n\30\3\30\3\30\3\31\3\31\5\31\u01a2\n\31\3\31\2\3 \32\2"+ + "\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\2\16\4\2\26\26KK\3\3"+ + "\r\r\3\2\66A\3\2\34\36\3\2\37 \3\2!#\3\2$\'\3\2(+\3\2\64\65\3\2BE\4\2"+ + "\32\33\37 \3\2LM\u01cd\2\65\3\2\2\2\4@\3\2\2\2\6E\3\2\2\2\b\u00a3\3\2"+ + "\2\2\n\u00a7\3\2\2\2\f\u00a9\3\2\2\2\16\u00b2\3\2\2\2\20\u00b6\3\2\2\2"+ + "\22\u00b8\3\2\2\2\24\u00ba\3\2\2\2\26\u00c3\3\2\2\2\30\u00d1\3\2\2\2\32"+ + "\u00d3\3\2\2\2\34\u00d8\3\2\2\2\36\u00df\3\2\2\2 \u00ea\3\2\2\2\"\u0147"+ + "\3\2\2\2$\u016b\3\2\2\2&\u017f\3\2\2\2(\u0185\3\2\2\2*\u018c\3\2\2\2,"+ + "\u018e\3\2\2\2.\u0192\3\2\2\2\60\u01a1\3\2\2\2\62\64\5\4\3\2\63\62\3\2"+ + "\2\2\64\67\3\2\2\2\65\63\3\2\2\2\65\66\3\2\2\2\66;\3\2\2\2\67\65\3\2\2"+ + "\28:\5\b\5\298\3\2\2\2:=\3\2\2\2;9\3\2\2\2;<\3\2\2\2<>\3\2\2\2=;\3\2\2"+ + "\2>?\7\2\2\3?\3\3\2\2\2@A\5\26\f\2AB\7K\2\2BC\5\6\4\2CD\5\f\7\2D\5\3\2"+ + "\2\2EQ\7\t\2\2FG\5\26\f\2GN\7K\2\2HI\7\f\2\2IJ\5\26\f\2JK\7K\2\2KM\3\2"+ + "\2\2LH\3\2\2\2MP\3\2\2\2NL\3\2\2\2NO\3\2\2\2OR\3\2\2\2PN\3\2\2\2QF\3\2"+ + "\2\2QR\3\2\2\2RS\3\2\2\2ST\7\n\2\2T\7\3\2\2\2UV\7\16\2\2VW\7\t\2\2WX\5"+ + " \21\2XY\7\n\2\2Y]\5\n\6\2Z[\7\17\2\2[^\5\n\6\2\\^\6\5\2\2]Z\3\2\2\2]"+ + "\\\3\2\2\2^\u00a4\3\2\2\2_`\7\20\2\2`a\7\t\2\2ab\5 \21\2be\7\n\2\2cf\5"+ + "\n\6\2df\5\16\b\2ec\3\2\2\2ed\3\2\2\2f\u00a4\3\2\2\2gh\7\21\2\2hi\5\f"+ + "\7\2ij\7\20\2\2jk\7\t\2\2kl\5 \21\2lm\7\n\2\2mn\5\36\20\2n\u00a4\3\2\2"+ + "\2op\7\22\2\2pr\7\t\2\2qs\5\20\t\2rq\3\2\2\2rs\3\2\2\2st\3\2\2\2tv\7\r"+ + "\2\2uw\5 \21\2vu\3\2\2\2vw\3\2\2\2wx\3\2\2\2xz\7\r\2\2y{\5\22\n\2zy\3"+ + "\2\2\2z{\3\2\2\2{|\3\2\2\2|\177\7\n\2\2}\u0080\5\n\6\2~\u0080\5\16\b\2"+ + "\177}\3\2\2\2\177~\3\2\2\2\u0080\u00a4\3\2\2\2\u0081\u0082\7\22\2\2\u0082"+ + "\u0083\7\t\2\2\u0083\u0084\5\26\f\2\u0084\u0085\7K\2\2\u0085\u0086\7\62"+ + "\2\2\u0086\u0087\5 \21\2\u0087\u0088\7\n\2\2\u0088\u0089\5\n\6\2\u0089"+ + "\u00a4\3\2\2\2\u008a\u008b\5\24\13\2\u008b\u008c\5\36\20\2\u008c\u00a4"+ + "\3\2\2\2\u008d\u008e\7\23\2\2\u008e\u00a4\5\36\20\2\u008f\u0090\7\24\2"+ + "\2\u0090\u00a4\5\36\20\2\u0091\u0092\7\25\2\2\u0092\u0093\5 \21\2\u0093"+ + "\u0094\5\36\20\2\u0094\u00a4\3\2\2\2\u0095\u0096\7\27\2\2\u0096\u0098"+ + "\5\f\7\2\u0097\u0099\5\34\17\2\u0098\u0097\3\2\2\2\u0099\u009a\3\2\2\2"+ + "\u009a\u0098\3\2\2\2\u009a\u009b\3\2\2\2\u009b\u00a4\3\2\2\2\u009c\u009d"+ + "\7\31\2\2\u009d\u009e\5 \21\2\u009e\u009f\5\36\20\2\u009f\u00a4\3\2\2"+ + "\2\u00a0\u00a1\5 \21\2\u00a1\u00a2\5\36\20\2\u00a2\u00a4\3\2\2\2\u00a3"+ + "U\3\2\2\2\u00a3_\3\2\2\2\u00a3g\3\2\2\2\u00a3o\3\2\2\2\u00a3\u0081\3\2"+ + "\2\2\u00a3\u008a\3\2\2\2\u00a3\u008d\3\2\2\2\u00a3\u008f\3\2\2\2\u00a3"+ + "\u0091\3\2\2\2\u00a3\u0095\3\2\2\2\u00a3\u009c\3\2\2\2\u00a3\u00a0\3\2"+ + "\2\2\u00a4\t\3\2\2\2\u00a5\u00a8\5\f\7\2\u00a6\u00a8\5\b\5\2\u00a7\u00a5"+ + "\3\2\2\2\u00a7\u00a6\3\2\2\2\u00a8\13\3\2\2\2\u00a9\u00ad\7\5\2\2\u00aa"+ + "\u00ac\5\b\5\2\u00ab\u00aa\3\2\2\2\u00ac\u00af\3\2\2\2\u00ad\u00ab\3\2"+ + "\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b0\3\2\2\2\u00af\u00ad\3\2\2\2\u00b0"+ + "\u00b1\7\6\2\2\u00b1\r\3\2\2\2\u00b2\u00b3\7\r\2\2\u00b3\17\3\2\2\2\u00b4"+ + "\u00b7\5\24\13\2\u00b5\u00b7\5 \21\2\u00b6\u00b4\3\2\2\2\u00b6\u00b5\3"+ + "\2\2\2\u00b7\21\3\2\2\2\u00b8\u00b9\5 \21\2\u00b9\23\3\2\2\2\u00ba\u00bb"+ + "\5\26\f\2\u00bb\u00c0\5\32\16\2\u00bc\u00bd\7\f\2\2\u00bd\u00bf\5\32\16"+ + "\2\u00be\u00bc\3\2\2\2\u00bf\u00c2\3\2\2\2\u00c0\u00be\3\2\2\2\u00c0\u00c1"+ + "\3\2\2\2\u00c1\25\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c3\u00c8\7J\2\2\u00c4"+ + "\u00c5\7\7\2\2\u00c5\u00c7\7\b\2\2\u00c6\u00c4\3\2\2\2\u00c7\u00ca\3\2"+ + "\2\2\u00c8\u00c6\3\2\2\2\u00c8\u00c9\3\2\2\2\u00c9\27\3\2\2\2\u00ca\u00c8"+ + "\3\2\2\2\u00cb\u00cc\7J\2\2\u00cc\u00cd\7\63\2\2\u00cd\u00d2\t\2\2\2\u00ce"+ + "\u00cf\7K\2\2\u00cf\u00d0\7\63\2\2\u00d0\u00d2\7K\2\2\u00d1\u00cb\3\2"+ + "\2\2\u00d1\u00ce\3\2\2\2\u00d2\31\3\2\2\2\u00d3\u00d6\7K\2\2\u00d4\u00d5"+ + "\7\66\2\2\u00d5\u00d7\5 \21\2\u00d6\u00d4\3\2\2\2\u00d6\u00d7\3\2\2\2"+ + "\u00d7\33\3\2\2\2\u00d8\u00d9\7\30\2\2\u00d9\u00da\7\t\2\2\u00da\u00db"+ + "\7J\2\2\u00db\u00dc\7K\2\2\u00dc\u00dd\7\n\2\2\u00dd\u00de\5\f\7\2\u00de"+ + "\35\3\2\2\2\u00df\u00e0\t\3\2\2\u00e0\37\3\2\2\2\u00e1\u00e2\b\21\1\2"+ + "\u00e2\u00e3\5$\23\2\u00e3\u00e4\t\4\2\2\u00e4\u00e5\5 \21\3\u00e5\u00e6"+ + "\b\21\1\2\u00e6\u00eb\3\2\2\2\u00e7\u00e8\5\"\22\2\u00e8\u00e9\b\21\1"+ + "\2\u00e9\u00eb\3\2\2\2\u00ea\u00e1\3\2\2\2\u00ea\u00e7\3\2\2\2\u00eb\u0127"+ + "\3\2\2\2\u00ec\u00ed\f\16\2\2\u00ed\u00ee\t\5\2\2\u00ee\u00ef\5 \21\17"+ + "\u00ef\u00f0\b\21\1\2\u00f0\u0126\3\2\2\2\u00f1\u00f2\f\r\2\2\u00f2\u00f3"+ + "\t\6\2\2\u00f3\u00f4\5 \21\16\u00f4\u00f5\b\21\1\2\u00f5\u0126\3\2\2\2"+ + "\u00f6\u00f7\f\f\2\2\u00f7\u00f8\t\7\2\2\u00f8\u00f9\5 \21\r\u00f9\u00fa"+ + "\b\21\1\2\u00fa\u0126\3\2\2\2\u00fb\u00fc\f\13\2\2\u00fc\u00fd\t\b\2\2"+ + "\u00fd\u00fe\5 \21\f\u00fe\u00ff\b\21\1\2\u00ff\u0126\3\2\2\2\u0100\u0101"+ + "\f\n\2\2\u0101\u0102\t\t\2\2\u0102\u0103\5 \21\13\u0103\u0104\b\21\1\2"+ + "\u0104\u0126\3\2\2\2\u0105\u0106\f\t\2\2\u0106\u0107\7,\2\2\u0107\u0108"+ + "\5 \21\n\u0108\u0109\b\21\1\2\u0109\u0126\3\2\2\2\u010a\u010b\f\b\2\2"+ + "\u010b\u010c\7-\2\2\u010c\u010d\5 \21\t\u010d\u010e\b\21\1\2\u010e\u0126"+ + "\3\2\2\2\u010f\u0110\f\7\2\2\u0110\u0111\7.\2\2\u0111\u0112\5 \21\b\u0112"+ + "\u0113\b\21\1\2\u0113\u0126\3\2\2\2\u0114\u0115\f\6\2\2\u0115\u0116\7"+ + "/\2\2\u0116\u0117\5 \21\7\u0117\u0118\b\21\1\2\u0118\u0126\3\2\2\2\u0119"+ + "\u011a\f\5\2\2\u011a\u011b\7\60\2\2\u011b\u011c\5 \21\6\u011c\u011d\b"+ + "\21\1\2\u011d\u0126\3\2\2\2\u011e\u011f\f\4\2\2\u011f\u0120\7\61\2\2\u0120"+ + "\u0121\5 \21\2\u0121\u0122\7\62\2\2\u0122\u0123\5 \21\4\u0123\u0124\b"+ + "\21\1\2\u0124\u0126\3\2\2\2\u0125\u00ec\3\2\2\2\u0125\u00f1\3\2\2\2\u0125"+ + "\u00f6\3\2\2\2\u0125\u00fb\3\2\2\2\u0125\u0100\3\2\2\2\u0125\u0105\3\2"+ + "\2\2\u0125\u010a\3\2\2\2\u0125\u010f\3\2\2\2\u0125\u0114\3\2\2\2\u0125"+ + "\u0119\3\2\2\2\u0125\u011e\3\2\2\2\u0126\u0129\3\2\2\2\u0127\u0125\3\2"+ + "\2\2\u0127\u0128\3\2\2\2\u0128!\3\2\2\2\u0129\u0127\3\2\2\2\u012a\u012b"+ + "\6\22\16\3\u012b\u012c\t\n\2\2\u012c\u0148\5$\23\2\u012d\u012e\6\22\17"+ + "\3\u012e\u012f\5$\23\2\u012f\u0130\t\n\2\2\u0130\u0148\3\2\2\2\u0131\u0132"+ + "\6\22\20\3\u0132\u0148\5$\23\2\u0133\u0134\6\22\21\3\u0134\u0135\t\13"+ + "\2\2\u0135\u0148\b\22\1\2\u0136\u0137\6\22\22\3\u0137\u0138\7G\2\2\u0138"+ + "\u0148\b\22\1\2\u0139\u013a\6\22\23\3\u013a\u013b\7H\2\2\u013b\u0148\b"+ + "\22\1\2\u013c\u013d\6\22\24\3\u013d\u013e\7I\2\2\u013e\u0148\b\22\1\2"+ + "\u013f\u0140\6\22\25\3\u0140\u0141\t\f\2\2\u0141\u0148\5\"\22\2\u0142"+ + "\u0143\7\t\2\2\u0143\u0144\5\26\f\2\u0144\u0145\7\n\2\2\u0145\u0146\5"+ + "\"\22\2\u0146\u0148\3\2\2\2\u0147\u012a\3\2\2\2\u0147\u012d\3\2\2\2\u0147"+ + "\u0131\3\2\2\2\u0147\u0133\3\2\2\2\u0147\u0136\3\2\2\2\u0147\u0139\3\2"+ + "\2\2\u0147\u013c\3\2\2\2\u0147\u013f\3\2\2\2\u0147\u0142\3\2\2\2\u0148"+ + "#\3\2\2\2\u0149\u014d\5&\24\2\u014a\u014c\5(\25\2\u014b\u014a\3\2\2\2"+ + "\u014c\u014f\3\2\2\2\u014d\u014b\3\2\2\2\u014d\u014e\3\2\2\2\u014e\u016c"+ + "\3\2\2\2\u014f\u014d\3\2\2\2\u0150\u0151\5\26\f\2\u0151\u0155\5*\26\2"+ + "\u0152\u0154\5(\25\2\u0153\u0152\3\2\2\2\u0154\u0157\3\2\2\2\u0155\u0153"+ + "\3\2\2\2\u0155\u0156\3\2\2\2\u0156\u016c\3\2\2\2\u0157\u0155\3\2\2\2\u0158"+ + "\u0159\7\26\2\2\u0159\u015e\7J\2\2\u015a\u015b\7\7\2\2\u015b\u015c\5 "+ + "\21\2\u015c\u015d\7\b\2\2\u015d\u015f\3\2\2\2\u015e\u015a\3\2\2\2\u015f"+ + "\u0160\3\2\2\2\u0160\u015e\3\2\2\2\u0160\u0161\3\2\2\2\u0161\u0169\3\2"+ + "\2\2\u0162\u0166\5*\26\2\u0163\u0165\5(\25\2\u0164\u0163\3\2\2\2\u0165"+ + "\u0168\3\2\2\2\u0166\u0164\3\2\2\2\u0166\u0167\3\2\2\2\u0167\u016a\3\2"+ + "\2\2\u0168\u0166\3\2\2\2\u0169\u0162\3\2\2\2\u0169\u016a\3\2\2\2\u016a"+ + "\u016c\3\2\2\2\u016b\u0149\3\2\2\2\u016b\u0150\3\2\2\2\u016b\u0158\3\2"+ + "\2\2\u016c%\3\2\2\2\u016d\u016e\6\24\26\3\u016e\u016f\7\t\2\2\u016f\u0170"+ + "\5 \21\2\u0170\u0171\7\n\2\2\u0171\u0172\b\24\1\2\u0172\u0180\3\2\2\2"+ + "\u0173\u0174\6\24\27\3\u0174\u0175\7\t\2\2\u0175\u0176\5\"\22\2\u0176"+ + "\u0177\7\n\2\2\u0177\u0180\3\2\2\2\u0178\u0180\7F\2\2\u0179\u0180\7K\2"+ + "\2\u017a\u017b\7K\2\2\u017b\u0180\5.\30\2\u017c\u017d\7\26\2\2\u017d\u017e"+ + "\7J\2\2\u017e\u0180\5.\30\2\u017f\u016d\3\2\2\2\u017f\u0173\3\2\2\2\u017f"+ + "\u0178\3\2\2\2\u017f\u0179\3\2\2\2\u017f\u017a\3\2\2\2\u017f\u017c\3\2"+ + "\2\2\u0180\'\3\2\2\2\u0181\u0182\6\25\30\3\u0182\u0186\5*\26\2\u0183\u0184"+ + "\6\25\31\3\u0184\u0186\5,\27\2\u0185\u0181\3\2\2\2\u0185\u0183\3\2\2\2"+ + "\u0186)\3\2\2\2\u0187\u0188\7\13\2\2\u0188\u0189\7M\2\2\u0189\u018d\5"+ + ".\30\2\u018a\u018b\7\13\2\2\u018b\u018d\t\r\2\2\u018c\u0187\3\2\2\2\u018c"+ + "\u018a\3\2\2\2\u018d+\3\2\2\2\u018e\u018f\7\7\2\2\u018f\u0190\5 \21\2"+ + "\u0190\u0191\7\b\2\2\u0191-\3\2\2\2\u0192\u019b\7\t\2\2\u0193\u0198\5"+ + "\60\31\2\u0194\u0195\7\f\2\2\u0195\u0197\5\60\31\2\u0196\u0194\3\2\2\2"+ + "\u0197\u019a\3\2\2\2\u0198\u0196\3\2\2\2\u0198\u0199\3\2\2\2\u0199\u019c"+ + "\3\2\2\2\u019a\u0198\3\2\2\2\u019b\u0193\3\2\2\2\u019b\u019c\3\2\2\2\u019c"+ + "\u019d\3\2\2\2\u019d\u019e\7\n\2\2\u019e/\3\2\2\2\u019f\u01a2\5 \21\2"+ + "\u01a0\u01a2\5\30\r\2\u01a1\u019f\3\2\2\2\u01a1\u01a0\3\2\2\2\u01a2\61"+ + "\3\2\2\2%\65;NQ]ervz\177\u009a\u00a3\u00a7\u00ad\u00b6\u00c0\u00c8\u00d1"+ + "\u00d6\u00ea\u0125\u0127\u0147\u014d\u0155\u0160\u0166\u0169\u016b\u017f"+ + "\u0185\u018c\u0198\u019b\u01a1"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { 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 c1fe02f9388..aa474f14c81 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 @@ -100,6 +100,7 @@ import org.elasticsearch.painless.node.AStatement; import org.elasticsearch.painless.node.EBinary; import org.elasticsearch.painless.node.EBool; import org.elasticsearch.painless.node.EBoolean; +import org.elasticsearch.painless.node.ECapturingFunctionRef; import org.elasticsearch.painless.node.EChain; import org.elasticsearch.painless.node.EComp; import org.elasticsearch.painless.node.EConditional; @@ -447,15 +448,19 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitFuncref(FuncrefContext ctx) { - final String methodText; - if (ctx.ID() != null) { - methodText = ctx.ID().getText(); - } else if (ctx.NEW() != null ){ - methodText = ctx.NEW().getText(); + if (ctx.TYPE() != null) { + // non-capturing Type::method or Type::new + final String methodText; + if (ctx.NEW() != null) { + methodText = ctx.NEW().getText(); + } else { + methodText = ctx.ID(0).getText(); + } + return new EFunctionRef(location(ctx), ctx.TYPE().getText(), methodText); } else { - throw location(ctx).createError(new IllegalStateException("Illegal tree structure.")); + // capturing object::method + return new ECapturingFunctionRef(location(ctx), ctx.ID(0).getText(), ctx.ID(1).getText()); } - return new EFunctionRef(location(ctx), ctx.TYPE().getText(), methodText); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java new file mode 100644 index 00000000000..3e35602a3a2 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java @@ -0,0 +1,119 @@ +/* + * 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.node; + +import org.elasticsearch.painless.DefBootstrap; +import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.FunctionRef; +import org.elasticsearch.painless.Location; +import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Variable; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE; +import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; + +import java.lang.invoke.LambdaMetafactory; + +/** + * Represents a capturing function reference. + */ +public class ECapturingFunctionRef extends AExpression { + public final String type; + public final String call; + + private FunctionRef ref; + Variable captured; + private boolean defInterface; + + public ECapturingFunctionRef(Location location, String type, String call) { + super(location); + + this.type = type; + this.call = call; + } + + @Override + void analyze(Locals variables) { + captured = variables.getVariable(location, type); + if (expected == null) { + defInterface = true; + actual = Definition.getType("String"); + } else { + defInterface = false; + // static case + if (captured.type.sort != Definition.Sort.DEF) { + try { + ref = new FunctionRef(expected, captured.type.name, call, captured.type.clazz); + } catch (IllegalArgumentException e) { + throw createError(e); + } + } + actual = expected; + } + } + + @Override + void write(MethodWriter writer) { + writer.writeDebugInfo(location); + if (defInterface && captured.type.sort == Definition.Sort.DEF) { + // dynamic interface, dynamic implementation + writer.push("D" + type + "." + call + ",1"); + writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.slot); + } else if (defInterface) { + // dynamic interface, typed implementation + writer.push("S" + captured.type.name + "." + call + ",1"); + writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.slot); + } else if (ref == null) { + // typed interface, dynamic implementation + writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.slot); + String descriptor = Type.getMethodType(expected.type, captured.type.type).getDescriptor(); + writer.invokeDynamic(call, descriptor, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.REFERENCE, expected.name); + } else { + // typed interface, typed implementation + writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.slot); + // convert MethodTypes to asm Type for the constant pool. + String invokedType = ref.invokedType.toMethodDescriptorString(); + Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); + Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); + if (ref.needsBridges()) { + writer.invokeDynamic(ref.invokedName, + invokedType, + LAMBDA_BOOTSTRAP_HANDLE, + samMethodType, + ref.implMethodASM, + samMethodType, + LambdaMetafactory.FLAG_BRIDGES, + 1, + interfaceType); + } else { + writer.invokeDynamic(ref.invokedName, + invokedType, + LAMBDA_BOOTSTRAP_HANDLE, + samMethodType, + ref.implMethodASM, + samMethodType, + 0); + } + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java index ec45f89868a..9d96809bf25 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java @@ -64,7 +64,7 @@ public class EFunctionRef extends AExpression { @Override void write(MethodWriter writer) { if (ref == null) { - writer.push(type + "." + call); + writer.push("S" + type + "." + call + ",0"); } else { writer.writeDebugInfo(location); // convert MethodTypes to asm Type for the constant pool. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java index 524f62eea15..dc8890e5122 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java @@ -62,7 +62,7 @@ final class LDefArray extends ALink implements IDefLink { writer.writeDebugInfo(location); String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type, index.actual.type); - writer.invokeDynamic("arrayLoad", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_LOAD, 0); + writer.invokeDynamic("arrayLoad", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_LOAD); } @Override @@ -70,6 +70,6 @@ final class LDefArray extends ALink implements IDefLink { writer.writeDebugInfo(location); String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, index.actual.type, after.type); - writer.invokeDynamic("arrayStore", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_STORE, 0); + writer.invokeDynamic("arrayStore", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ARRAY_STORE); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java index 871bd2273db..554144c2999 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java @@ -55,11 +55,15 @@ final class LDefCall extends ALink implements IDefLink { } recipe = 0; + int totalCaptures = 0; for (int argument = 0; argument < arguments.size(); ++argument) { AExpression expression = arguments.get(argument); if (expression instanceof EFunctionRef) { - recipe |= (1L << argument); // mark argument as deferred reference + recipe |= (1L << (argument + totalCaptures)); // mark argument as deferred reference + } else if (expression instanceof ECapturingFunctionRef) { + recipe |= (1L << (argument + totalCaptures)); // mark argument as deferred reference + totalCaptures++; } expression.internal = true; expression.analyze(locals); @@ -90,6 +94,10 @@ final class LDefCall extends ALink implements IDefLink { for (AExpression argument : arguments) { signature.append(argument.actual.type.getDescriptor()); + if (argument instanceof ECapturingFunctionRef) { + ECapturingFunctionRef capturingRef = (ECapturingFunctionRef) argument; + signature.append(capturingRef.captured.type.type.getDescriptor()); + } argument.write(writer); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java index 861dfe0df53..91ee8e0f03d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java @@ -59,7 +59,7 @@ final class LDefField extends ALink implements IDefLink { writer.writeDebugInfo(location); String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type); - writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.LOAD, 0); + writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.LOAD); } @Override @@ -67,6 +67,6 @@ final class LDefField extends ALink implements IDefLink { writer.writeDebugInfo(location); String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, after.type); - writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.STORE, 0); + writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.STORE); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index b42bd1aff5a..a6156324873 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -195,7 +195,7 @@ public class SEach extends AStatement { if (method == null) { Type itr = Definition.getType("Iterator"); String desc = org.objectweb.asm.Type.getMethodDescriptor(itr.type, Definition.DEF_TYPE.type); - writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR, (Object)0); + writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR); } else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) { writer.invokeInterface(method.owner.type, method.method); } else { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java index 8ab69366f24..d98e5f68bcf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java @@ -35,6 +35,7 @@ * {@link org.elasticsearch.painless.node.EBinary} - Represents a binary math expression. * {@link org.elasticsearch.painless.node.EBool} - Represents a boolean expression. * {@link org.elasticsearch.painless.node.EBoolean} - Represents a boolean constant. + * {@link org.elasticsearch.painless.node.ECapturingFunctionRef} - Represents a function reference (capturing). * {@link org.elasticsearch.painless.node.ECast} - Represents an implicit cast in most cases. (Internal only.) * {@link org.elasticsearch.painless.node.EChain} - Represents the entirety of a variable/method chain for read/write operations. * {@link org.elasticsearch.painless.node.EComp} - Represents a comparison expression. @@ -42,7 +43,7 @@ * {@link org.elasticsearch.painless.node.EConstant} - Represents a constant. (Internal only.) * {@link org.elasticsearch.painless.node.EDecimal} - Represents a decimal constant. * {@link org.elasticsearch.painless.node.EExplicit} - Represents an explicit cast. - * {@link org.elasticsearch.painless.node.EFunctionRef} - Represents a function reference. + * {@link org.elasticsearch.painless.node.EFunctionRef} - Represents a function reference (non-capturing). * {@link org.elasticsearch.painless.node.ENull} - Represents a null constant. * {@link org.elasticsearch.painless.node.ENumeric} - Represents a non-decimal numeric constant. * {@link org.elasticsearch.painless.node.EUnary} - Represents a unary math expression. diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt index 7fff9ac0aa4..308f2661ebc 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.txt @@ -125,4 +125,5 @@ class org.elasticsearch.painless.FeatureTest -> org.elasticsearch.painless.Featu void setY(int) boolean overloadedStatic() boolean overloadedStatic(boolean) + Object twoFunctionsOfX(Function,Function) } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java index 7a55844a224..4330c613e14 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java @@ -33,7 +33,7 @@ public class DefBootstrapTests extends ESTestCase { CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), - DefBootstrap.METHOD_CALL, 0); + DefBootstrap.METHOD_CALL, 0L); MethodHandle handle = site.dynamicInvoker(); assertDepthEquals(site, 0); @@ -50,7 +50,7 @@ public class DefBootstrapTests extends ESTestCase { CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), - DefBootstrap.METHOD_CALL, 0); + DefBootstrap.METHOD_CALL, 0L); MethodHandle handle = site.dynamicInvoker(); assertDepthEquals(site, 0); @@ -72,7 +72,7 @@ public class DefBootstrapTests extends ESTestCase { CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), - DefBootstrap.METHOD_CALL, 0); + DefBootstrap.METHOD_CALL, 0L); MethodHandle handle = site.dynamicInvoker(); assertDepthEquals(site, 0); 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 12265f161ab..d3055ac228b 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 @@ -19,6 +19,8 @@ package org.elasticsearch.painless; +import java.util.Comparator; + public class FunctionRefTests extends ScriptTestCase { public void testStaticMethodReference() { @@ -39,12 +41,12 @@ public class FunctionRefTests extends ScriptTestCase { public void testCtorMethodReference() { assertEquals(3.0D, - exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + - "DoubleStream doubleStream = l.stream().mapToDouble(Double::doubleValue);" + - "DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new, " + - "DoubleSummaryStatistics::accept, " + - "DoubleSummaryStatistics::combine); " + - "return stats.getSum()")); + exec("List l = new ArrayList(); l.add(1.0); l.add(2.0); " + + "DoubleStream doubleStream = l.stream().mapToDouble(Double::doubleValue);" + + "DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new, " + + "DoubleSummaryStatistics::accept, " + + "DoubleSummaryStatistics::combine); " + + "return stats.getSum()")); } public void testCtorMethodReferenceDef() { @@ -56,6 +58,58 @@ public class FunctionRefTests extends ScriptTestCase { "DoubleSummaryStatistics::combine); " + "return stats.getSum()")); } + + 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);")); + } + + public void testCapturingMethodReferenceDefImpl() { + assertEquals("5", exec("def x = Integer.valueOf(5); return Optional.empty().orElseGet(x::toString);")); + assertEquals("[]", exec("def l = new ArrayList(); return Optional.empty().orElseGet(l::toString);")); + } + + public void testCapturingMethodReferenceDefInterface() { + assertEquals("5", exec("Integer x = Integer.valueOf(5); def opt = Optional.empty(); return opt.orElseGet(x::toString);")); + assertEquals("[]", exec("List l = new ArrayList(); def opt = Optional.empty(); return opt.orElseGet(l::toString);")); + } + + public void testCapturingMethodReferenceDefEverywhere() { + assertEquals("5", exec("def x = Integer.valueOf(5); def opt = Optional.empty(); return opt.orElseGet(x::toString);")); + assertEquals("[]", exec("def l = new ArrayList(); def opt = Optional.empty(); return opt.orElseGet(l::toString);")); + } + + public void testCapturingMethodReferenceMultipleLambdas() { + assertEquals("testingcdefg", exec( + "String x = 'testing';" + + "String y = 'abcdefg';" + + "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "return test.twoFunctionsOfX(x::concat, y::substring);")); + } + + public void testCapturingMethodReferenceMultipleLambdasDefImpls() { + assertEquals("testingcdefg", exec( + "def x = 'testing';" + + "def y = 'abcdefg';" + + "org.elasticsearch.painless.FeatureTest test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "return test.twoFunctionsOfX(x::concat, y::substring);")); + } + + public void testCapturingMethodReferenceMultipleLambdasDefInterface() { + assertEquals("testingcdefg", exec( + "String x = 'testing';" + + "String y = 'abcdefg';" + + "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "return test.twoFunctionsOfX(x::concat, y::substring);")); + } + + public void testCapturingMethodReferenceMultipleLambdasDefEverywhere() { + assertEquals("testingcdefg", exec( + "def x = 'testing';" + + "def y = 'abcdefg';" + + "def test = new org.elasticsearch.painless.FeatureTest(2,3);" + + "return test.twoFunctionsOfX(x::concat, y::substring);")); + } public void testMethodMissing() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> {