From edb0fd3b888eb36c18669543a2933e1a5f0f908b Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Fri, 10 Jun 2016 14:24:58 -0400 Subject: [PATCH 1/3] painless: add capturing method references --- .../src/main/antlr/PainlessParser.g4 | 1 + .../java/org/elasticsearch/painless/Def.java | 104 ++- .../elasticsearch/painless/DefBootstrap.java | 50 +- .../elasticsearch/painless/Definition.java | 3 +- .../elasticsearch/painless/FeatureTest.java | 7 + .../elasticsearch/painless/FunctionRef.java | 24 +- .../elasticsearch/painless/MethodWriter.java | 1 - .../elasticsearch/painless/ScriptImpl.java | 2 +- .../painless/WriterConstants.java | 2 +- .../painless/antlr/PainlessParser.java | 637 +++++++++--------- .../elasticsearch/painless/antlr/Walker.java | 19 +- .../painless/node/ECapturingFunctionRef.java | 118 ++++ .../painless/node/EFunctionRef.java | 2 +- .../painless/node/LDefArray.java | 4 +- .../elasticsearch/painless/node/LDefCall.java | 10 +- .../painless/node/LDefField.java | 4 +- .../elasticsearch/painless/node/SEach.java | 2 +- .../painless/org.elasticsearch.txt | 1 + .../painless/DefBootstrapTests.java | 6 +- .../painless/FunctionRefTests.java | 66 +- 20 files changed, 698 insertions(+), 365 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java diff --git a/modules/lang-painless/src/main/antlr/PainlessParser.g4 b/modules/lang-painless/src/main/antlr/PainlessParser.g4 index 141ff25e318..bf141b53cbd 100644 --- a/modules/lang-painless/src/main/antlr/PainlessParser.g4 +++ b/modules/lang-painless/src/main/antlr/PainlessParser.g4 @@ -75,6 +75,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 dbfc9993c31..1285b4cfb5a 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 7b7caec320e..d7c09a9c122 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 80082c4487b..7129df80a08 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 540773fbf70..a3ccc415028 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 @@ -1029,7 +1029,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); @@ -1047,19 +1050,37 @@ class PainlessParser extends Parser { enterRule(_localctx, 18, RULE_funcref); int _la; try { - enterOuterAlt(_localctx, 1); - { - setState(170); - match(TYPE); - setState(171); - match(REF); - setState(172); - _la = _input.LA(1); - if ( !(_la==NEW || _la==ID) ) { - _errHandler.recoverInline(this); - } else { - consume(); - } + setState(176); + switch (_input.LA(1)) { + case TYPE: + enterOuterAlt(_localctx, 1); + { + setState(170); + match(TYPE); + setState(171); + match(REF); + setState(172); + _la = _input.LA(1); + if ( !(_la==NEW || _la==ID) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + break; + case ID: + enterOuterAlt(_localctx, 2); + { + setState(173); + match(ID); + setState(174); + match(REF); + setState(175); + match(ID); + } + break; + default: + throw new NoViableAltException(this); } } catch (RecognitionException re) { @@ -1097,15 +1118,15 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(174); + setState(178); match(ID); - setState(177); + setState(181); _la = _input.LA(1); if (_la==ASSIGN) { { - setState(175); + setState(179); match(ASSIGN); - setState(176); + setState(180); expression(0); } } @@ -1149,17 +1170,17 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(179); - match(CATCH); - setState(180); - match(LP); - setState(181); - match(TYPE); - setState(182); - match(ID); setState(183); - match(RP); + match(CATCH); setState(184); + match(LP); + setState(185); + match(TYPE); + setState(186); + match(ID); + setState(187); + match(RP); + setState(188); block(); } } @@ -1195,7 +1216,7 @@ class PainlessParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(186); + setState(190); _la = _input.LA(1); if ( !(_la==EOF || _la==SEMICOLON) ) { _errHandler.recoverInline(this); @@ -1364,24 +1385,24 @@ class PainlessParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(197); - switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + setState(201); + switch ( getInterpreter().adaptivePredict(_input,16,_ctx) ) { case 1: { _localctx = new AssignmentContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(189); + setState(193); chain(true); - setState(190); + setState(194); _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(191); + setState(195); expression(1); ((AssignmentContext)_localctx).s = false; } @@ -1391,37 +1412,37 @@ class PainlessParser extends Parser { _localctx = new SingleContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(194); + setState(198); ((SingleContext)_localctx).u = unary(false); ((SingleContext)_localctx).s = ((SingleContext)_localctx).u.s; } break; } _ctx.stop = _input.LT(-1); - setState(258); + setState(262); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + _alt = getInterpreter().adaptivePredict(_input,18,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(256); - switch ( getInterpreter().adaptivePredict(_input,16,_ctx) ) { + setState(260); + switch ( getInterpreter().adaptivePredict(_input,17,_ctx) ) { case 1: { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(199); + setState(203); if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)"); - setState(200); + setState(204); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIV) | (1L << REM))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(201); + setState(205); expression(13); ((BinaryContext)_localctx).s = false; } @@ -1430,16 +1451,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(204); + setState(208); if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)"); - setState(205); + setState(209); _la = _input.LA(1); if ( !(_la==ADD || _la==SUB) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(206); + setState(210); expression(12); ((BinaryContext)_localctx).s = false; } @@ -1448,16 +1469,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(209); + setState(213); if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); - setState(210); + setState(214); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LSH) | (1L << RSH) | (1L << USH))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(211); + setState(215); expression(11); ((BinaryContext)_localctx).s = false; } @@ -1466,16 +1487,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(214); + setState(218); if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); - setState(215); + setState(219); _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(216); + setState(220); expression(10); ((CompContext)_localctx).s = false; } @@ -1484,16 +1505,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(219); + setState(223); if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)"); - setState(220); + setState(224); _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(221); + setState(225); expression(9); ((CompContext)_localctx).s = false; } @@ -1502,11 +1523,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(224); + setState(228); if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); - setState(225); + setState(229); match(BWAND); - setState(226); + setState(230); expression(8); ((BinaryContext)_localctx).s = false; } @@ -1515,11 +1536,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(229); + setState(233); if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); - setState(230); + setState(234); match(XOR); - setState(231); + setState(235); expression(7); ((BinaryContext)_localctx).s = false; } @@ -1528,11 +1549,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(234); + setState(238); if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); - setState(235); + setState(239); match(BWOR); - setState(236); + setState(240); expression(6); ((BinaryContext)_localctx).s = false; } @@ -1541,11 +1562,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(239); + setState(243); if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(240); + setState(244); match(BOOLAND); - setState(241); + setState(245); expression(5); ((BoolContext)_localctx).s = false; } @@ -1554,11 +1575,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(244); + setState(248); if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(245); + setState(249); match(BOOLOR); - setState(246); + setState(250); expression(4); ((BoolContext)_localctx).s = false; } @@ -1567,15 +1588,15 @@ class PainlessParser extends Parser { { _localctx = new ConditionalContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(249); - if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(250); - match(COND); - setState(251); - ((ConditionalContext)_localctx).e0 = expression(0); - setState(252); - match(COLON); setState(253); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(254); + match(COND); + setState(255); + ((ConditionalContext)_localctx).e0 = expression(0); + setState(256); + match(COLON); + setState(257); ((ConditionalContext)_localctx).e1 = expression(2); ((ConditionalContext)_localctx).s = ((ConditionalContext)_localctx).e0.s && ((ConditionalContext)_localctx).e1.s; } @@ -1583,9 +1604,9 @@ class PainlessParser extends Parser { } } } - setState(260); + setState(264); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + _alt = getInterpreter().adaptivePredict(_input,18,_ctx); } } } @@ -1730,22 +1751,22 @@ class PainlessParser extends Parser { enterRule(_localctx, 28, RULE_unary); int _la; try { - setState(290); - switch ( getInterpreter().adaptivePredict(_input,18,_ctx) ) { + setState(294); + switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) { case 1: _localctx = new PreContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(261); + setState(265); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(262); + setState(266); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(263); + setState(267); chain(true); } break; @@ -1753,11 +1774,11 @@ class PainlessParser extends Parser { _localctx = new PostContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(264); + setState(268); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(265); + setState(269); chain(true); - setState(266); + setState(270); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); @@ -1770,9 +1791,9 @@ class PainlessParser extends Parser { _localctx = new ReadContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(268); + setState(272); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(269); + setState(273); chain(false); } break; @@ -1780,9 +1801,9 @@ class PainlessParser extends Parser { _localctx = new NumericContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(270); + setState(274); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(271); + setState(275); _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); @@ -1796,9 +1817,9 @@ class PainlessParser extends Parser { _localctx = new TrueContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(273); + setState(277); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(274); + setState(278); match(TRUE); ((TrueContext)_localctx).s = false; } @@ -1807,9 +1828,9 @@ class PainlessParser extends Parser { _localctx = new FalseContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(276); + setState(280); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(277); + setState(281); match(FALSE); ((FalseContext)_localctx).s = false; } @@ -1818,9 +1839,9 @@ class PainlessParser extends Parser { _localctx = new NullContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(279); + setState(283); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(280); + setState(284); match(NULL); ((NullContext)_localctx).s = false; } @@ -1829,16 +1850,16 @@ class PainlessParser extends Parser { _localctx = new OperatorContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(282); + setState(286); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(283); + setState(287); _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(284); + setState(288); unary(false); } break; @@ -1846,13 +1867,13 @@ class PainlessParser extends Parser { _localctx = new CastContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(285); + setState(289); match(LP); - setState(286); + setState(290); decltype(); - setState(287); + setState(291); match(RP); - setState(288); + setState(292); unary(_localctx.c); } break; @@ -1961,29 +1982,29 @@ class PainlessParser extends Parser { enterRule(_localctx, 30, RULE_chain); try { int _alt; - setState(326); - switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { + setState(330); + switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { case 1: _localctx = new DynamicContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(292); - ((DynamicContext)_localctx).p = primary(_localctx.c); setState(296); + ((DynamicContext)_localctx).p = primary(_localctx.c); + setState(300); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,19,_ctx); + _alt = getInterpreter().adaptivePredict(_input,20,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(293); + setState(297); secondary(((DynamicContext)_localctx).p.s); } } } - setState(298); + setState(302); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,19,_ctx); + _alt = getInterpreter().adaptivePredict(_input,20,_ctx); } } break; @@ -1991,25 +2012,25 @@ class PainlessParser extends Parser { _localctx = new StaticContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(299); + setState(303); decltype(); - setState(300); - dot(); setState(304); + dot(); + setState(308); _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 ) { { { - setState(301); + setState(305); secondary(true); } } } - setState(306); + setState(310); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,20,_ctx); + _alt = getInterpreter().adaptivePredict(_input,21,_ctx); } } break; @@ -2017,11 +2038,11 @@ class PainlessParser extends Parser { _localctx = new NewarrayContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(307); + setState(311); match(NEW); - setState(308); + setState(312); match(TYPE); - setState(313); + setState(317); _errHandler.sync(this); _alt = 1; do { @@ -2029,11 +2050,11 @@ class PainlessParser extends Parser { case 1: { { - setState(309); + setState(313); match(LBRACE); - setState(310); + setState(314); expression(0); - setState(311); + setState(315); match(RBRACE); } } @@ -2041,31 +2062,31 @@ class PainlessParser extends Parser { default: throw new NoViableAltException(this); } - setState(315); - _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,21,_ctx); - } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); - setState(324); - switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { - case 1: - { - setState(317); - dot(); - setState(321); + setState(319); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); + setState(328); + switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { + case 1: + { + setState(321); + dot(); + setState(325); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(318); + setState(322); secondary(true); } } } - setState(323); + setState(327); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); } } break; @@ -2165,19 +2186,19 @@ class PainlessParser extends Parser { PrimaryContext _localctx = new PrimaryContext(_ctx, getState(), c); enterRule(_localctx, 32, RULE_primary); try { - setState(344); - switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { + setState(348); + switch ( getInterpreter().adaptivePredict(_input,26,_ctx) ) { case 1: _localctx = new ExprprecContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(328); + setState(332); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(329); + setState(333); match(LP); - setState(330); + setState(334); ((ExprprecContext)_localctx).e = expression(0); - setState(331); + setState(335); match(RP); ((ExprprecContext)_localctx).s = ((ExprprecContext)_localctx).e.s; } @@ -2186,13 +2207,13 @@ class PainlessParser extends Parser { _localctx = new ChainprecContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(334); + setState(338); if (!( _localctx.c )) throw new FailedPredicateException(this, " $c "); - setState(335); + setState(339); match(LP); - setState(336); + setState(340); unary(true); - setState(337); + setState(341); match(RP); } break; @@ -2200,7 +2221,7 @@ class PainlessParser extends Parser { _localctx = new StringContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(339); + setState(343); match(STRING); } break; @@ -2208,7 +2229,7 @@ class PainlessParser extends Parser { _localctx = new VariableContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(340); + setState(344); match(ID); } break; @@ -2216,11 +2237,11 @@ class PainlessParser extends Parser { _localctx = new NewobjectContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(341); + setState(345); match(NEW); - setState(342); + setState(346); match(TYPE); - setState(343); + setState(347); arguments(); } break; @@ -2262,23 +2283,23 @@ class PainlessParser extends Parser { SecondaryContext _localctx = new SecondaryContext(_ctx, getState(), s); enterRule(_localctx, 34, RULE_secondary); try { - setState(350); - switch ( getInterpreter().adaptivePredict(_input,26,_ctx) ) { + setState(354); + switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(346); + setState(350); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(347); + setState(351); dot(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(348); + setState(352); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(349); + setState(353); brace(); } break; @@ -2336,17 +2357,17 @@ class PainlessParser extends Parser { enterRule(_localctx, 36, RULE_dot); int _la; try { - setState(357); - switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { + setState(361); + switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) { case 1: _localctx = new CallinvokeContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(352); + setState(356); match(DOT); - setState(353); + setState(357); match(DOTID); - setState(354); + setState(358); arguments(); } break; @@ -2354,9 +2375,9 @@ class PainlessParser extends Parser { _localctx = new FieldaccessContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(355); + setState(359); match(DOT); - setState(356); + setState(360); _la = _input.LA(1); if ( !(_la==DOTINTEGER || _la==DOTID) ) { _errHandler.recoverInline(this); @@ -2410,11 +2431,11 @@ class PainlessParser extends Parser { _localctx = new BraceaccessContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(359); + setState(363); match(LBRACE); - setState(360); + setState(364); expression(0); - setState(361); + setState(365); match(RBRACE); } } @@ -2461,34 +2482,34 @@ class PainlessParser extends Parser { enterOuterAlt(_localctx, 1); { { - setState(363); + setState(367); match(LP); - setState(372); - switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) { + setState(376); + switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { case 1: { - setState(364); + setState(368); argument(); - setState(369); + setState(373); _errHandler.sync(this); _la = _input.LA(1); while (_la==COMMA) { { { - setState(365); + setState(369); match(COMMA); - setState(366); + setState(370); argument(); } } - setState(371); + setState(375); _errHandler.sync(this); _la = _input.LA(1); } } break; } - setState(374); + setState(378); match(RP); } } @@ -2526,19 +2547,19 @@ class PainlessParser extends Parser { ArgumentContext _localctx = new ArgumentContext(_ctx, getState()); enterRule(_localctx, 42, RULE_argument); try { - setState(378); - switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { + setState(382); + switch ( getInterpreter().adaptivePredict(_input,31,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(376); + setState(380); expression(0); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(377); + setState(381); funcref(); } break; @@ -2645,7 +2666,7 @@ class PainlessParser extends Parser { } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u017f\4\2\t\2\4"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u0183\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\3\2\7\2\60\n\2\f\2"+ @@ -2657,134 +2678,136 @@ class PainlessParser extends Parser { "\u0085\n\3\3\4\3\4\5\4\u0089\n\4\3\5\3\5\7\5\u008d\n\5\f\5\16\5\u0090"+ "\13\5\3\5\3\5\3\6\3\6\3\7\3\7\5\7\u0098\n\7\3\b\3\b\3\t\3\t\3\t\3\t\7"+ "\t\u00a0\n\t\f\t\16\t\u00a3\13\t\3\n\3\n\3\n\7\n\u00a8\n\n\f\n\16\n\u00ab"+ - "\13\n\3\13\3\13\3\13\3\13\3\f\3\f\3\f\5\f\u00b4\n\f\3\r\3\r\3\r\3\r\3"+ - "\r\3\r\3\r\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\5\17"+ - "\u00c8\n\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ + "\13\n\3\13\3\13\3\13\3\13\3\13\3\13\5\13\u00b3\n\13\3\f\3\f\3\f\5\f\u00b8"+ + "\n\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3"+ + "\17\3\17\3\17\3\17\5\17\u00cc\n\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ - "\3\17\3\17\3\17\7\17\u0103\n\17\f\17\16\17\u0106\13\17\3\20\3\20\3\20"+ + "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\7\17\u0107\n\17\f\17\16\17\u010a"+ + "\13\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20"+ "\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20"+ - "\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\5\20\u0125"+ - "\n\20\3\21\3\21\7\21\u0129\n\21\f\21\16\21\u012c\13\21\3\21\3\21\3\21"+ - "\7\21\u0131\n\21\f\21\16\21\u0134\13\21\3\21\3\21\3\21\3\21\3\21\3\21"+ - "\6\21\u013c\n\21\r\21\16\21\u013d\3\21\3\21\7\21\u0142\n\21\f\21\16\21"+ - "\u0145\13\21\5\21\u0147\n\21\5\21\u0149\n\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\5\22\u015b\n\22"+ - "\3\23\3\23\3\23\3\23\5\23\u0161\n\23\3\24\3\24\3\24\3\24\3\24\5\24\u0168"+ - "\n\24\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\7\26\u0172\n\26\f\26\16"+ - "\26\u0175\13\26\5\26\u0177\n\26\3\26\3\26\3\27\3\27\5\27\u017d\n\27\3"+ - "\27\2\3\34\30\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,\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\u01a5\2\61\3\2\2\2\4\u0084\3\2\2\2\6\u0088"+ - "\3\2\2\2\b\u008a\3\2\2\2\n\u0093\3\2\2\2\f\u0097\3\2\2\2\16\u0099\3\2"+ - "\2\2\20\u009b\3\2\2\2\22\u00a4\3\2\2\2\24\u00ac\3\2\2\2\26\u00b0\3\2\2"+ - "\2\30\u00b5\3\2\2\2\32\u00bc\3\2\2\2\34\u00c7\3\2\2\2\36\u0124\3\2\2\2"+ - " \u0148\3\2\2\2\"\u015a\3\2\2\2$\u0160\3\2\2\2&\u0167\3\2\2\2(\u0169\3"+ - "\2\2\2*\u016d\3\2\2\2,\u017c\3\2\2\2.\60\5\4\3\2/.\3\2\2\2\60\63\3\2\2"+ - "\2\61/\3\2\2\2\61\62\3\2\2\2\62\64\3\2\2\2\63\61\3\2\2\2\64\65\7\2\2\3"+ - "\65\3\3\2\2\2\66\67\7\16\2\2\678\7\t\2\289\5\34\17\29:\7\n\2\2:>\5\6\4"+ - "\2;<\7\17\2\2;\3\2\2\2>=\3\2\2\2?\u0085\3\2\2\2@"+ - "A\7\20\2\2AB\7\t\2\2BC\5\34\17\2CF\7\n\2\2DG\5\6\4\2EG\5\n\6\2FD\3\2\2"+ - "\2FE\3\2\2\2G\u0085\3\2\2\2HI\7\21\2\2IJ\5\b\5\2JK\7\20\2\2KL\7\t\2\2"+ - "LM\5\34\17\2MN\7\n\2\2NO\5\32\16\2O\u0085\3\2\2\2PQ\7\22\2\2QS\7\t\2\2"+ - "RT\5\f\7\2SR\3\2\2\2ST\3\2\2\2TU\3\2\2\2UW\7\r\2\2VX\5\34\17\2WV\3\2\2"+ - "\2WX\3\2\2\2XY\3\2\2\2Y[\7\r\2\2Z\\\5\16\b\2[Z\3\2\2\2[\\\3\2\2\2\\]\3"+ - "\2\2\2]`\7\n\2\2^a\5\6\4\2_a\5\n\6\2`^\3\2\2\2`_\3\2\2\2a\u0085\3\2\2"+ - "\2bc\7\22\2\2cd\7\t\2\2de\5\22\n\2ef\7K\2\2fg\7\62\2\2gh\5\34\17\2hi\7"+ - "\n\2\2ij\5\6\4\2j\u0085\3\2\2\2kl\5\20\t\2lm\5\32\16\2m\u0085\3\2\2\2"+ - "no\7\23\2\2o\u0085\5\32\16\2pq\7\24\2\2q\u0085\5\32\16\2rs\7\25\2\2st"+ - "\5\34\17\2tu\5\32\16\2u\u0085\3\2\2\2vw\7\27\2\2wy\5\b\5\2xz\5\30\r\2"+ - "yx\3\2\2\2z{\3\2\2\2{y\3\2\2\2{|\3\2\2\2|\u0085\3\2\2\2}~\7\31\2\2~\177"+ - "\5\34\17\2\177\u0080\5\32\16\2\u0080\u0085\3\2\2\2\u0081\u0082\5\34\17"+ - "\2\u0082\u0083\5\32\16\2\u0083\u0085\3\2\2\2\u0084\66\3\2\2\2\u0084@\3"+ - "\2\2\2\u0084H\3\2\2\2\u0084P\3\2\2\2\u0084b\3\2\2\2\u0084k\3\2\2\2\u0084"+ - "n\3\2\2\2\u0084p\3\2\2\2\u0084r\3\2\2\2\u0084v\3\2\2\2\u0084}\3\2\2\2"+ - "\u0084\u0081\3\2\2\2\u0085\5\3\2\2\2\u0086\u0089\5\b\5\2\u0087\u0089\5"+ - "\4\3\2\u0088\u0086\3\2\2\2\u0088\u0087\3\2\2\2\u0089\7\3\2\2\2\u008a\u008e"+ - "\7\5\2\2\u008b\u008d\5\4\3\2\u008c\u008b\3\2\2\2\u008d\u0090\3\2\2\2\u008e"+ - "\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0091\3\2\2\2\u0090\u008e\3\2"+ - "\2\2\u0091\u0092\7\6\2\2\u0092\t\3\2\2\2\u0093\u0094\7\r\2\2\u0094\13"+ - "\3\2\2\2\u0095\u0098\5\20\t\2\u0096\u0098\5\34\17\2\u0097\u0095\3\2\2"+ - "\2\u0097\u0096\3\2\2\2\u0098\r\3\2\2\2\u0099\u009a\5\34\17\2\u009a\17"+ - "\3\2\2\2\u009b\u009c\5\22\n\2\u009c\u00a1\5\26\f\2\u009d\u009e\7\f\2\2"+ - "\u009e\u00a0\5\26\f\2\u009f\u009d\3\2\2\2\u00a0\u00a3\3\2\2\2\u00a1\u009f"+ - "\3\2\2\2\u00a1\u00a2\3\2\2\2\u00a2\21\3\2\2\2\u00a3\u00a1\3\2\2\2\u00a4"+ - "\u00a9\7J\2\2\u00a5\u00a6\7\7\2\2\u00a6\u00a8\7\b\2\2\u00a7\u00a5\3\2"+ - "\2\2\u00a8\u00ab\3\2\2\2\u00a9\u00a7\3\2\2\2\u00a9\u00aa\3\2\2\2\u00aa"+ - "\23\3\2\2\2\u00ab\u00a9\3\2\2\2\u00ac\u00ad\7J\2\2\u00ad\u00ae\7\63\2"+ - "\2\u00ae\u00af\t\2\2\2\u00af\25\3\2\2\2\u00b0\u00b3\7K\2\2\u00b1\u00b2"+ - "\7\66\2\2\u00b2\u00b4\5\34\17\2\u00b3\u00b1\3\2\2\2\u00b3\u00b4\3\2\2"+ - "\2\u00b4\27\3\2\2\2\u00b5\u00b6\7\30\2\2\u00b6\u00b7\7\t\2\2\u00b7\u00b8"+ - "\7J\2\2\u00b8\u00b9\7K\2\2\u00b9\u00ba\7\n\2\2\u00ba\u00bb\5\b\5\2\u00bb"+ - "\31\3\2\2\2\u00bc\u00bd\t\3\2\2\u00bd\33\3\2\2\2\u00be\u00bf\b\17\1\2"+ - "\u00bf\u00c0\5 \21\2\u00c0\u00c1\t\4\2\2\u00c1\u00c2\5\34\17\3\u00c2\u00c3"+ - "\b\17\1\2\u00c3\u00c8\3\2\2\2\u00c4\u00c5\5\36\20\2\u00c5\u00c6\b\17\1"+ - "\2\u00c6\u00c8\3\2\2\2\u00c7\u00be\3\2\2\2\u00c7\u00c4\3\2\2\2\u00c8\u0104"+ - "\3\2\2\2\u00c9\u00ca\f\16\2\2\u00ca\u00cb\t\5\2\2\u00cb\u00cc\5\34\17"+ - "\17\u00cc\u00cd\b\17\1\2\u00cd\u0103\3\2\2\2\u00ce\u00cf\f\r\2\2\u00cf"+ - "\u00d0\t\6\2\2\u00d0\u00d1\5\34\17\16\u00d1\u00d2\b\17\1\2\u00d2\u0103"+ - "\3\2\2\2\u00d3\u00d4\f\f\2\2\u00d4\u00d5\t\7\2\2\u00d5\u00d6\5\34\17\r"+ - "\u00d6\u00d7\b\17\1\2\u00d7\u0103\3\2\2\2\u00d8\u00d9\f\13\2\2\u00d9\u00da"+ - "\t\b\2\2\u00da\u00db\5\34\17\f\u00db\u00dc\b\17\1\2\u00dc\u0103\3\2\2"+ - "\2\u00dd\u00de\f\n\2\2\u00de\u00df\t\t\2\2\u00df\u00e0\5\34\17\13\u00e0"+ - "\u00e1\b\17\1\2\u00e1\u0103\3\2\2\2\u00e2\u00e3\f\t\2\2\u00e3\u00e4\7"+ - ",\2\2\u00e4\u00e5\5\34\17\n\u00e5\u00e6\b\17\1\2\u00e6\u0103\3\2\2\2\u00e7"+ - "\u00e8\f\b\2\2\u00e8\u00e9\7-\2\2\u00e9\u00ea\5\34\17\t\u00ea\u00eb\b"+ - "\17\1\2\u00eb\u0103\3\2\2\2\u00ec\u00ed\f\7\2\2\u00ed\u00ee\7.\2\2\u00ee"+ - "\u00ef\5\34\17\b\u00ef\u00f0\b\17\1\2\u00f0\u0103\3\2\2\2\u00f1\u00f2"+ - "\f\6\2\2\u00f2\u00f3\7/\2\2\u00f3\u00f4\5\34\17\7\u00f4\u00f5\b\17\1\2"+ - "\u00f5\u0103\3\2\2\2\u00f6\u00f7\f\5\2\2\u00f7\u00f8\7\60\2\2\u00f8\u00f9"+ - "\5\34\17\6\u00f9\u00fa\b\17\1\2\u00fa\u0103\3\2\2\2\u00fb\u00fc\f\4\2"+ - "\2\u00fc\u00fd\7\61\2\2\u00fd\u00fe\5\34\17\2\u00fe\u00ff\7\62\2\2\u00ff"+ - "\u0100\5\34\17\4\u0100\u0101\b\17\1\2\u0101\u0103\3\2\2\2\u0102\u00c9"+ - "\3\2\2\2\u0102\u00ce\3\2\2\2\u0102\u00d3\3\2\2\2\u0102\u00d8\3\2\2\2\u0102"+ - "\u00dd\3\2\2\2\u0102\u00e2\3\2\2\2\u0102\u00e7\3\2\2\2\u0102\u00ec\3\2"+ - "\2\2\u0102\u00f1\3\2\2\2\u0102\u00f6\3\2\2\2\u0102\u00fb\3\2\2\2\u0103"+ - "\u0106\3\2\2\2\u0104\u0102\3\2\2\2\u0104\u0105\3\2\2\2\u0105\35\3\2\2"+ - "\2\u0106\u0104\3\2\2\2\u0107\u0108\6\20\16\3\u0108\u0109\t\n\2\2\u0109"+ - "\u0125\5 \21\2\u010a\u010b\6\20\17\3\u010b\u010c\5 \21\2\u010c\u010d\t"+ - "\n\2\2\u010d\u0125\3\2\2\2\u010e\u010f\6\20\20\3\u010f\u0125\5 \21\2\u0110"+ - "\u0111\6\20\21\3\u0111\u0112\t\13\2\2\u0112\u0125\b\20\1\2\u0113\u0114"+ - "\6\20\22\3\u0114\u0115\7G\2\2\u0115\u0125\b\20\1\2\u0116\u0117\6\20\23"+ - "\3\u0117\u0118\7H\2\2\u0118\u0125\b\20\1\2\u0119\u011a\6\20\24\3\u011a"+ - "\u011b\7I\2\2\u011b\u0125\b\20\1\2\u011c\u011d\6\20\25\3\u011d\u011e\t"+ - "\f\2\2\u011e\u0125\5\36\20\2\u011f\u0120\7\t\2\2\u0120\u0121\5\22\n\2"+ - "\u0121\u0122\7\n\2\2\u0122\u0123\5\36\20\2\u0123\u0125\3\2\2\2\u0124\u0107"+ - "\3\2\2\2\u0124\u010a\3\2\2\2\u0124\u010e\3\2\2\2\u0124\u0110\3\2\2\2\u0124"+ - "\u0113\3\2\2\2\u0124\u0116\3\2\2\2\u0124\u0119\3\2\2\2\u0124\u011c\3\2"+ - "\2\2\u0124\u011f\3\2\2\2\u0125\37\3\2\2\2\u0126\u012a\5\"\22\2\u0127\u0129"+ - "\5$\23\2\u0128\u0127\3\2\2\2\u0129\u012c\3\2\2\2\u012a\u0128\3\2\2\2\u012a"+ - "\u012b\3\2\2\2\u012b\u0149\3\2\2\2\u012c\u012a\3\2\2\2\u012d\u012e\5\22"+ - "\n\2\u012e\u0132\5&\24\2\u012f\u0131\5$\23\2\u0130\u012f\3\2\2\2\u0131"+ - "\u0134\3\2\2\2\u0132\u0130\3\2\2\2\u0132\u0133\3\2\2\2\u0133\u0149\3\2"+ - "\2\2\u0134\u0132\3\2\2\2\u0135\u0136\7\26\2\2\u0136\u013b\7J\2\2\u0137"+ - "\u0138\7\7\2\2\u0138\u0139\5\34\17\2\u0139\u013a\7\b\2\2\u013a\u013c\3"+ - "\2\2\2\u013b\u0137\3\2\2\2\u013c\u013d\3\2\2\2\u013d\u013b\3\2\2\2\u013d"+ - "\u013e\3\2\2\2\u013e\u0146\3\2\2\2\u013f\u0143\5&\24\2\u0140\u0142\5$"+ - "\23\2\u0141\u0140\3\2\2\2\u0142\u0145\3\2\2\2\u0143\u0141\3\2\2\2\u0143"+ - "\u0144\3\2\2\2\u0144\u0147\3\2\2\2\u0145\u0143\3\2\2\2\u0146\u013f\3\2"+ - "\2\2\u0146\u0147\3\2\2\2\u0147\u0149\3\2\2\2\u0148\u0126\3\2\2\2\u0148"+ - "\u012d\3\2\2\2\u0148\u0135\3\2\2\2\u0149!\3\2\2\2\u014a\u014b\6\22\26"+ - "\3\u014b\u014c\7\t\2\2\u014c\u014d\5\34\17\2\u014d\u014e\7\n\2\2\u014e"+ - "\u014f\b\22\1\2\u014f\u015b\3\2\2\2\u0150\u0151\6\22\27\3\u0151\u0152"+ - "\7\t\2\2\u0152\u0153\5\36\20\2\u0153\u0154\7\n\2\2\u0154\u015b\3\2\2\2"+ - "\u0155\u015b\7F\2\2\u0156\u015b\7K\2\2\u0157\u0158\7\26\2\2\u0158\u0159"+ - "\7J\2\2\u0159\u015b\5*\26\2\u015a\u014a\3\2\2\2\u015a\u0150\3\2\2\2\u015a"+ - "\u0155\3\2\2\2\u015a\u0156\3\2\2\2\u015a\u0157\3\2\2\2\u015b#\3\2\2\2"+ - "\u015c\u015d\6\23\30\3\u015d\u0161\5&\24\2\u015e\u015f\6\23\31\3\u015f"+ - "\u0161\5(\25\2\u0160\u015c\3\2\2\2\u0160\u015e\3\2\2\2\u0161%\3\2\2\2"+ - "\u0162\u0163\7\13\2\2\u0163\u0164\7M\2\2\u0164\u0168\5*\26\2\u0165\u0166"+ - "\7\13\2\2\u0166\u0168\t\r\2\2\u0167\u0162\3\2\2\2\u0167\u0165\3\2\2\2"+ - "\u0168\'\3\2\2\2\u0169\u016a\7\7\2\2\u016a\u016b\5\34\17\2\u016b\u016c"+ - "\7\b\2\2\u016c)\3\2\2\2\u016d\u0176\7\t\2\2\u016e\u0173\5,\27\2\u016f"+ - "\u0170\7\f\2\2\u0170\u0172\5,\27\2\u0171\u016f\3\2\2\2\u0172\u0175\3\2"+ - "\2\2\u0173\u0171\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u0177\3\2\2\2\u0175"+ - "\u0173\3\2\2\2\u0176\u016e\3\2\2\2\u0176\u0177\3\2\2\2\u0177\u0178\3\2"+ - "\2\2\u0178\u0179\7\n\2\2\u0179+\3\2\2\2\u017a\u017d\5\34\17\2\u017b\u017d"+ - "\5\24\13\2\u017c\u017a\3\2\2\2\u017c\u017b\3\2\2\2\u017d-\3\2\2\2!\61"+ - ">FSW[`{\u0084\u0088\u008e\u0097\u00a1\u00a9\u00b3\u00c7\u0102\u0104\u0124"+ - "\u012a\u0132\u013d\u0143\u0146\u0148\u015a\u0160\u0167\u0173\u0176\u017c"; + "\3\20\3\20\5\20\u0129\n\20\3\21\3\21\7\21\u012d\n\21\f\21\16\21\u0130"+ + "\13\21\3\21\3\21\3\21\7\21\u0135\n\21\f\21\16\21\u0138\13\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\6\21\u0140\n\21\r\21\16\21\u0141\3\21\3\21\7\21\u0146"+ + "\n\21\f\21\16\21\u0149\13\21\5\21\u014b\n\21\5\21\u014d\n\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"+ + "\5\22\u015f\n\22\3\23\3\23\3\23\3\23\5\23\u0165\n\23\3\24\3\24\3\24\3"+ + "\24\3\24\5\24\u016c\n\24\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\7\26"+ + "\u0176\n\26\f\26\16\26\u0179\13\26\5\26\u017b\n\26\3\26\3\26\3\27\3\27"+ + "\5\27\u0181\n\27\3\27\2\3\34\30\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36"+ + " \"$&(*,\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\u01aa\2\61\3\2\2\2\4\u0084"+ + "\3\2\2\2\6\u0088\3\2\2\2\b\u008a\3\2\2\2\n\u0093\3\2\2\2\f\u0097\3\2\2"+ + "\2\16\u0099\3\2\2\2\20\u009b\3\2\2\2\22\u00a4\3\2\2\2\24\u00b2\3\2\2\2"+ + "\26\u00b4\3\2\2\2\30\u00b9\3\2\2\2\32\u00c0\3\2\2\2\34\u00cb\3\2\2\2\36"+ + "\u0128\3\2\2\2 \u014c\3\2\2\2\"\u015e\3\2\2\2$\u0164\3\2\2\2&\u016b\3"+ + "\2\2\2(\u016d\3\2\2\2*\u0171\3\2\2\2,\u0180\3\2\2\2.\60\5\4\3\2/.\3\2"+ + "\2\2\60\63\3\2\2\2\61/\3\2\2\2\61\62\3\2\2\2\62\64\3\2\2\2\63\61\3\2\2"+ + "\2\64\65\7\2\2\3\65\3\3\2\2\2\66\67\7\16\2\2\678\7\t\2\289\5\34\17\29"+ + ":\7\n\2\2:>\5\6\4\2;<\7\17\2\2;\3\2\2\2>=\3\2\2\2"+ + "?\u0085\3\2\2\2@A\7\20\2\2AB\7\t\2\2BC\5\34\17\2CF\7\n\2\2DG\5\6\4\2E"+ + "G\5\n\6\2FD\3\2\2\2FE\3\2\2\2G\u0085\3\2\2\2HI\7\21\2\2IJ\5\b\5\2JK\7"+ + "\20\2\2KL\7\t\2\2LM\5\34\17\2MN\7\n\2\2NO\5\32\16\2O\u0085\3\2\2\2PQ\7"+ + "\22\2\2QS\7\t\2\2RT\5\f\7\2SR\3\2\2\2ST\3\2\2\2TU\3\2\2\2UW\7\r\2\2VX"+ + "\5\34\17\2WV\3\2\2\2WX\3\2\2\2XY\3\2\2\2Y[\7\r\2\2Z\\\5\16\b\2[Z\3\2\2"+ + "\2[\\\3\2\2\2\\]\3\2\2\2]`\7\n\2\2^a\5\6\4\2_a\5\n\6\2`^\3\2\2\2`_\3\2"+ + "\2\2a\u0085\3\2\2\2bc\7\22\2\2cd\7\t\2\2de\5\22\n\2ef\7K\2\2fg\7\62\2"+ + "\2gh\5\34\17\2hi\7\n\2\2ij\5\6\4\2j\u0085\3\2\2\2kl\5\20\t\2lm\5\32\16"+ + "\2m\u0085\3\2\2\2no\7\23\2\2o\u0085\5\32\16\2pq\7\24\2\2q\u0085\5\32\16"+ + "\2rs\7\25\2\2st\5\34\17\2tu\5\32\16\2u\u0085\3\2\2\2vw\7\27\2\2wy\5\b"+ + "\5\2xz\5\30\r\2yx\3\2\2\2z{\3\2\2\2{y\3\2\2\2{|\3\2\2\2|\u0085\3\2\2\2"+ + "}~\7\31\2\2~\177\5\34\17\2\177\u0080\5\32\16\2\u0080\u0085\3\2\2\2\u0081"+ + "\u0082\5\34\17\2\u0082\u0083\5\32\16\2\u0083\u0085\3\2\2\2\u0084\66\3"+ + "\2\2\2\u0084@\3\2\2\2\u0084H\3\2\2\2\u0084P\3\2\2\2\u0084b\3\2\2\2\u0084"+ + "k\3\2\2\2\u0084n\3\2\2\2\u0084p\3\2\2\2\u0084r\3\2\2\2\u0084v\3\2\2\2"+ + "\u0084}\3\2\2\2\u0084\u0081\3\2\2\2\u0085\5\3\2\2\2\u0086\u0089\5\b\5"+ + "\2\u0087\u0089\5\4\3\2\u0088\u0086\3\2\2\2\u0088\u0087\3\2\2\2\u0089\7"+ + "\3\2\2\2\u008a\u008e\7\5\2\2\u008b\u008d\5\4\3\2\u008c\u008b\3\2\2\2\u008d"+ + "\u0090\3\2\2\2\u008e\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0091\3\2"+ + "\2\2\u0090\u008e\3\2\2\2\u0091\u0092\7\6\2\2\u0092\t\3\2\2\2\u0093\u0094"+ + "\7\r\2\2\u0094\13\3\2\2\2\u0095\u0098\5\20\t\2\u0096\u0098\5\34\17\2\u0097"+ + "\u0095\3\2\2\2\u0097\u0096\3\2\2\2\u0098\r\3\2\2\2\u0099\u009a\5\34\17"+ + "\2\u009a\17\3\2\2\2\u009b\u009c\5\22\n\2\u009c\u00a1\5\26\f\2\u009d\u009e"+ + "\7\f\2\2\u009e\u00a0\5\26\f\2\u009f\u009d\3\2\2\2\u00a0\u00a3\3\2\2\2"+ + "\u00a1\u009f\3\2\2\2\u00a1\u00a2\3\2\2\2\u00a2\21\3\2\2\2\u00a3\u00a1"+ + "\3\2\2\2\u00a4\u00a9\7J\2\2\u00a5\u00a6\7\7\2\2\u00a6\u00a8\7\b\2\2\u00a7"+ + "\u00a5\3\2\2\2\u00a8\u00ab\3\2\2\2\u00a9\u00a7\3\2\2\2\u00a9\u00aa\3\2"+ + "\2\2\u00aa\23\3\2\2\2\u00ab\u00a9\3\2\2\2\u00ac\u00ad\7J\2\2\u00ad\u00ae"+ + "\7\63\2\2\u00ae\u00b3\t\2\2\2\u00af\u00b0\7K\2\2\u00b0\u00b1\7\63\2\2"+ + "\u00b1\u00b3\7K\2\2\u00b2\u00ac\3\2\2\2\u00b2\u00af\3\2\2\2\u00b3\25\3"+ + "\2\2\2\u00b4\u00b7\7K\2\2\u00b5\u00b6\7\66\2\2\u00b6\u00b8\5\34\17\2\u00b7"+ + "\u00b5\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b8\27\3\2\2\2\u00b9\u00ba\7\30\2"+ + "\2\u00ba\u00bb\7\t\2\2\u00bb\u00bc\7J\2\2\u00bc\u00bd\7K\2\2\u00bd\u00be"+ + "\7\n\2\2\u00be\u00bf\5\b\5\2\u00bf\31\3\2\2\2\u00c0\u00c1\t\3\2\2\u00c1"+ + "\33\3\2\2\2\u00c2\u00c3\b\17\1\2\u00c3\u00c4\5 \21\2\u00c4\u00c5\t\4\2"+ + "\2\u00c5\u00c6\5\34\17\3\u00c6\u00c7\b\17\1\2\u00c7\u00cc\3\2\2\2\u00c8"+ + "\u00c9\5\36\20\2\u00c9\u00ca\b\17\1\2\u00ca\u00cc\3\2\2\2\u00cb\u00c2"+ + "\3\2\2\2\u00cb\u00c8\3\2\2\2\u00cc\u0108\3\2\2\2\u00cd\u00ce\f\16\2\2"+ + "\u00ce\u00cf\t\5\2\2\u00cf\u00d0\5\34\17\17\u00d0\u00d1\b\17\1\2\u00d1"+ + "\u0107\3\2\2\2\u00d2\u00d3\f\r\2\2\u00d3\u00d4\t\6\2\2\u00d4\u00d5\5\34"+ + "\17\16\u00d5\u00d6\b\17\1\2\u00d6\u0107\3\2\2\2\u00d7\u00d8\f\f\2\2\u00d8"+ + "\u00d9\t\7\2\2\u00d9\u00da\5\34\17\r\u00da\u00db\b\17\1\2\u00db\u0107"+ + "\3\2\2\2\u00dc\u00dd\f\13\2\2\u00dd\u00de\t\b\2\2\u00de\u00df\5\34\17"+ + "\f\u00df\u00e0\b\17\1\2\u00e0\u0107\3\2\2\2\u00e1\u00e2\f\n\2\2\u00e2"+ + "\u00e3\t\t\2\2\u00e3\u00e4\5\34\17\13\u00e4\u00e5\b\17\1\2\u00e5\u0107"+ + "\3\2\2\2\u00e6\u00e7\f\t\2\2\u00e7\u00e8\7,\2\2\u00e8\u00e9\5\34\17\n"+ + "\u00e9\u00ea\b\17\1\2\u00ea\u0107\3\2\2\2\u00eb\u00ec\f\b\2\2\u00ec\u00ed"+ + "\7-\2\2\u00ed\u00ee\5\34\17\t\u00ee\u00ef\b\17\1\2\u00ef\u0107\3\2\2\2"+ + "\u00f0\u00f1\f\7\2\2\u00f1\u00f2\7.\2\2\u00f2\u00f3\5\34\17\b\u00f3\u00f4"+ + "\b\17\1\2\u00f4\u0107\3\2\2\2\u00f5\u00f6\f\6\2\2\u00f6\u00f7\7/\2\2\u00f7"+ + "\u00f8\5\34\17\7\u00f8\u00f9\b\17\1\2\u00f9\u0107\3\2\2\2\u00fa\u00fb"+ + "\f\5\2\2\u00fb\u00fc\7\60\2\2\u00fc\u00fd\5\34\17\6\u00fd\u00fe\b\17\1"+ + "\2\u00fe\u0107\3\2\2\2\u00ff\u0100\f\4\2\2\u0100\u0101\7\61\2\2\u0101"+ + "\u0102\5\34\17\2\u0102\u0103\7\62\2\2\u0103\u0104\5\34\17\4\u0104\u0105"+ + "\b\17\1\2\u0105\u0107\3\2\2\2\u0106\u00cd\3\2\2\2\u0106\u00d2\3\2\2\2"+ + "\u0106\u00d7\3\2\2\2\u0106\u00dc\3\2\2\2\u0106\u00e1\3\2\2\2\u0106\u00e6"+ + "\3\2\2\2\u0106\u00eb\3\2\2\2\u0106\u00f0\3\2\2\2\u0106\u00f5\3\2\2\2\u0106"+ + "\u00fa\3\2\2\2\u0106\u00ff\3\2\2\2\u0107\u010a\3\2\2\2\u0108\u0106\3\2"+ + "\2\2\u0108\u0109\3\2\2\2\u0109\35\3\2\2\2\u010a\u0108\3\2\2\2\u010b\u010c"+ + "\6\20\16\3\u010c\u010d\t\n\2\2\u010d\u0129\5 \21\2\u010e\u010f\6\20\17"+ + "\3\u010f\u0110\5 \21\2\u0110\u0111\t\n\2\2\u0111\u0129\3\2\2\2\u0112\u0113"+ + "\6\20\20\3\u0113\u0129\5 \21\2\u0114\u0115\6\20\21\3\u0115\u0116\t\13"+ + "\2\2\u0116\u0129\b\20\1\2\u0117\u0118\6\20\22\3\u0118\u0119\7G\2\2\u0119"+ + "\u0129\b\20\1\2\u011a\u011b\6\20\23\3\u011b\u011c\7H\2\2\u011c\u0129\b"+ + "\20\1\2\u011d\u011e\6\20\24\3\u011e\u011f\7I\2\2\u011f\u0129\b\20\1\2"+ + "\u0120\u0121\6\20\25\3\u0121\u0122\t\f\2\2\u0122\u0129\5\36\20\2\u0123"+ + "\u0124\7\t\2\2\u0124\u0125\5\22\n\2\u0125\u0126\7\n\2\2\u0126\u0127\5"+ + "\36\20\2\u0127\u0129\3\2\2\2\u0128\u010b\3\2\2\2\u0128\u010e\3\2\2\2\u0128"+ + "\u0112\3\2\2\2\u0128\u0114\3\2\2\2\u0128\u0117\3\2\2\2\u0128\u011a\3\2"+ + "\2\2\u0128\u011d\3\2\2\2\u0128\u0120\3\2\2\2\u0128\u0123\3\2\2\2\u0129"+ + "\37\3\2\2\2\u012a\u012e\5\"\22\2\u012b\u012d\5$\23\2\u012c\u012b\3\2\2"+ + "\2\u012d\u0130\3\2\2\2\u012e\u012c\3\2\2\2\u012e\u012f\3\2\2\2\u012f\u014d"+ + "\3\2\2\2\u0130\u012e\3\2\2\2\u0131\u0132\5\22\n\2\u0132\u0136\5&\24\2"+ + "\u0133\u0135\5$\23\2\u0134\u0133\3\2\2\2\u0135\u0138\3\2\2\2\u0136\u0134"+ + "\3\2\2\2\u0136\u0137\3\2\2\2\u0137\u014d\3\2\2\2\u0138\u0136\3\2\2\2\u0139"+ + "\u013a\7\26\2\2\u013a\u013f\7J\2\2\u013b\u013c\7\7\2\2\u013c\u013d\5\34"+ + "\17\2\u013d\u013e\7\b\2\2\u013e\u0140\3\2\2\2\u013f\u013b\3\2\2\2\u0140"+ + "\u0141\3\2\2\2\u0141\u013f\3\2\2\2\u0141\u0142\3\2\2\2\u0142\u014a\3\2"+ + "\2\2\u0143\u0147\5&\24\2\u0144\u0146\5$\23\2\u0145\u0144\3\2\2\2\u0146"+ + "\u0149\3\2\2\2\u0147\u0145\3\2\2\2\u0147\u0148\3\2\2\2\u0148\u014b\3\2"+ + "\2\2\u0149\u0147\3\2\2\2\u014a\u0143\3\2\2\2\u014a\u014b\3\2\2\2\u014b"+ + "\u014d\3\2\2\2\u014c\u012a\3\2\2\2\u014c\u0131\3\2\2\2\u014c\u0139\3\2"+ + "\2\2\u014d!\3\2\2\2\u014e\u014f\6\22\26\3\u014f\u0150\7\t\2\2\u0150\u0151"+ + "\5\34\17\2\u0151\u0152\7\n\2\2\u0152\u0153\b\22\1\2\u0153\u015f\3\2\2"+ + "\2\u0154\u0155\6\22\27\3\u0155\u0156\7\t\2\2\u0156\u0157\5\36\20\2\u0157"+ + "\u0158\7\n\2\2\u0158\u015f\3\2\2\2\u0159\u015f\7F\2\2\u015a\u015f\7K\2"+ + "\2\u015b\u015c\7\26\2\2\u015c\u015d\7J\2\2\u015d\u015f\5*\26\2\u015e\u014e"+ + "\3\2\2\2\u015e\u0154\3\2\2\2\u015e\u0159\3\2\2\2\u015e\u015a\3\2\2\2\u015e"+ + "\u015b\3\2\2\2\u015f#\3\2\2\2\u0160\u0161\6\23\30\3\u0161\u0165\5&\24"+ + "\2\u0162\u0163\6\23\31\3\u0163\u0165\5(\25\2\u0164\u0160\3\2\2\2\u0164"+ + "\u0162\3\2\2\2\u0165%\3\2\2\2\u0166\u0167\7\13\2\2\u0167\u0168\7M\2\2"+ + "\u0168\u016c\5*\26\2\u0169\u016a\7\13\2\2\u016a\u016c\t\r\2\2\u016b\u0166"+ + "\3\2\2\2\u016b\u0169\3\2\2\2\u016c\'\3\2\2\2\u016d\u016e\7\7\2\2\u016e"+ + "\u016f\5\34\17\2\u016f\u0170\7\b\2\2\u0170)\3\2\2\2\u0171\u017a\7\t\2"+ + "\2\u0172\u0177\5,\27\2\u0173\u0174\7\f\2\2\u0174\u0176\5,\27\2\u0175\u0173"+ + "\3\2\2\2\u0176\u0179\3\2\2\2\u0177\u0175\3\2\2\2\u0177\u0178\3\2\2\2\u0178"+ + "\u017b\3\2\2\2\u0179\u0177\3\2\2\2\u017a\u0172\3\2\2\2\u017a\u017b\3\2"+ + "\2\2\u017b\u017c\3\2\2\2\u017c\u017d\7\n\2\2\u017d+\3\2\2\2\u017e\u0181"+ + "\5\34\17\2\u017f\u0181\5\24\13\2\u0180\u017e\3\2\2\2\u0180\u017f\3\2\2"+ + "\2\u0181-\3\2\2\2\"\61>FSW[`{\u0084\u0088\u008e\u0097\u00a1\u00a9\u00b2"+ + "\u00b7\u00cb\u0106\u0108\u0128\u012e\u0136\u0141\u0147\u014a\u014c\u015e"+ + "\u0164\u016b\u0177\u017a\u0180"; 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 471cb7d64ea..82ccf4083f1 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 @@ -94,6 +94,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; @@ -405,15 +406,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..d6c16b6ffc2 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java @@ -0,0 +1,118 @@ +/* + * 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.Variables; +import org.elasticsearch.painless.Variables.Variable; +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(Variables 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.loadLocal(captured.slot); + } else if (defInterface) { + // dynamic interface, typed implementation + writer.push("S" + captured.type.name + "." + call + ",1"); + writer.loadLocal(captured.slot); + } else if (ref == null) { + // typed interface, dynamic implementation + writer.loadLocal(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.loadLocal(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 38b7a1b8ddb..26be001b211 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 5e28cd31b98..40e95879634 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 28c57744860..3a06b9c899e 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(variables); @@ -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 ec9d5bbe9e7..aad11d2e91a 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 9a6fe798957..2a8fb0e4063 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 @@ -197,7 +197,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, 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/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, () -> { From 16d17351f77ccae519a7f0263201056179e39d97 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Fri, 10 Jun 2016 17:37:57 -0400 Subject: [PATCH 2/3] don't use loadLocal, add new node to package-info --- .../painless/node/ECapturingFunctionRef.java | 9 +++++---- .../org/elasticsearch/painless/node/package-info.java | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) 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 index 4e125a55de6..3e35602a3a2 100644 --- 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 @@ -26,6 +26,7 @@ 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; @@ -77,19 +78,19 @@ public class ECapturingFunctionRef extends AExpression { if (defInterface && captured.type.sort == Definition.Sort.DEF) { // dynamic interface, dynamic implementation writer.push("D" + type + "." + call + ",1"); - writer.loadLocal(captured.slot); + 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.loadLocal(captured.slot); + writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.slot); } else if (ref == null) { // typed interface, dynamic implementation - writer.loadLocal(captured.slot); + 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.loadLocal(captured.slot); + 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()); 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. From 387155559ead75fbc6d6f330528229be47e75781 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 10 Jun 2016 15:32:30 -0400 Subject: [PATCH 3/3] Make TimeValue Writeable instead of Streamable Writeable is better for immutable objects like TimeValue. Switch to writeZLong which takes up less space than the original writeLong in the majority of cases. Since we expect negative TimeValues we shouldn't use writeVLong. --- .../cluster/health/ClusterHealthRequest.java | 4 +- .../cluster/health/ClusterHealthResponse.java | 2 +- .../hotthreads/NodesHotThreadsRequest.java | 2 +- .../action/bulk/BulkRequest.java | 2 +- .../action/index/IndexRequest.java | 10 +--- .../support/master/AcknowledgedRequest.java | 3 +- .../support/master/MasterNodeRequest.java | 2 +- .../support/nodes/BaseNodesRequest.java | 11 +---- .../replication/ReplicationRequest.java | 2 +- .../InstanceShardOperationRequest.java | 2 +- .../support/tasks/BaseTasksRequest.java | 6 +-- .../common/io/stream/Streamable.java | 2 + .../common/io/stream/Writeable.java | 2 - .../elasticsearch/common/unit/TimeValue.java | 46 +++++++------------ .../zen/ping/unicast/UnicastZenPing.java | 4 +- .../java/org/elasticsearch/search/Scroll.java | 13 +----- .../elasticsearch/threadpool/ThreadPool.java | 2 +- .../common/unit/TimeValueTests.java | 14 ++++-- .../reindex/AbstractBulkByScrollRequest.java | 4 +- .../index/reindex/BulkByScrollTask.java | 4 +- .../reindex/BulkIndexByScrollResponse.java | 2 +- 21 files changed, 52 insertions(+), 87 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java index 59b426d8c31..27970f332fc 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java @@ -33,8 +33,6 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static org.elasticsearch.common.unit.TimeValue.readTimeValue; - /** * */ @@ -160,7 +158,7 @@ public class ClusterHealthRequest extends MasterNodeReadRequest implements Composite } } refreshPolicy = RefreshPolicy.readFrom(in); - timeout = TimeValue.readTimeValue(in); + timeout = new TimeValue(in); } @Override diff --git a/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java b/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java index bc1e631e559..a79f6d83204 100644 --- a/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java +++ b/core/src/main/java/org/elasticsearch/action/index/IndexRequest.java @@ -633,9 +633,8 @@ public class IndexRequest extends ReplicatedWriteRequest implement routing = in.readOptionalString(); parent = in.readOptionalString(); timestamp = in.readOptionalString(); - ttl = in.readBoolean() ? TimeValue.readTimeValue(in) : null; + ttl = in.readOptionalWriteable(TimeValue::new); source = in.readBytesReference(); - opType = OpType.fromId(in.readByte()); version = in.readLong(); versionType = VersionType.fromValue(in.readByte()); @@ -650,12 +649,7 @@ public class IndexRequest extends ReplicatedWriteRequest implement out.writeOptionalString(routing); out.writeOptionalString(parent); out.writeOptionalString(timestamp); - if (ttl == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - ttl.writeTo(out); - } + out.writeOptionalWriteable(ttl); out.writeBytesReference(source); out.writeByte(opType.id()); out.writeLong(version); diff --git a/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java b/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java index 9e45bccc547..e3f32543bf2 100644 --- a/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java +++ b/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; -import static org.elasticsearch.common.unit.TimeValue.readTimeValue; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; /** @@ -75,7 +74,7 @@ public abstract class AcknowledgedRequest public void readFrom(StreamInput in) throws IOException { super.readFrom(in); nodesIds = in.readStringArray(); - if (in.readBoolean()) { - timeout = TimeValue.readTimeValue(in); - } + timeout = in.readOptionalWriteable(TimeValue::new); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArrayNullable(nodesIds); - if (timeout == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - timeout.writeTo(out); - } + out.writeOptionalWriteable(timeout); } } diff --git a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationRequest.java b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationRequest.java index 44c420598b5..c444fd2cf39 100644 --- a/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationRequest.java +++ b/core/src/main/java/org/elasticsearch/action/support/replication/ReplicationRequest.java @@ -181,7 +181,7 @@ public abstract class ReplicationRequest> extends parentTaskId = TaskId.readFromStream(in); nodesIds = in.readStringArray(); actions = in.readStringArray(); - if (in.readBoolean()) { - timeout = TimeValue.readTimeValue(in); - } + timeout = in.readOptionalWriteable(TimeValue::new); } @Override @@ -156,7 +154,7 @@ public class BaseTasksRequest> extends parentTaskId.writeTo(out); out.writeStringArrayNullable(nodesIds); out.writeStringArrayNullable(actions); - out.writeOptionalStreamable(timeout); + out.writeOptionalWriteable(timeout); } public boolean match(Task task) { diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/Streamable.java b/core/src/main/java/org/elasticsearch/common/io/stream/Streamable.java index 4added7df31..a37c6371482 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/Streamable.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/Streamable.java @@ -26,6 +26,8 @@ import java.io.IOException; * across the wire" using Elasticsearch's internal protocol. If the implementer also implements equals and hashCode then a copy made by * serializing and deserializing must be equal and have the same hashCode. It isn't required that such a copy be entirely unchanged. For * example, {@link org.elasticsearch.common.unit.TimeValue} converts the time to nanoseconds for serialization. + * {@linkplain org.elasticsearch.common.unit.TimeValue} actually implements {@linkplain Writeable} not {@linkplain Streamable} but it has + * the same contract. * * Prefer implementing {@link Writeable} over implementing this interface where possible. Lots of code depends on this interface so this * isn't always possible. diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/Writeable.java b/core/src/main/java/org/elasticsearch/common/io/stream/Writeable.java index 55ab419881f..cf127e5b968 100644 --- a/core/src/main/java/org/elasticsearch/common/io/stream/Writeable.java +++ b/core/src/main/java/org/elasticsearch/common/io/stream/Writeable.java @@ -26,8 +26,6 @@ import java.io.IOException; * across the wire" using Elasticsearch's internal protocol. If the implementer also implements equals and hashCode then a copy made by * serializing and deserializing must be equal and have the same hashCode. It isn't required that such a copy be entirely unchanged. For * example, {@link org.elasticsearch.common.unit.TimeValue} converts the time to nanoseconds for serialization. - * {@linkplain org.elasticsearch.common.unit.TimeValue} actually implements {@linkplain Streamable} not {@linkplain Writeable} but it has - * the same contract. * * Prefer implementing this interface over implementing {@link Streamable} where possible. Lots of code depends on {@linkplain Streamable} * so this isn't always possible. diff --git a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java index c467a0c18a8..2058355d300 100644 --- a/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java +++ b/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java @@ -23,7 +23,7 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.io.stream.Streamable; +import org.elasticsearch.common.io.stream.Writeable; import org.joda.time.Period; import org.joda.time.PeriodType; import org.joda.time.format.PeriodFormat; @@ -34,7 +34,7 @@ import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; -public class TimeValue implements Streamable { +public class TimeValue implements Writeable { /** How many nano-seconds in one milli-second */ public static final long NSEC_PER_MSEC = 1000000; @@ -59,13 +59,8 @@ public class TimeValue implements Streamable { return new TimeValue(hours, TimeUnit.HOURS); } - private long duration; - - private TimeUnit timeUnit; - - private TimeValue() { - - } + private final long duration; + private final TimeUnit timeUnit; public TimeValue(long millis) { this(millis, TimeUnit.MILLISECONDS); @@ -76,6 +71,19 @@ public class TimeValue implements Streamable { this.timeUnit = timeUnit; } + /** + * Read from a stream. + */ + public TimeValue(StreamInput in) throws IOException { + duration = in.readZLong(); + timeUnit = TimeUnit.NANOSECONDS; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeZLong(nanos()); + } + public long nanos() { return timeUnit.toNanos(duration); } @@ -304,26 +312,6 @@ public class TimeValue implements Streamable { static final long C5 = C4 * 60L; static final long C6 = C5 * 24L; - public static TimeValue readTimeValue(StreamInput in) throws IOException { - TimeValue timeValue = new TimeValue(); - timeValue.readFrom(in); - return timeValue; - } - - /** - * serialization converts TimeValue internally to NANOSECONDS - */ - @Override - public void readFrom(StreamInput in) throws IOException { - duration = in.readLong(); - timeUnit = TimeUnit.NANOSECONDS; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeLong(nanos()); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ping/unicast/UnicastZenPing.java b/core/src/main/java/org/elasticsearch/discovery/zen/ping/unicast/UnicastZenPing.java index d200ca5b07b..1b4f1ba75b0 100644 --- a/core/src/main/java/org/elasticsearch/discovery/zen/ping/unicast/UnicastZenPing.java +++ b/core/src/main/java/org/elasticsearch/discovery/zen/ping/unicast/UnicastZenPing.java @@ -20,6 +20,7 @@ package org.elasticsearch.discovery.zen.ping.unicast; import com.carrotsearch.hppc.cursors.ObjectCursor; + import org.apache.lucene.util.IOUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; @@ -79,7 +80,6 @@ import java.util.function.Function; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; -import static org.elasticsearch.common.unit.TimeValue.readTimeValue; import static org.elasticsearch.common.util.concurrent.ConcurrentCollections.newConcurrentMap; import static org.elasticsearch.discovery.zen.ping.ZenPing.PingResponse.readPingResponse; @@ -545,7 +545,7 @@ public class UnicastZenPing extends AbstractLifecycleComponent implemen public void readFrom(StreamInput in) throws IOException { super.readFrom(in); id = in.readInt(); - timeout = readTimeValue(in); + timeout = new TimeValue(in); pingResponse = readPingResponse(in); } diff --git a/core/src/main/java/org/elasticsearch/search/Scroll.java b/core/src/main/java/org/elasticsearch/search/Scroll.java index 2d703bdae90..13298b2cbbe 100644 --- a/core/src/main/java/org/elasticsearch/search/Scroll.java +++ b/core/src/main/java/org/elasticsearch/search/Scroll.java @@ -26,8 +26,6 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; -import static org.elasticsearch.common.unit.TimeValue.readTimeValue; - /** * A scroll enables scrolling of search request. It holds a {@link #keepAlive()} time that * will control how long to keep the scrolling resources open. @@ -64,18 +62,11 @@ public class Scroll implements Streamable { @Override public void readFrom(StreamInput in) throws IOException { - if (in.readBoolean()) { - keepAlive = readTimeValue(in); - } + in.readOptionalWriteable(TimeValue::new); } @Override public void writeTo(StreamOutput out) throws IOException { - if (keepAlive == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - keepAlive.writeTo(out); - } + out.writeOptionalWriteable(keepAlive); } } diff --git a/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java b/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java index 5c31323b3d8..f8cb882b083 100644 --- a/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java +++ b/core/src/main/java/org/elasticsearch/threadpool/ThreadPool.java @@ -579,7 +579,7 @@ public class ThreadPool extends AbstractComponent implements Closeable { min = in.readInt(); max = in.readInt(); if (in.readBoolean()) { - keepAlive = TimeValue.readTimeValue(in); + keepAlive = new TimeValue(in); } if (in.readBoolean()) { queueSize = SizeValue.readSizeValue(in); diff --git a/core/src/test/java/org/elasticsearch/common/unit/TimeValueTests.java b/core/src/test/java/org/elasticsearch/common/unit/TimeValueTests.java index 3cd68bea038..cc36625e68f 100644 --- a/core/src/test/java/org/elasticsearch/common/unit/TimeValueTests.java +++ b/core/src/test/java/org/elasticsearch/common/unit/TimeValueTests.java @@ -28,6 +28,8 @@ import org.joda.time.PeriodType; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static org.elasticsearch.common.unit.TimeValue.timeValueNanos; +import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThan; @@ -123,20 +125,22 @@ public class TimeValueTests extends ESTestCase { TimeValue.parseTimeValue("10W", null, "test")); } - private void assertEqualityAfterSerialize(TimeValue value) throws IOException { + private void assertEqualityAfterSerialize(TimeValue value, int expectedSize) throws IOException { BytesStreamOutput out = new BytesStreamOutput(); value.writeTo(out); + assertEquals(expectedSize, out.size()); StreamInput in = StreamInput.wrap(out.bytes()); - TimeValue inValue = TimeValue.readTimeValue(in); + TimeValue inValue = new TimeValue(in); assertThat(inValue, equalTo(value)); } public void testSerialize() throws Exception { - assertEqualityAfterSerialize(new TimeValue(100, TimeUnit.DAYS)); - assertEqualityAfterSerialize(new TimeValue(-1)); - assertEqualityAfterSerialize(new TimeValue(1, TimeUnit.NANOSECONDS)); + assertEqualityAfterSerialize(new TimeValue(100, TimeUnit.DAYS), 8); + assertEqualityAfterSerialize(timeValueNanos(-1), 1); + assertEqualityAfterSerialize(timeValueNanos(1), 1); + assertEqualityAfterSerialize(timeValueSeconds(30), 6); } public void testFailOnUnknownUnits() { diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java index 7505f490f45..499dfc82457 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java @@ -299,9 +299,9 @@ public abstract class AbstractBulkByScrollRequest indexingFailures = new ArrayList<>(indexingFailuresCount);