Implement reading from null safe dereferences

Null safe dereferences make handling null or missing values shorter.
Compare without:
```
if (ctx._source.missing != null && ctx._source.missing.foo != null) {
  ctx._source.foo_length = ctx.source.missing.foo.length()
}
```

To with:
```
Integer length = ctx._source.missing?.foo?.length();
if (length != null) {
  ctx._source.foo_length = length
}
```

Combining this with the as of yet unimplemented elvis operator allows
for very concise defaults for nulls:
```
ctx._source.foo_length = ctx._source.missing?.foo?.length() ?: 0;
```

Since you have to start somewhere, we started with null safe dereferenes.

Anyway, this is a feature borrowed from groovy. Groovy allows writing to
null values like:
```
def v = null
v?.field = 'cat'
```
And the writes are simply ignored. Painless doesn't support this at this
point because it'd be complex to implement and maybe not all that useful.

There is no runtime cost for this feature if it is not used. When it is
used we implement it fairly efficiently, adding a jump rather than a
temporary variable.

This should also work fairly well with doc values.
This commit is contained in:
Nik Everett 2016-10-30 12:25:51 -04:00
parent b743ab0b07
commit d03b8e4abb
13 changed files with 699 additions and 295 deletions

View File

@ -130,6 +130,37 @@ using these characters:
|`x` | COMMENTS (aka extended) | `'a' ==~ /a #comment/x`
|=======================================================================
[float]
[[painless-deref]]
=== Dereferences
Like lots of languages, Painless uses `.` to reference fields and call methods:
[source,painless]
---------------------------------------------------------
String foo = 'foo';
TypeWithGetterOrPublicField bar = new TypeWithGetterOrPublicField()
return foo.length() + bar.x
---------------------------------------------------------
Like Groovy, Painless uses `?.` to perform null-safe references, with the
result being `null` if the left hand side is null:
[source,painless]
---------------------------------------------------------
String foo = null;
return foo?.length() // Returns null
---------------------------------------------------------
Unlike Groovy, Painless doesn't support writing to null values with this
operator:
[source,painless]
---------------------------------------------------------
TypeWithSetterOrPublicField foo = null;
foo?.x = 'bar' // Compile error
---------------------------------------------------------
[float]
[[painless-operators]]
=== Operators

View File

@ -156,11 +156,11 @@ postdot
;
callinvoke
: DOT DOTID arguments
: COND? DOT DOTID arguments
;
fieldaccess
: DOT ( DOTID | DOTINTEGER )
: COND? DOT ( DOTID | DOTINTEGER )
;
braceaccess

View File

@ -30,6 +30,9 @@ import org.elasticsearch.painless.Definition.Type;
public final class AnalyzerCaster {
public static Cast getLegalCast(Location location, Type actual, Type expected, boolean explicit, boolean internal) {
if (actual == null || expected == null) {
throw new IllegalStateException("Neither actual [" + actual + "] nor expected [" + expected + "] can be null");
}
if (actual.equals(expected)) {
return null;
}

View File

@ -2558,6 +2558,7 @@ class PainlessParser extends Parser {
public ArgumentsContext arguments() {
return getRuleContext(ArgumentsContext.class,0);
}
public TerminalNode COND() { return getToken(PainlessParser.COND, 0); }
public CallinvokeContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@ -2572,14 +2573,24 @@ class PainlessParser extends Parser {
public final CallinvokeContext callinvoke() throws RecognitionException {
CallinvokeContext _localctx = new CallinvokeContext(_ctx, getState());
enterRule(_localctx, 40, RULE_callinvoke);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
setState(354);
match(DOT);
setState(355);
_la = _input.LA(1);
if (_la==COND) {
{
setState(354);
match(COND);
}
}
setState(357);
match(DOT);
setState(358);
match(DOTID);
setState(356);
setState(359);
arguments();
}
}
@ -2598,6 +2609,7 @@ class PainlessParser extends Parser {
public TerminalNode DOT() { return getToken(PainlessParser.DOT, 0); }
public TerminalNode DOTID() { return getToken(PainlessParser.DOTID, 0); }
public TerminalNode DOTINTEGER() { return getToken(PainlessParser.DOTINTEGER, 0); }
public TerminalNode COND() { return getToken(PainlessParser.COND, 0); }
public FieldaccessContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@ -2616,9 +2628,18 @@ class PainlessParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(358);
setState(362);
_la = _input.LA(1);
if (_la==COND) {
{
setState(361);
match(COND);
}
}
setState(364);
match(DOT);
setState(359);
setState(365);
_la = _input.LA(1);
if ( !(_la==DOTINTEGER || _la==DOTID) ) {
_errHandler.recoverInline(this);
@ -2661,11 +2682,11 @@ class PainlessParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(361);
setState(367);
match(LBRACE);
setState(362);
setState(368);
expression(0);
setState(363);
setState(369);
match(RBRACE);
}
}
@ -2762,17 +2783,17 @@ class PainlessParser extends Parser {
int _la;
try {
int _alt;
setState(409);
switch ( getInterpreter().adaptivePredict(_input,34,_ctx) ) {
setState(415);
switch ( getInterpreter().adaptivePredict(_input,36,_ctx) ) {
case 1:
_localctx = new NewstandardarrayContext(_localctx);
enterOuterAlt(_localctx, 1);
{
setState(365);
setState(371);
match(NEW);
setState(366);
setState(372);
match(TYPE);
setState(371);
setState(377);
_errHandler.sync(this);
_alt = 1;
do {
@ -2780,11 +2801,11 @@ class PainlessParser extends Parser {
case 1:
{
{
setState(367);
setState(373);
match(LBRACE);
setState(368);
setState(374);
expression(0);
setState(369);
setState(375);
match(RBRACE);
}
}
@ -2792,31 +2813,31 @@ class PainlessParser extends Parser {
default:
throw new NoViableAltException(this);
}
setState(373);
setState(379);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,27,_ctx);
_alt = getInterpreter().adaptivePredict(_input,29,_ctx);
} while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
setState(382);
switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) {
setState(388);
switch ( getInterpreter().adaptivePredict(_input,31,_ctx) ) {
case 1:
{
setState(375);
setState(381);
postdot();
setState(379);
setState(385);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,28,_ctx);
_alt = getInterpreter().adaptivePredict(_input,30,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
setState(376);
setState(382);
postfix();
}
}
}
setState(381);
setState(387);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,28,_ctx);
_alt = getInterpreter().adaptivePredict(_input,30,_ctx);
}
}
break;
@ -2827,67 +2848,67 @@ class PainlessParser extends Parser {
_localctx = new NewinitializedarrayContext(_localctx);
enterOuterAlt(_localctx, 2);
{
setState(384);
setState(390);
match(NEW);
setState(385);
setState(391);
match(TYPE);
setState(386);
setState(392);
match(LBRACE);
setState(387);
setState(393);
match(RBRACE);
setState(388);
setState(394);
match(LBRACK);
setState(397);
setState(403);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L << SUB) | (1L << INCR) | (1L << DECR))) != 0) || ((((_la - 70)) & ~0x3f) == 0 && ((1L << (_la - 70)) & ((1L << (OCTAL - 70)) | (1L << (HEX - 70)) | (1L << (INTEGER - 70)) | (1L << (DECIMAL - 70)) | (1L << (STRING - 70)) | (1L << (REGEX - 70)) | (1L << (TRUE - 70)) | (1L << (FALSE - 70)) | (1L << (NULL - 70)) | (1L << (TYPE - 70)) | (1L << (ID - 70)))) != 0)) {
{
setState(389);
setState(395);
expression(0);
setState(394);
setState(400);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(390);
setState(396);
match(COMMA);
setState(391);
setState(397);
expression(0);
}
}
setState(396);
setState(402);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
setState(400);
setState(406);
_la = _input.LA(1);
if (_la==SEMICOLON) {
{
setState(399);
setState(405);
match(SEMICOLON);
}
}
setState(402);
setState(408);
match(RBRACK);
setState(406);
setState(412);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,33,_ctx);
_alt = getInterpreter().adaptivePredict(_input,35,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
setState(403);
setState(409);
postfix();
}
}
}
setState(408);
setState(414);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,33,_ctx);
_alt = getInterpreter().adaptivePredict(_input,35,_ctx);
}
}
break;
@ -2933,41 +2954,41 @@ class PainlessParser extends Parser {
enterRule(_localctx, 48, RULE_listinitializer);
int _la;
try {
setState(424);
switch ( getInterpreter().adaptivePredict(_input,36,_ctx) ) {
setState(430);
switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
setState(411);
match(LBRACE);
setState(412);
expression(0);
setState(417);
match(LBRACE);
setState(418);
expression(0);
setState(423);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(413);
setState(419);
match(COMMA);
setState(414);
setState(420);
expression(0);
}
}
setState(419);
setState(425);
_errHandler.sync(this);
_la = _input.LA(1);
}
setState(420);
setState(426);
match(RBRACE);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
setState(422);
setState(428);
match(LBRACE);
setState(423);
setState(429);
match(RBRACE);
}
break;
@ -3014,43 +3035,43 @@ class PainlessParser extends Parser {
enterRule(_localctx, 50, RULE_mapinitializer);
int _la;
try {
setState(440);
switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) {
setState(446);
switch ( getInterpreter().adaptivePredict(_input,40,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
setState(426);
match(LBRACE);
setState(427);
maptoken();
setState(432);
match(LBRACE);
setState(433);
maptoken();
setState(438);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(428);
setState(434);
match(COMMA);
setState(429);
setState(435);
maptoken();
}
}
setState(434);
setState(440);
_errHandler.sync(this);
_la = _input.LA(1);
}
setState(435);
setState(441);
match(RBRACE);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
setState(437);
setState(443);
match(LBRACE);
setState(438);
setState(444);
match(COLON);
setState(439);
setState(445);
match(RBRACE);
}
break;
@ -3092,11 +3113,11 @@ class PainlessParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(442);
setState(448);
expression(0);
setState(443);
setState(449);
match(COLON);
setState(444);
setState(450);
expression(0);
}
}
@ -3143,34 +3164,34 @@ class PainlessParser extends Parser {
enterOuterAlt(_localctx, 1);
{
{
setState(446);
setState(452);
match(LP);
setState(455);
setState(461);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << THIS) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L << SUB) | (1L << INCR) | (1L << DECR))) != 0) || ((((_la - 70)) & ~0x3f) == 0 && ((1L << (_la - 70)) & ((1L << (OCTAL - 70)) | (1L << (HEX - 70)) | (1L << (INTEGER - 70)) | (1L << (DECIMAL - 70)) | (1L << (STRING - 70)) | (1L << (REGEX - 70)) | (1L << (TRUE - 70)) | (1L << (FALSE - 70)) | (1L << (NULL - 70)) | (1L << (TYPE - 70)) | (1L << (ID - 70)))) != 0)) {
{
setState(447);
setState(453);
argument();
setState(452);
setState(458);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(448);
setState(454);
match(COMMA);
setState(449);
setState(455);
argument();
}
}
setState(454);
setState(460);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
setState(457);
setState(463);
match(RP);
}
}
@ -3211,26 +3232,26 @@ class PainlessParser extends Parser {
ArgumentContext _localctx = new ArgumentContext(_ctx, getState());
enterRule(_localctx, 56, RULE_argument);
try {
setState(462);
switch ( getInterpreter().adaptivePredict(_input,41,_ctx) ) {
setState(468);
switch ( getInterpreter().adaptivePredict(_input,43,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
setState(459);
setState(465);
expression(0);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
setState(460);
setState(466);
lambda();
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
setState(461);
setState(467);
funcref();
}
break;
@ -3285,58 +3306,58 @@ class PainlessParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(477);
setState(483);
switch (_input.LA(1)) {
case TYPE:
case ID:
{
setState(464);
setState(470);
lamtype();
}
break;
case LP:
{
setState(465);
setState(471);
match(LP);
setState(474);
setState(480);
_la = _input.LA(1);
if (_la==TYPE || _la==ID) {
{
setState(466);
setState(472);
lamtype();
setState(471);
setState(477);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(467);
setState(473);
match(COMMA);
setState(468);
setState(474);
lamtype();
}
}
setState(473);
setState(479);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
setState(476);
setState(482);
match(RP);
}
break;
default:
throw new NoViableAltException(this);
}
setState(479);
setState(485);
match(ARROW);
setState(482);
setState(488);
switch (_input.LA(1)) {
case LBRACK:
{
setState(480);
setState(486);
block();
}
break;
@ -3361,7 +3382,7 @@ class PainlessParser extends Parser {
case TYPE:
case ID:
{
setState(481);
setState(487);
expression(0);
}
break;
@ -3404,16 +3425,16 @@ class PainlessParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(485);
setState(491);
_la = _input.LA(1);
if (_la==TYPE) {
{
setState(484);
setState(490);
decltype();
}
}
setState(487);
setState(493);
match(ID);
}
}
@ -3492,17 +3513,17 @@ class PainlessParser extends Parser {
FuncrefContext _localctx = new FuncrefContext(_ctx, getState());
enterRule(_localctx, 62, RULE_funcref);
try {
setState(502);
switch ( getInterpreter().adaptivePredict(_input,47,_ctx) ) {
setState(508);
switch ( getInterpreter().adaptivePredict(_input,49,_ctx) ) {
case 1:
_localctx = new ClassfuncrefContext(_localctx);
enterOuterAlt(_localctx, 1);
{
setState(489);
setState(495);
match(TYPE);
setState(490);
setState(496);
match(REF);
setState(491);
setState(497);
match(ID);
}
break;
@ -3510,11 +3531,11 @@ class PainlessParser extends Parser {
_localctx = new ConstructorfuncrefContext(_localctx);
enterOuterAlt(_localctx, 2);
{
setState(492);
setState(498);
decltype();
setState(493);
setState(499);
match(REF);
setState(494);
setState(500);
match(NEW);
}
break;
@ -3522,11 +3543,11 @@ class PainlessParser extends Parser {
_localctx = new CapturingfuncrefContext(_localctx);
enterOuterAlt(_localctx, 3);
{
setState(496);
setState(502);
match(ID);
setState(497);
setState(503);
match(REF);
setState(498);
setState(504);
match(ID);
}
break;
@ -3534,11 +3555,11 @@ class PainlessParser extends Parser {
_localctx = new LocalfuncrefContext(_localctx);
enterOuterAlt(_localctx, 4);
{
setState(499);
setState(505);
match(THIS);
setState(500);
setState(506);
match(REF);
setState(501);
setState(507);
match(ID);
}
break;
@ -3606,7 +3627,7 @@ class PainlessParser extends Parser {
}
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3T\u01fb\4\2\t\2\4"+
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3T\u0201\4\2\t\2\4"+
"\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+
"\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@ -3632,173 +3653,176 @@ class PainlessParser extends Parser {
"\3\22\7\22\u0140\n\22\f\22\16\22\u0143\13\22\3\22\5\22\u0146\n\22\3\23"+
"\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23"+
"\3\23\3\23\3\23\5\23\u015a\n\23\3\24\3\24\3\24\5\24\u015f\n\24\3\25\3"+
"\25\5\25\u0163\n\25\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\30\3\30\3\30"+
"\3\30\3\31\3\31\3\31\3\31\3\31\3\31\6\31\u0176\n\31\r\31\16\31\u0177\3"+
"\31\3\31\7\31\u017c\n\31\f\31\16\31\u017f\13\31\5\31\u0181\n\31\3\31\3"+
"\31\3\31\3\31\3\31\3\31\3\31\3\31\7\31\u018b\n\31\f\31\16\31\u018e\13"+
"\31\5\31\u0190\n\31\3\31\5\31\u0193\n\31\3\31\3\31\7\31\u0197\n\31\f\31"+
"\16\31\u019a\13\31\5\31\u019c\n\31\3\32\3\32\3\32\3\32\7\32\u01a2\n\32"+
"\f\32\16\32\u01a5\13\32\3\32\3\32\3\32\3\32\5\32\u01ab\n\32\3\33\3\33"+
"\3\33\3\33\7\33\u01b1\n\33\f\33\16\33\u01b4\13\33\3\33\3\33\3\33\3\33"+
"\3\33\5\33\u01bb\n\33\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\7\35\u01c5"+
"\n\35\f\35\16\35\u01c8\13\35\5\35\u01ca\n\35\3\35\3\35\3\36\3\36\3\36"+
"\5\36\u01d1\n\36\3\37\3\37\3\37\3\37\3\37\7\37\u01d8\n\37\f\37\16\37\u01db"+
"\13\37\5\37\u01dd\n\37\3\37\5\37\u01e0\n\37\3\37\3\37\3\37\5\37\u01e5"+
"\n\37\3 \5 \u01e8\n \3 \3 \3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\5!\u01f9"+
"\n!\3!\2\3\36\"\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62"+
"\64\668:<>@\2\16\3\3\r\r\3\2\37!\3\2\"#\3\289\3\2$&\3\2\'*\3\2+.\3\2<"+
"G\3\2:;\4\2\35\36\"#\3\2HK\3\2ST\u0233\2E\3\2\2\2\4P\3\2\2\2\6U\3\2\2"+
"\2\b\u00bb\3\2\2\2\n\u00bf\3\2\2\2\f\u00c1\3\2\2\2\16\u00ca\3\2\2\2\20"+
"\u00ce\3\2\2\2\22\u00d0\3\2\2\2\24\u00d2\3\2\2\2\26\u00db\3\2\2\2\30\u00e3"+
"\3\2\2\2\32\u00e8\3\2\2\2\34\u00ef\3\2\2\2\36\u00f1\3\2\2\2 \u0133\3\2"+
"\2\2\"\u0145\3\2\2\2$\u0159\3\2\2\2&\u015e\3\2\2\2(\u0162\3\2\2\2*\u0164"+
"\3\2\2\2,\u0168\3\2\2\2.\u016b\3\2\2\2\60\u019b\3\2\2\2\62\u01aa\3\2\2"+
"\2\64\u01ba\3\2\2\2\66\u01bc\3\2\2\28\u01c0\3\2\2\2:\u01d0\3\2\2\2<\u01df"+
"\3\2\2\2>\u01e7\3\2\2\2@\u01f8\3\2\2\2BD\5\4\3\2CB\3\2\2\2DG\3\2\2\2E"+
"C\3\2\2\2EF\3\2\2\2FK\3\2\2\2GE\3\2\2\2HJ\5\b\5\2IH\3\2\2\2JM\3\2\2\2"+
"KI\3\2\2\2KL\3\2\2\2LN\3\2\2\2MK\3\2\2\2NO\7\2\2\3O\3\3\2\2\2PQ\5\26\f"+
"\2QR\7R\2\2RS\5\6\4\2ST\5\f\7\2T\5\3\2\2\2Ua\7\t\2\2VW\5\26\f\2W^\7R\2"+
"\2XY\7\f\2\2YZ\5\26\f\2Z[\7R\2\2[]\3\2\2\2\\X\3\2\2\2]`\3\2\2\2^\\\3\2"+
"\2\2^_\3\2\2\2_b\3\2\2\2`^\3\2\2\2aV\3\2\2\2ab\3\2\2\2bc\3\2\2\2cd\7\n"+
"\2\2d\7\3\2\2\2ef\7\16\2\2fg\7\t\2\2gh\5\36\20\2hi\7\n\2\2im\5\n\6\2j"+
"k\7\20\2\2kn\5\n\6\2ln\6\5\2\2mj\3\2\2\2ml\3\2\2\2n\u00bc\3\2\2\2op\7"+
"\21\2\2pq\7\t\2\2qr\5\36\20\2ru\7\n\2\2sv\5\n\6\2tv\5\16\b\2us\3\2\2\2"+
"ut\3\2\2\2v\u00bc\3\2\2\2wx\7\22\2\2xy\5\f\7\2yz\7\21\2\2z{\7\t\2\2{|"+
"\5\36\20\2|}\7\n\2\2}~\5\34\17\2~\u00bc\3\2\2\2\177\u0080\7\23\2\2\u0080"+
"\u0082\7\t\2\2\u0081\u0083\5\20\t\2\u0082\u0081\3\2\2\2\u0082\u0083\3"+
"\2\2\2\u0083\u0084\3\2\2\2\u0084\u0086\7\r\2\2\u0085\u0087\5\36\20\2\u0086"+
"\u0085\3\2\2\2\u0086\u0087\3\2\2\2\u0087\u0088\3\2\2\2\u0088\u008a\7\r"+
"\2\2\u0089\u008b\5\22\n\2\u008a\u0089\3\2\2\2\u008a\u008b\3\2\2\2\u008b"+
"\u008c\3\2\2\2\u008c\u008f\7\n\2\2\u008d\u0090\5\n\6\2\u008e\u0090\5\16"+
"\b\2\u008f\u008d\3\2\2\2\u008f\u008e\3\2\2\2\u0090\u00bc\3\2\2\2\u0091"+
"\u0092\7\23\2\2\u0092\u0093\7\t\2\2\u0093\u0094\5\26\f\2\u0094\u0095\7"+
"R\2\2\u0095\u0096\7\65\2\2\u0096\u0097\5\36\20\2\u0097\u0098\7\n\2\2\u0098"+
"\u0099\5\n\6\2\u0099\u00bc\3\2\2\2\u009a\u009b\7\23\2\2\u009b\u009c\7"+
"\t\2\2\u009c\u009d\7R\2\2\u009d\u009e\7\17\2\2\u009e\u009f\5\36\20\2\u009f"+
"\u00a0\7\n\2\2\u00a0\u00a1\5\n\6\2\u00a1\u00bc\3\2\2\2\u00a2\u00a3\5\24"+
"\13\2\u00a3\u00a4\5\34\17\2\u00a4\u00bc\3\2\2\2\u00a5\u00a6\7\24\2\2\u00a6"+
"\u00bc\5\34\17\2\u00a7\u00a8\7\25\2\2\u00a8\u00bc\5\34\17\2\u00a9\u00aa"+
"\7\26\2\2\u00aa\u00ab\5\36\20\2\u00ab\u00ac\5\34\17\2\u00ac\u00bc\3\2"+
"\2\2\u00ad\u00ae\7\30\2\2\u00ae\u00b0\5\f\7\2\u00af\u00b1\5\32\16\2\u00b0"+
"\u00af\3\2\2\2\u00b1\u00b2\3\2\2\2\u00b2\u00b0\3\2\2\2\u00b2\u00b3\3\2"+
"\2\2\u00b3\u00bc\3\2\2\2\u00b4\u00b5\7\32\2\2\u00b5\u00b6\5\36\20\2\u00b6"+
"\u00b7\5\34\17\2\u00b7\u00bc\3\2\2\2\u00b8\u00b9\5\36\20\2\u00b9\u00ba"+
"\5\34\17\2\u00ba\u00bc\3\2\2\2\u00bbe\3\2\2\2\u00bbo\3\2\2\2\u00bbw\3"+
"\2\2\2\u00bb\177\3\2\2\2\u00bb\u0091\3\2\2\2\u00bb\u009a\3\2\2\2\u00bb"+
"\u00a2\3\2\2\2\u00bb\u00a5\3\2\2\2\u00bb\u00a7\3\2\2\2\u00bb\u00a9\3\2"+
"\2\2\u00bb\u00ad\3\2\2\2\u00bb\u00b4\3\2\2\2\u00bb\u00b8\3\2\2\2\u00bc"+
"\t\3\2\2\2\u00bd\u00c0\5\f\7\2\u00be\u00c0\5\b\5\2\u00bf\u00bd\3\2\2\2"+
"\u00bf\u00be\3\2\2\2\u00c0\13\3\2\2\2\u00c1\u00c5\7\5\2\2\u00c2\u00c4"+
"\5\b\5\2\u00c3\u00c2\3\2\2\2\u00c4\u00c7\3\2\2\2\u00c5\u00c3\3\2\2\2\u00c5"+
"\u00c6\3\2\2\2\u00c6\u00c8\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c8\u00c9\7\6"+
"\2\2\u00c9\r\3\2\2\2\u00ca\u00cb\7\r\2\2\u00cb\17\3\2\2\2\u00cc\u00cf"+
"\5\24\13\2\u00cd\u00cf\5\36\20\2\u00ce\u00cc\3\2\2\2\u00ce\u00cd\3\2\2"+
"\2\u00cf\21\3\2\2\2\u00d0\u00d1\5\36\20\2\u00d1\23\3\2\2\2\u00d2\u00d3"+
"\5\26\f\2\u00d3\u00d8\5\30\r\2\u00d4\u00d5\7\f\2\2\u00d5\u00d7\5\30\r"+
"\2\u00d6\u00d4\3\2\2\2\u00d7\u00da\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9"+
"\3\2\2\2\u00d9\25\3\2\2\2\u00da\u00d8\3\2\2\2\u00db\u00e0\7Q\2\2\u00dc"+
"\u00dd\7\7\2\2\u00dd\u00df\7\b\2\2\u00de\u00dc\3\2\2\2\u00df\u00e2\3\2"+
"\2\2\u00e0\u00de\3\2\2\2\u00e0\u00e1\3\2\2\2\u00e1\27\3\2\2\2\u00e2\u00e0"+
"\3\2\2\2\u00e3\u00e6\7R\2\2\u00e4\u00e5\7<\2\2\u00e5\u00e7\5\36\20\2\u00e6"+
"\u00e4\3\2\2\2\u00e6\u00e7\3\2\2\2\u00e7\31\3\2\2\2\u00e8\u00e9\7\31\2"+
"\2\u00e9\u00ea\7\t\2\2\u00ea\u00eb\7Q\2\2\u00eb\u00ec\7R\2\2\u00ec\u00ed"+
"\7\n\2\2\u00ed\u00ee\5\f\7\2\u00ee\33\3\2\2\2\u00ef\u00f0\t\2\2\2\u00f0"+
"\35\3\2\2\2\u00f1\u00f2\b\20\1\2\u00f2\u00f3\5 \21\2\u00f3\u0123\3\2\2"+
"\2\u00f4\u00f5\f\20\2\2\u00f5\u00f6\t\3\2\2\u00f6\u0122\5\36\20\21\u00f7"+
"\u00f8\f\17\2\2\u00f8\u00f9\t\4\2\2\u00f9\u0122\5\36\20\20\u00fa\u00fb"+
"\f\16\2\2\u00fb\u00fc\t\5\2\2\u00fc\u0122\5\36\20\17\u00fd\u00fe\f\r\2"+
"\2\u00fe\u00ff\t\6\2\2\u00ff\u0122\5\36\20\16\u0100\u0101\f\f\2\2\u0101"+
"\u0102\t\7\2\2\u0102\u0122\5\36\20\r\u0103\u0104\f\n\2\2\u0104\u0105\t"+
"\b\2\2\u0105\u0122\5\36\20\13\u0106\u0107\f\t\2\2\u0107\u0108\7/\2\2\u0108"+
"\u0122\5\36\20\n\u0109\u010a\f\b\2\2\u010a\u010b\7\60\2\2\u010b\u0122"+
"\5\36\20\t\u010c\u010d\f\7\2\2\u010d\u010e\7\61\2\2\u010e\u0122\5\36\20"+
"\b\u010f\u0110\f\6\2\2\u0110\u0111\7\62\2\2\u0111\u0122\5\36\20\7\u0112"+
"\u0113\f\5\2\2\u0113\u0114\7\63\2\2\u0114\u0122\5\36\20\6\u0115\u0116"+
"\f\4\2\2\u0116\u0117\7\64\2\2\u0117\u0118\5\36\20\2\u0118\u0119\7\65\2"+
"\2\u0119\u011a\5\36\20\4\u011a\u0122\3\2\2\2\u011b\u011c\f\3\2\2\u011c"+
"\u011d\t\t\2\2\u011d\u0122\5\36\20\3\u011e\u011f\f\13\2\2\u011f\u0120"+
"\7\34\2\2\u0120\u0122\5\26\f\2\u0121\u00f4\3\2\2\2\u0121\u00f7\3\2\2\2"+
"\u0121\u00fa\3\2\2\2\u0121\u00fd\3\2\2\2\u0121\u0100\3\2\2\2\u0121\u0103"+
"\3\2\2\2\u0121\u0106\3\2\2\2\u0121\u0109\3\2\2\2\u0121\u010c\3\2\2\2\u0121"+
"\u010f\3\2\2\2\u0121\u0112\3\2\2\2\u0121\u0115\3\2\2\2\u0121\u011b\3\2"+
"\2\2\u0121\u011e\3\2\2\2\u0122\u0125\3\2\2\2\u0123\u0121\3\2\2\2\u0123"+
"\u0124\3\2\2\2\u0124\37\3\2\2\2\u0125\u0123\3\2\2\2\u0126\u0127\t\n\2"+
"\2\u0127\u0134\5\"\22\2\u0128\u0129\5\"\22\2\u0129\u012a\t\n\2\2\u012a"+
"\u0134\3\2\2\2\u012b\u0134\5\"\22\2\u012c\u012d\t\13\2\2\u012d\u0134\5"+
" \21\2\u012e\u012f\7\t\2\2\u012f\u0130\5\26\f\2\u0130\u0131\7\n\2\2\u0131"+
"\u0132\5 \21\2\u0132\u0134\3\2\2\2\u0133\u0126\3\2\2\2\u0133\u0128\3\2"+
"\2\2\u0133\u012b\3\2\2\2\u0133\u012c\3\2\2\2\u0133\u012e\3\2\2\2\u0134"+
"!\3\2\2\2\u0135\u0139\5$\23\2\u0136\u0138\5&\24\2\u0137\u0136\3\2\2\2"+
"\u0138\u013b\3\2\2\2\u0139\u0137\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u0146"+
"\3\2\2\2\u013b\u0139\3\2\2\2\u013c\u013d\5\26\f\2\u013d\u0141\5(\25\2"+
"\u013e\u0140\5&\24\2\u013f\u013e\3\2\2\2\u0140\u0143\3\2\2\2\u0141\u013f"+
"\3\2\2\2\u0141\u0142\3\2\2\2\u0142\u0146\3\2\2\2\u0143\u0141\3\2\2\2\u0144"+
"\u0146\5\60\31\2\u0145\u0135\3\2\2\2\u0145\u013c\3\2\2\2\u0145\u0144\3"+
"\2\2\2\u0146#\3\2\2\2\u0147\u0148\7\t\2\2\u0148\u0149\5\36\20\2\u0149"+
"\u014a\7\n\2\2\u014a\u015a\3\2\2\2\u014b\u015a\t\f\2\2\u014c\u015a\7N"+
"\2\2\u014d\u015a\7O\2\2\u014e\u015a\7P\2\2\u014f\u015a\7L\2\2\u0150\u015a"+
"\7M\2\2\u0151\u015a\5\62\32\2\u0152\u015a\5\64\33\2\u0153\u015a\7R\2\2"+
"\u0154\u0155\7R\2\2\u0155\u015a\58\35\2\u0156\u0157\7\27\2\2\u0157\u0158"+
"\7Q\2\2\u0158\u015a\58\35\2\u0159\u0147\3\2\2\2\u0159\u014b\3\2\2\2\u0159"+
"\u014c\3\2\2\2\u0159\u014d\3\2\2\2\u0159\u014e\3\2\2\2\u0159\u014f\3\2"+
"\2\2\u0159\u0150\3\2\2\2\u0159\u0151\3\2\2\2\u0159\u0152\3\2\2\2\u0159"+
"\u0153\3\2\2\2\u0159\u0154\3\2\2\2\u0159\u0156\3\2\2\2\u015a%\3\2\2\2"+
"\u015b\u015f\5*\26\2\u015c\u015f\5,\27\2\u015d\u015f\5.\30\2\u015e\u015b"+
"\3\2\2\2\u015e\u015c\3\2\2\2\u015e\u015d\3\2\2\2\u015f\'\3\2\2\2\u0160"+
"\u0163\5*\26\2\u0161\u0163\5,\27\2\u0162\u0160\3\2\2\2\u0162\u0161\3\2"+
"\2\2\u0163)\3\2\2\2\u0164\u0165\7\13\2\2\u0165\u0166\7T\2\2\u0166\u0167"+
"\58\35\2\u0167+\3\2\2\2\u0168\u0169\7\13\2\2\u0169\u016a\t\r\2\2\u016a"+
"-\3\2\2\2\u016b\u016c\7\7\2\2\u016c\u016d\5\36\20\2\u016d\u016e\7\b\2"+
"\2\u016e/\3\2\2\2\u016f\u0170\7\27\2\2\u0170\u0175\7Q\2\2\u0171\u0172"+
"\7\7\2\2\u0172\u0173\5\36\20\2\u0173\u0174\7\b\2\2\u0174\u0176\3\2\2\2"+
"\u0175\u0171\3\2\2\2\u0176\u0177\3\2\2\2\u0177\u0175\3\2\2\2\u0177\u0178"+
"\3\2\2\2\u0178\u0180\3\2\2\2\u0179\u017d\5(\25\2\u017a\u017c\5&\24\2\u017b"+
"\u017a\3\2\2\2\u017c\u017f\3\2\2\2\u017d\u017b\3\2\2\2\u017d\u017e\3\2"+
"\2\2\u017e\u0181\3\2\2\2\u017f\u017d\3\2\2\2\u0180\u0179\3\2\2\2\u0180"+
"\u0181\3\2\2\2\u0181\u019c\3\2\2\2\u0182\u0183\7\27\2\2\u0183\u0184\7"+
"Q\2\2\u0184\u0185\7\7\2\2\u0185\u0186\7\b\2\2\u0186\u018f\7\5\2\2\u0187"+
"\u018c\5\36\20\2\u0188\u0189\7\f\2\2\u0189\u018b\5\36\20\2\u018a\u0188"+
"\3\2\2\2\u018b\u018e\3\2\2\2\u018c\u018a\3\2\2\2\u018c\u018d\3\2\2\2\u018d"+
"\u0190\3\2\2\2\u018e\u018c\3\2\2\2\u018f\u0187\3\2\2\2\u018f\u0190\3\2"+
"\2\2\u0190\u0192\3\2\2\2\u0191\u0193\7\r\2\2\u0192\u0191\3\2\2\2\u0192"+
"\u0193\3\2\2\2\u0193\u0194\3\2\2\2\u0194\u0198\7\6\2\2\u0195\u0197\5&"+
"\24\2\u0196\u0195\3\2\2\2\u0197\u019a\3\2\2\2\u0198\u0196\3\2\2\2\u0198"+
"\u0199\3\2\2\2\u0199\u019c\3\2\2\2\u019a\u0198\3\2\2\2\u019b\u016f\3\2"+
"\2\2\u019b\u0182\3\2\2\2\u019c\61\3\2\2\2\u019d\u019e\7\7\2\2\u019e\u01a3"+
"\5\36\20\2\u019f\u01a0\7\f\2\2\u01a0\u01a2\5\36\20\2\u01a1\u019f\3\2\2"+
"\2\u01a2\u01a5\3\2\2\2\u01a3\u01a1\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a6"+
"\3\2\2\2\u01a5\u01a3\3\2\2\2\u01a6\u01a7\7\b\2\2\u01a7\u01ab\3\2\2\2\u01a8"+
"\u01a9\7\7\2\2\u01a9\u01ab\7\b\2\2\u01aa\u019d\3\2\2\2\u01aa\u01a8\3\2"+
"\2\2\u01ab\63\3\2\2\2\u01ac\u01ad\7\7\2\2\u01ad\u01b2\5\66\34\2\u01ae"+
"\u01af\7\f\2\2\u01af\u01b1\5\66\34\2\u01b0\u01ae\3\2\2\2\u01b1\u01b4\3"+
"\2\2\2\u01b2\u01b0\3\2\2\2\u01b2\u01b3\3\2\2\2\u01b3\u01b5\3\2\2\2\u01b4"+
"\u01b2\3\2\2\2\u01b5\u01b6\7\b\2\2\u01b6\u01bb\3\2\2\2\u01b7\u01b8\7\7"+
"\2\2\u01b8\u01b9\7\65\2\2\u01b9\u01bb\7\b\2\2\u01ba\u01ac\3\2\2\2\u01ba"+
"\u01b7\3\2\2\2\u01bb\65\3\2\2\2\u01bc\u01bd\5\36\20\2\u01bd\u01be\7\65"+
"\2\2\u01be\u01bf\5\36\20\2\u01bf\67\3\2\2\2\u01c0\u01c9\7\t\2\2\u01c1"+
"\u01c6\5:\36\2\u01c2\u01c3\7\f\2\2\u01c3\u01c5\5:\36\2\u01c4\u01c2\3\2"+
"\2\2\u01c5\u01c8\3\2\2\2\u01c6\u01c4\3\2\2\2\u01c6\u01c7\3\2\2\2\u01c7"+
"\u01ca\3\2\2\2\u01c8\u01c6\3\2\2\2\u01c9\u01c1\3\2\2\2\u01c9\u01ca\3\2"+
"\2\2\u01ca\u01cb\3\2\2\2\u01cb\u01cc\7\n\2\2\u01cc9\3\2\2\2\u01cd\u01d1"+
"\5\36\20\2\u01ce\u01d1\5<\37\2\u01cf\u01d1\5@!\2\u01d0\u01cd\3\2\2\2\u01d0"+
"\u01ce\3\2\2\2\u01d0\u01cf\3\2\2\2\u01d1;\3\2\2\2\u01d2\u01e0\5> \2\u01d3"+
"\u01dc\7\t\2\2\u01d4\u01d9\5> \2\u01d5\u01d6\7\f\2\2\u01d6\u01d8\5> \2"+
"\u01d7\u01d5\3\2\2\2\u01d8\u01db\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da"+
"\3\2\2\2\u01da\u01dd\3\2\2\2\u01db\u01d9\3\2\2\2\u01dc\u01d4\3\2\2\2\u01dc"+
"\u01dd\3\2\2\2\u01dd\u01de\3\2\2\2\u01de\u01e0\7\n\2\2\u01df\u01d2\3\2"+
"\2\2\u01df\u01d3\3\2\2\2\u01e0\u01e1\3\2\2\2\u01e1\u01e4\7\67\2\2\u01e2"+
"\u01e5\5\f\7\2\u01e3\u01e5\5\36\20\2\u01e4\u01e2\3\2\2\2\u01e4\u01e3\3"+
"\2\2\2\u01e5=\3\2\2\2\u01e6\u01e8\5\26\f\2\u01e7\u01e6\3\2\2\2\u01e7\u01e8"+
"\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9\u01ea\7R\2\2\u01ea?\3\2\2\2\u01eb\u01ec"+
"\7Q\2\2\u01ec\u01ed\7\66\2\2\u01ed\u01f9\7R\2\2\u01ee\u01ef\5\26\f\2\u01ef"+
"\u01f0\7\66\2\2\u01f0\u01f1\7\27\2\2\u01f1\u01f9\3\2\2\2\u01f2\u01f3\7"+
"R\2\2\u01f3\u01f4\7\66\2\2\u01f4\u01f9\7R\2\2\u01f5\u01f6\7\33\2\2\u01f6"+
"\u01f7\7\66\2\2\u01f7\u01f9\7R\2\2\u01f8\u01eb\3\2\2\2\u01f8\u01ee\3\2"+
"\2\2\u01f8\u01f2\3\2\2\2\u01f8\u01f5\3\2\2\2\u01f9A\3\2\2\2\62EK^amu\u0082"+
"\u0086\u008a\u008f\u00b2\u00bb\u00bf\u00c5\u00ce\u00d8\u00e0\u00e6\u0121"+
"\u0123\u0133\u0139\u0141\u0145\u0159\u015e\u0162\u0177\u017d\u0180\u018c"+
"\u018f\u0192\u0198\u019b\u01a3\u01aa\u01b2\u01ba\u01c6\u01c9\u01d0\u01d9"+
"\u01dc\u01df\u01e4\u01e7\u01f8";
"\25\5\25\u0163\n\25\3\26\5\26\u0166\n\26\3\26\3\26\3\26\3\26\3\27\5\27"+
"\u016d\n\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31"+
"\3\31\6\31\u017c\n\31\r\31\16\31\u017d\3\31\3\31\7\31\u0182\n\31\f\31"+
"\16\31\u0185\13\31\5\31\u0187\n\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31"+
"\3\31\7\31\u0191\n\31\f\31\16\31\u0194\13\31\5\31\u0196\n\31\3\31\5\31"+
"\u0199\n\31\3\31\3\31\7\31\u019d\n\31\f\31\16\31\u01a0\13\31\5\31\u01a2"+
"\n\31\3\32\3\32\3\32\3\32\7\32\u01a8\n\32\f\32\16\32\u01ab\13\32\3\32"+
"\3\32\3\32\3\32\5\32\u01b1\n\32\3\33\3\33\3\33\3\33\7\33\u01b7\n\33\f"+
"\33\16\33\u01ba\13\33\3\33\3\33\3\33\3\33\3\33\5\33\u01c1\n\33\3\34\3"+
"\34\3\34\3\34\3\35\3\35\3\35\3\35\7\35\u01cb\n\35\f\35\16\35\u01ce\13"+
"\35\5\35\u01d0\n\35\3\35\3\35\3\36\3\36\3\36\5\36\u01d7\n\36\3\37\3\37"+
"\3\37\3\37\3\37\7\37\u01de\n\37\f\37\16\37\u01e1\13\37\5\37\u01e3\n\37"+
"\3\37\5\37\u01e6\n\37\3\37\3\37\3\37\5\37\u01eb\n\37\3 \5 \u01ee\n \3"+
" \3 \3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\5!\u01ff\n!\3!\2\3\36\"\2"+
"\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<>@\2\16\3"+
"\3\r\r\3\2\37!\3\2\"#\3\289\3\2$&\3\2\'*\3\2+.\3\2<G\3\2:;\4\2\35\36\""+
"#\3\2HK\3\2ST\u023b\2E\3\2\2\2\4P\3\2\2\2\6U\3\2\2\2\b\u00bb\3\2\2\2\n"+
"\u00bf\3\2\2\2\f\u00c1\3\2\2\2\16\u00ca\3\2\2\2\20\u00ce\3\2\2\2\22\u00d0"+
"\3\2\2\2\24\u00d2\3\2\2\2\26\u00db\3\2\2\2\30\u00e3\3\2\2\2\32\u00e8\3"+
"\2\2\2\34\u00ef\3\2\2\2\36\u00f1\3\2\2\2 \u0133\3\2\2\2\"\u0145\3\2\2"+
"\2$\u0159\3\2\2\2&\u015e\3\2\2\2(\u0162\3\2\2\2*\u0165\3\2\2\2,\u016c"+
"\3\2\2\2.\u0171\3\2\2\2\60\u01a1\3\2\2\2\62\u01b0\3\2\2\2\64\u01c0\3\2"+
"\2\2\66\u01c2\3\2\2\28\u01c6\3\2\2\2:\u01d6\3\2\2\2<\u01e5\3\2\2\2>\u01ed"+
"\3\2\2\2@\u01fe\3\2\2\2BD\5\4\3\2CB\3\2\2\2DG\3\2\2\2EC\3\2\2\2EF\3\2"+
"\2\2FK\3\2\2\2GE\3\2\2\2HJ\5\b\5\2IH\3\2\2\2JM\3\2\2\2KI\3\2\2\2KL\3\2"+
"\2\2LN\3\2\2\2MK\3\2\2\2NO\7\2\2\3O\3\3\2\2\2PQ\5\26\f\2QR\7R\2\2RS\5"+
"\6\4\2ST\5\f\7\2T\5\3\2\2\2Ua\7\t\2\2VW\5\26\f\2W^\7R\2\2XY\7\f\2\2YZ"+
"\5\26\f\2Z[\7R\2\2[]\3\2\2\2\\X\3\2\2\2]`\3\2\2\2^\\\3\2\2\2^_\3\2\2\2"+
"_b\3\2\2\2`^\3\2\2\2aV\3\2\2\2ab\3\2\2\2bc\3\2\2\2cd\7\n\2\2d\7\3\2\2"+
"\2ef\7\16\2\2fg\7\t\2\2gh\5\36\20\2hi\7\n\2\2im\5\n\6\2jk\7\20\2\2kn\5"+
"\n\6\2ln\6\5\2\2mj\3\2\2\2ml\3\2\2\2n\u00bc\3\2\2\2op\7\21\2\2pq\7\t\2"+
"\2qr\5\36\20\2ru\7\n\2\2sv\5\n\6\2tv\5\16\b\2us\3\2\2\2ut\3\2\2\2v\u00bc"+
"\3\2\2\2wx\7\22\2\2xy\5\f\7\2yz\7\21\2\2z{\7\t\2\2{|\5\36\20\2|}\7\n\2"+
"\2}~\5\34\17\2~\u00bc\3\2\2\2\177\u0080\7\23\2\2\u0080\u0082\7\t\2\2\u0081"+
"\u0083\5\20\t\2\u0082\u0081\3\2\2\2\u0082\u0083\3\2\2\2\u0083\u0084\3"+
"\2\2\2\u0084\u0086\7\r\2\2\u0085\u0087\5\36\20\2\u0086\u0085\3\2\2\2\u0086"+
"\u0087\3\2\2\2\u0087\u0088\3\2\2\2\u0088\u008a\7\r\2\2\u0089\u008b\5\22"+
"\n\2\u008a\u0089\3\2\2\2\u008a\u008b\3\2\2\2\u008b\u008c\3\2\2\2\u008c"+
"\u008f\7\n\2\2\u008d\u0090\5\n\6\2\u008e\u0090\5\16\b\2\u008f\u008d\3"+
"\2\2\2\u008f\u008e\3\2\2\2\u0090\u00bc\3\2\2\2\u0091\u0092\7\23\2\2\u0092"+
"\u0093\7\t\2\2\u0093\u0094\5\26\f\2\u0094\u0095\7R\2\2\u0095\u0096\7\65"+
"\2\2\u0096\u0097\5\36\20\2\u0097\u0098\7\n\2\2\u0098\u0099\5\n\6\2\u0099"+
"\u00bc\3\2\2\2\u009a\u009b\7\23\2\2\u009b\u009c\7\t\2\2\u009c\u009d\7"+
"R\2\2\u009d\u009e\7\17\2\2\u009e\u009f\5\36\20\2\u009f\u00a0\7\n\2\2\u00a0"+
"\u00a1\5\n\6\2\u00a1\u00bc\3\2\2\2\u00a2\u00a3\5\24\13\2\u00a3\u00a4\5"+
"\34\17\2\u00a4\u00bc\3\2\2\2\u00a5\u00a6\7\24\2\2\u00a6\u00bc\5\34\17"+
"\2\u00a7\u00a8\7\25\2\2\u00a8\u00bc\5\34\17\2\u00a9\u00aa\7\26\2\2\u00aa"+
"\u00ab\5\36\20\2\u00ab\u00ac\5\34\17\2\u00ac\u00bc\3\2\2\2\u00ad\u00ae"+
"\7\30\2\2\u00ae\u00b0\5\f\7\2\u00af\u00b1\5\32\16\2\u00b0\u00af\3\2\2"+
"\2\u00b1\u00b2\3\2\2\2\u00b2\u00b0\3\2\2\2\u00b2\u00b3\3\2\2\2\u00b3\u00bc"+
"\3\2\2\2\u00b4\u00b5\7\32\2\2\u00b5\u00b6\5\36\20\2\u00b6\u00b7\5\34\17"+
"\2\u00b7\u00bc\3\2\2\2\u00b8\u00b9\5\36\20\2\u00b9\u00ba\5\34\17\2\u00ba"+
"\u00bc\3\2\2\2\u00bbe\3\2\2\2\u00bbo\3\2\2\2\u00bbw\3\2\2\2\u00bb\177"+
"\3\2\2\2\u00bb\u0091\3\2\2\2\u00bb\u009a\3\2\2\2\u00bb\u00a2\3\2\2\2\u00bb"+
"\u00a5\3\2\2\2\u00bb\u00a7\3\2\2\2\u00bb\u00a9\3\2\2\2\u00bb\u00ad\3\2"+
"\2\2\u00bb\u00b4\3\2\2\2\u00bb\u00b8\3\2\2\2\u00bc\t\3\2\2\2\u00bd\u00c0"+
"\5\f\7\2\u00be\u00c0\5\b\5\2\u00bf\u00bd\3\2\2\2\u00bf\u00be\3\2\2\2\u00c0"+
"\13\3\2\2\2\u00c1\u00c5\7\5\2\2\u00c2\u00c4\5\b\5\2\u00c3\u00c2\3\2\2"+
"\2\u00c4\u00c7\3\2\2\2\u00c5\u00c3\3\2\2\2\u00c5\u00c6\3\2\2\2\u00c6\u00c8"+
"\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c8\u00c9\7\6\2\2\u00c9\r\3\2\2\2\u00ca"+
"\u00cb\7\r\2\2\u00cb\17\3\2\2\2\u00cc\u00cf\5\24\13\2\u00cd\u00cf\5\36"+
"\20\2\u00ce\u00cc\3\2\2\2\u00ce\u00cd\3\2\2\2\u00cf\21\3\2\2\2\u00d0\u00d1"+
"\5\36\20\2\u00d1\23\3\2\2\2\u00d2\u00d3\5\26\f\2\u00d3\u00d8\5\30\r\2"+
"\u00d4\u00d5\7\f\2\2\u00d5\u00d7\5\30\r\2\u00d6\u00d4\3\2\2\2\u00d7\u00da"+
"\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9\25\3\2\2\2\u00da"+
"\u00d8\3\2\2\2\u00db\u00e0\7Q\2\2\u00dc\u00dd\7\7\2\2\u00dd\u00df\7\b"+
"\2\2\u00de\u00dc\3\2\2\2\u00df\u00e2\3\2\2\2\u00e0\u00de\3\2\2\2\u00e0"+
"\u00e1\3\2\2\2\u00e1\27\3\2\2\2\u00e2\u00e0\3\2\2\2\u00e3\u00e6\7R\2\2"+
"\u00e4\u00e5\7<\2\2\u00e5\u00e7\5\36\20\2\u00e6\u00e4\3\2\2\2\u00e6\u00e7"+
"\3\2\2\2\u00e7\31\3\2\2\2\u00e8\u00e9\7\31\2\2\u00e9\u00ea\7\t\2\2\u00ea"+
"\u00eb\7Q\2\2\u00eb\u00ec\7R\2\2\u00ec\u00ed\7\n\2\2\u00ed\u00ee\5\f\7"+
"\2\u00ee\33\3\2\2\2\u00ef\u00f0\t\2\2\2\u00f0\35\3\2\2\2\u00f1\u00f2\b"+
"\20\1\2\u00f2\u00f3\5 \21\2\u00f3\u0123\3\2\2\2\u00f4\u00f5\f\20\2\2\u00f5"+
"\u00f6\t\3\2\2\u00f6\u0122\5\36\20\21\u00f7\u00f8\f\17\2\2\u00f8\u00f9"+
"\t\4\2\2\u00f9\u0122\5\36\20\20\u00fa\u00fb\f\16\2\2\u00fb\u00fc\t\5\2"+
"\2\u00fc\u0122\5\36\20\17\u00fd\u00fe\f\r\2\2\u00fe\u00ff\t\6\2\2\u00ff"+
"\u0122\5\36\20\16\u0100\u0101\f\f\2\2\u0101\u0102\t\7\2\2\u0102\u0122"+
"\5\36\20\r\u0103\u0104\f\n\2\2\u0104\u0105\t\b\2\2\u0105\u0122\5\36\20"+
"\13\u0106\u0107\f\t\2\2\u0107\u0108\7/\2\2\u0108\u0122\5\36\20\n\u0109"+
"\u010a\f\b\2\2\u010a\u010b\7\60\2\2\u010b\u0122\5\36\20\t\u010c\u010d"+
"\f\7\2\2\u010d\u010e\7\61\2\2\u010e\u0122\5\36\20\b\u010f\u0110\f\6\2"+
"\2\u0110\u0111\7\62\2\2\u0111\u0122\5\36\20\7\u0112\u0113\f\5\2\2\u0113"+
"\u0114\7\63\2\2\u0114\u0122\5\36\20\6\u0115\u0116\f\4\2\2\u0116\u0117"+
"\7\64\2\2\u0117\u0118\5\36\20\2\u0118\u0119\7\65\2\2\u0119\u011a\5\36"+
"\20\4\u011a\u0122\3\2\2\2\u011b\u011c\f\3\2\2\u011c\u011d\t\t\2\2\u011d"+
"\u0122\5\36\20\3\u011e\u011f\f\13\2\2\u011f\u0120\7\34\2\2\u0120\u0122"+
"\5\26\f\2\u0121\u00f4\3\2\2\2\u0121\u00f7\3\2\2\2\u0121\u00fa\3\2\2\2"+
"\u0121\u00fd\3\2\2\2\u0121\u0100\3\2\2\2\u0121\u0103\3\2\2\2\u0121\u0106"+
"\3\2\2\2\u0121\u0109\3\2\2\2\u0121\u010c\3\2\2\2\u0121\u010f\3\2\2\2\u0121"+
"\u0112\3\2\2\2\u0121\u0115\3\2\2\2\u0121\u011b\3\2\2\2\u0121\u011e\3\2"+
"\2\2\u0122\u0125\3\2\2\2\u0123\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124"+
"\37\3\2\2\2\u0125\u0123\3\2\2\2\u0126\u0127\t\n\2\2\u0127\u0134\5\"\22"+
"\2\u0128\u0129\5\"\22\2\u0129\u012a\t\n\2\2\u012a\u0134\3\2\2\2\u012b"+
"\u0134\5\"\22\2\u012c\u012d\t\13\2\2\u012d\u0134\5 \21\2\u012e\u012f\7"+
"\t\2\2\u012f\u0130\5\26\f\2\u0130\u0131\7\n\2\2\u0131\u0132\5 \21\2\u0132"+
"\u0134\3\2\2\2\u0133\u0126\3\2\2\2\u0133\u0128\3\2\2\2\u0133\u012b\3\2"+
"\2\2\u0133\u012c\3\2\2\2\u0133\u012e\3\2\2\2\u0134!\3\2\2\2\u0135\u0139"+
"\5$\23\2\u0136\u0138\5&\24\2\u0137\u0136\3\2\2\2\u0138\u013b\3\2\2\2\u0139"+
"\u0137\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u0146\3\2\2\2\u013b\u0139\3\2"+
"\2\2\u013c\u013d\5\26\f\2\u013d\u0141\5(\25\2\u013e\u0140\5&\24\2\u013f"+
"\u013e\3\2\2\2\u0140\u0143\3\2\2\2\u0141\u013f\3\2\2\2\u0141\u0142\3\2"+
"\2\2\u0142\u0146\3\2\2\2\u0143\u0141\3\2\2\2\u0144\u0146\5\60\31\2\u0145"+
"\u0135\3\2\2\2\u0145\u013c\3\2\2\2\u0145\u0144\3\2\2\2\u0146#\3\2\2\2"+
"\u0147\u0148\7\t\2\2\u0148\u0149\5\36\20\2\u0149\u014a\7\n\2\2\u014a\u015a"+
"\3\2\2\2\u014b\u015a\t\f\2\2\u014c\u015a\7N\2\2\u014d\u015a\7O\2\2\u014e"+
"\u015a\7P\2\2\u014f\u015a\7L\2\2\u0150\u015a\7M\2\2\u0151\u015a\5\62\32"+
"\2\u0152\u015a\5\64\33\2\u0153\u015a\7R\2\2\u0154\u0155\7R\2\2\u0155\u015a"+
"\58\35\2\u0156\u0157\7\27\2\2\u0157\u0158\7Q\2\2\u0158\u015a\58\35\2\u0159"+
"\u0147\3\2\2\2\u0159\u014b\3\2\2\2\u0159\u014c\3\2\2\2\u0159\u014d\3\2"+
"\2\2\u0159\u014e\3\2\2\2\u0159\u014f\3\2\2\2\u0159\u0150\3\2\2\2\u0159"+
"\u0151\3\2\2\2\u0159\u0152\3\2\2\2\u0159\u0153\3\2\2\2\u0159\u0154\3\2"+
"\2\2\u0159\u0156\3\2\2\2\u015a%\3\2\2\2\u015b\u015f\5*\26\2\u015c\u015f"+
"\5,\27\2\u015d\u015f\5.\30\2\u015e\u015b\3\2\2\2\u015e\u015c\3\2\2\2\u015e"+
"\u015d\3\2\2\2\u015f\'\3\2\2\2\u0160\u0163\5*\26\2\u0161\u0163\5,\27\2"+
"\u0162\u0160\3\2\2\2\u0162\u0161\3\2\2\2\u0163)\3\2\2\2\u0164\u0166\7"+
"\64\2\2\u0165\u0164\3\2\2\2\u0165\u0166\3\2\2\2\u0166\u0167\3\2\2\2\u0167"+
"\u0168\7\13\2\2\u0168\u0169\7T\2\2\u0169\u016a\58\35\2\u016a+\3\2\2\2"+
"\u016b\u016d\7\64\2\2\u016c\u016b\3\2\2\2\u016c\u016d\3\2\2\2\u016d\u016e"+
"\3\2\2\2\u016e\u016f\7\13\2\2\u016f\u0170\t\r\2\2\u0170-\3\2\2\2\u0171"+
"\u0172\7\7\2\2\u0172\u0173\5\36\20\2\u0173\u0174\7\b\2\2\u0174/\3\2\2"+
"\2\u0175\u0176\7\27\2\2\u0176\u017b\7Q\2\2\u0177\u0178\7\7\2\2\u0178\u0179"+
"\5\36\20\2\u0179\u017a\7\b\2\2\u017a\u017c\3\2\2\2\u017b\u0177\3\2\2\2"+
"\u017c\u017d\3\2\2\2\u017d\u017b\3\2\2\2\u017d\u017e\3\2\2\2\u017e\u0186"+
"\3\2\2\2\u017f\u0183\5(\25\2\u0180\u0182\5&\24\2\u0181\u0180\3\2\2\2\u0182"+
"\u0185\3\2\2\2\u0183\u0181\3\2\2\2\u0183\u0184\3\2\2\2\u0184\u0187\3\2"+
"\2\2\u0185\u0183\3\2\2\2\u0186\u017f\3\2\2\2\u0186\u0187\3\2\2\2\u0187"+
"\u01a2\3\2\2\2\u0188\u0189\7\27\2\2\u0189\u018a\7Q\2\2\u018a\u018b\7\7"+
"\2\2\u018b\u018c\7\b\2\2\u018c\u0195\7\5\2\2\u018d\u0192\5\36\20\2\u018e"+
"\u018f\7\f\2\2\u018f\u0191\5\36\20\2\u0190\u018e\3\2\2\2\u0191\u0194\3"+
"\2\2\2\u0192\u0190\3\2\2\2\u0192\u0193\3\2\2\2\u0193\u0196\3\2\2\2\u0194"+
"\u0192\3\2\2\2\u0195\u018d\3\2\2\2\u0195\u0196\3\2\2\2\u0196\u0198\3\2"+
"\2\2\u0197\u0199\7\r\2\2\u0198\u0197\3\2\2\2\u0198\u0199\3\2\2\2\u0199"+
"\u019a\3\2\2\2\u019a\u019e\7\6\2\2\u019b\u019d\5&\24\2\u019c\u019b\3\2"+
"\2\2\u019d\u01a0\3\2\2\2\u019e\u019c\3\2\2\2\u019e\u019f\3\2\2\2\u019f"+
"\u01a2\3\2\2\2\u01a0\u019e\3\2\2\2\u01a1\u0175\3\2\2\2\u01a1\u0188\3\2"+
"\2\2\u01a2\61\3\2\2\2\u01a3\u01a4\7\7\2\2\u01a4\u01a9\5\36\20\2\u01a5"+
"\u01a6\7\f\2\2\u01a6\u01a8\5\36\20\2\u01a7\u01a5\3\2\2\2\u01a8\u01ab\3"+
"\2\2\2\u01a9\u01a7\3\2\2\2\u01a9\u01aa\3\2\2\2\u01aa\u01ac\3\2\2\2\u01ab"+
"\u01a9\3\2\2\2\u01ac\u01ad\7\b\2\2\u01ad\u01b1\3\2\2\2\u01ae\u01af\7\7"+
"\2\2\u01af\u01b1\7\b\2\2\u01b0\u01a3\3\2\2\2\u01b0\u01ae\3\2\2\2\u01b1"+
"\63\3\2\2\2\u01b2\u01b3\7\7\2\2\u01b3\u01b8\5\66\34\2\u01b4\u01b5\7\f"+
"\2\2\u01b5\u01b7\5\66\34\2\u01b6\u01b4\3\2\2\2\u01b7\u01ba\3\2\2\2\u01b8"+
"\u01b6\3\2\2\2\u01b8\u01b9\3\2\2\2\u01b9\u01bb\3\2\2\2\u01ba\u01b8\3\2"+
"\2\2\u01bb\u01bc\7\b\2\2\u01bc\u01c1\3\2\2\2\u01bd\u01be\7\7\2\2\u01be"+
"\u01bf\7\65\2\2\u01bf\u01c1\7\b\2\2\u01c0\u01b2\3\2\2\2\u01c0\u01bd\3"+
"\2\2\2\u01c1\65\3\2\2\2\u01c2\u01c3\5\36\20\2\u01c3\u01c4\7\65\2\2\u01c4"+
"\u01c5\5\36\20\2\u01c5\67\3\2\2\2\u01c6\u01cf\7\t\2\2\u01c7\u01cc\5:\36"+
"\2\u01c8\u01c9\7\f\2\2\u01c9\u01cb\5:\36\2\u01ca\u01c8\3\2\2\2\u01cb\u01ce"+
"\3\2\2\2\u01cc\u01ca\3\2\2\2\u01cc\u01cd\3\2\2\2\u01cd\u01d0\3\2\2\2\u01ce"+
"\u01cc\3\2\2\2\u01cf\u01c7\3\2\2\2\u01cf\u01d0\3\2\2\2\u01d0\u01d1\3\2"+
"\2\2\u01d1\u01d2\7\n\2\2\u01d29\3\2\2\2\u01d3\u01d7\5\36\20\2\u01d4\u01d7"+
"\5<\37\2\u01d5\u01d7\5@!\2\u01d6\u01d3\3\2\2\2\u01d6\u01d4\3\2\2\2\u01d6"+
"\u01d5\3\2\2\2\u01d7;\3\2\2\2\u01d8\u01e6\5> \2\u01d9\u01e2\7\t\2\2\u01da"+
"\u01df\5> \2\u01db\u01dc\7\f\2\2\u01dc\u01de\5> \2\u01dd\u01db\3\2\2\2"+
"\u01de\u01e1\3\2\2\2\u01df\u01dd\3\2\2\2\u01df\u01e0\3\2\2\2\u01e0\u01e3"+
"\3\2\2\2\u01e1\u01df\3\2\2\2\u01e2\u01da\3\2\2\2\u01e2\u01e3\3\2\2\2\u01e3"+
"\u01e4\3\2\2\2\u01e4\u01e6\7\n\2\2\u01e5\u01d8\3\2\2\2\u01e5\u01d9\3\2"+
"\2\2\u01e6\u01e7\3\2\2\2\u01e7\u01ea\7\67\2\2\u01e8\u01eb\5\f\7\2\u01e9"+
"\u01eb\5\36\20\2\u01ea\u01e8\3\2\2\2\u01ea\u01e9\3\2\2\2\u01eb=\3\2\2"+
"\2\u01ec\u01ee\5\26\f\2\u01ed\u01ec\3\2\2\2\u01ed\u01ee\3\2\2\2\u01ee"+
"\u01ef\3\2\2\2\u01ef\u01f0\7R\2\2\u01f0?\3\2\2\2\u01f1\u01f2\7Q\2\2\u01f2"+
"\u01f3\7\66\2\2\u01f3\u01ff\7R\2\2\u01f4\u01f5\5\26\f\2\u01f5\u01f6\7"+
"\66\2\2\u01f6\u01f7\7\27\2\2\u01f7\u01ff\3\2\2\2\u01f8\u01f9\7R\2\2\u01f9"+
"\u01fa\7\66\2\2\u01fa\u01ff\7R\2\2\u01fb\u01fc\7\33\2\2\u01fc\u01fd\7"+
"\66\2\2\u01fd\u01ff\7R\2\2\u01fe\u01f1\3\2\2\2\u01fe\u01f4\3\2\2\2\u01fe"+
"\u01f8\3\2\2\2\u01fe\u01fb\3\2\2\2\u01ffA\3\2\2\2\64EK^amu\u0082\u0086"+
"\u008a\u008f\u00b2\u00bb\u00bf\u00c5\u00ce\u00d8\u00e0\u00e6\u0121\u0123"+
"\u0133\u0139\u0141\u0145\u0159\u015e\u0162\u0165\u016c\u017d\u0183\u0186"+
"\u0192\u0195\u0198\u019e\u01a1\u01a9\u01b0\u01b8\u01c0\u01cc\u01cf\u01d6"+
"\u01df\u01e2\u01e5\u01ea\u01ed\u01fe";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

View File

@ -898,7 +898,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
String name = ctx.DOTID().getText();
List<AExpression> arguments = collectArguments(ctx.arguments());
return new PCallInvoke(location(ctx), prefix, name, arguments);
return new PCallInvoke(location(ctx), prefix, name, ctx.COND() != null, arguments);
}
@Override
@ -917,7 +917,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
throw location(ctx).createError(new IllegalStateException("Illegal tree structure."));
}
return new PField(location(ctx), prefix, value);
return new PField(location(ctx), prefix, ctx.COND() != null, value);
}
@Override

View File

@ -34,7 +34,7 @@ import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Opcodes;
/**
* Respresents a conditional expression.
* Represents a conditional expression.
*/
public final class EConditional extends AExpression {

View File

@ -39,14 +39,16 @@ import java.util.Set;
public final class PCallInvoke extends AExpression {
private final String name;
private final boolean nullSafe;
private final List<AExpression> arguments;
private AExpression sub = null;
public PCallInvoke(Location location, AExpression prefix, String name, List<AExpression> arguments) {
public PCallInvoke(Location location, AExpression prefix, String name, boolean nullSafe, List<AExpression> arguments) {
super(location, prefix);
this.name = Objects.requireNonNull(name);
this.nullSafe = nullSafe;
this.arguments = Objects.requireNonNull(arguments);
}
@ -87,6 +89,10 @@ public final class PCallInvoke extends AExpression {
"Unknown call [" + name + "] with [" + arguments.size() + "] arguments on type [" + struct.name + "]."));
}
if (nullSafe) {
sub = new PSubNullSafeCallInvoke(location, sub);
}
sub.expected = expected;
sub.explicit = explicit;
sub.analyze(locals);

View File

@ -40,13 +40,15 @@ import java.util.Set;
*/
public final class PField extends AStoreable {
private final boolean nullSafe;
private final String value;
private AStoreable sub = null;
public PField(Location location, AExpression prefix, String value) {
public PField(Location location, AExpression prefix, boolean nullSafe, String value) {
super(location, prefix);
this.nullSafe = nullSafe;
this.value = Objects.requireNonNull(value);
}
@ -106,6 +108,10 @@ public final class PField extends AStoreable {
throw createError(new IllegalArgumentException("Unknown field [" + value + "] for type [" + prefix.actual.name + "]."));
}
if (nullSafe) {
sub = new PSubNullSafeField(location, sub);
}
sub.write = write;
sub.read = read;
sub.expected = expected;

View File

@ -0,0 +1,76 @@
/*
* 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.Definition;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Label;
import java.util.Set;
import static java.util.Objects.requireNonNull;
/**
* Implements a call who's value is null if the prefix is null rather than throwing an NPE.
*/
public class PSubNullSafeCallInvoke extends AExpression {
/**
* The expression gaurded by the null check. Required at construction time and replaced at analysis time.
*/
private AExpression guarded;
public PSubNullSafeCallInvoke(Location location, AExpression guarded) {
super(location);
this.guarded = requireNonNull(guarded);
}
@Override
void extractVariables(Set<String> variables) {
guarded.extractVariables(variables);
}
@Override
void analyze(Locals locals) {
guarded.analyze(locals);
actual = guarded.actual;
if (actual.sort.primitive) {
// Result must be nullable. We emit boxing instructions if needed.
actual = Definition.getType(actual.sort.boxed.getSimpleName());
}
}
@Override
void write(MethodWriter writer, Globals globals) {
writer.writeDebugInfo(location);
Label end = new Label();
writer.dup();
writer.ifNull(end);
guarded.write(writer, globals);
if (guarded.actual.sort.primitive) {
// Box primitives so they are nullable
writer.box(guarded.actual.type);
}
writer.mark(end);
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.Definition.Type;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Label;
import java.util.Set;
/**
* Implements a field who's value is null if the prefix is null rather than throwing an NPE.
*/
public class PSubNullSafeField extends AStoreable {
private AStoreable guarded;
public PSubNullSafeField(Location location, AStoreable guarded) {
super(location);
this.guarded = guarded;
}
@Override
void extractVariables(Set<String> variables) {
guarded.extractVariables(variables);
}
@Override
void analyze(Locals locals) {
if (write) {
throw createError(new IllegalArgumentException("Can't write to null safe reference"));
}
guarded.read = read;
guarded.analyze(locals);
actual = guarded.actual;
if (actual.sort.primitive) {
// Result must be nullable. We emit boxing instructions if needed.
actual = Definition.getType(actual.sort.boxed.getSimpleName());
}
}
@Override
int accessElementCount() {
return guarded.accessElementCount();
}
@Override
boolean isDefOptimized() {
return guarded.isDefOptimized();
}
@Override
void updateActual(Type actual) {
guarded.updateActual(actual);
}
@Override
void write(MethodWriter writer, Globals globals) {
Label end = new Label();
writer.dup();
writer.ifNull(end);
guarded.write(writer, globals);
if (guarded.actual.sort.primitive) {
// Box primitives so they are nullable
writer.box(guarded.actual.type);
}
writer.mark(end);
}
@Override
void setup(MethodWriter writer, Globals globals) {
throw createError(new IllegalArgumentException("Can't write to null safe field"));
}
@Override
void load(MethodWriter writer, Globals globals) {
throw createError(new IllegalArgumentException("Can't write to null safe field"));
}
@Override
void store(MethodWriter writer, Globals globals) {
throw createError(new IllegalArgumentException("Can't write to null safe field"));
}
}

View File

@ -2,6 +2,8 @@ package org.elasticsearch.painless;
import java.util.Collections;
import static java.util.Collections.singletonMap;
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
@ -135,4 +137,73 @@ public class BasicExpressionTests extends ScriptTestCase {
assertEquals(2, exec("int x = 5; return (x+x)/x;"));
assertEquals(true, exec("boolean t = true, f = false; return t && (f || t);"));
}
public void testNullSafeDeref() {
// Objects in general
assertNull( exec("String a = null; return a?.toString()")); // Call
assertNull( exec("String a = null; return a?.length()")); // Call and box
assertEquals("foo", exec("String a = 'foo'; return a?.toString()")); // Call
assertEquals(Integer.valueOf(3), exec("String a = 'foo'; return a?.length()")); // Call and box
// Maps
assertNull( exec("Map a = null; return a?.toString()")); // Call
assertNull( exec("Map a = null; return a?.size()")); // Call and box
assertNull( exec("Map a = null; return a?.other")); // Read shortcut
assertEquals("{}", exec("Map a = [:]; return a?.toString()")); // Call
assertEquals(0, exec("Map a = [:]; return a?.size()")); // Call and box
assertEquals(1, exec("Map a = ['other':1]; return a?.other")); // Read shortcut
// Array
// Since you can't invoke methods on arrays we skip the toString and hashCode tests
assertNull( exec("int[] a = null; return a?.length")); // Length (boxed)
assertEquals(2, exec("int[] a = new int[] {2, 3}; return a?.length")); // Length (boxed)
// Def
assertNull( exec("def a = null; return a?.getX()")); // Invoke
assertNull( exec("def a = null; return a?.x")); // Read shortcut
assertEquals(0, exec("def a = new org.elasticsearch.painless.FeatureTest(); return a?.getX()")); // Invoke
assertEquals(0, exec("def a = new org.elasticsearch.painless.FeatureTest(); return a?.x")); // Read shortcut
// Results from maps (should just work but let's test anyway)
FeatureTest t = new FeatureTest();
assertNull( exec("Map a = ['thing': params.t]; return a.other?.getX()", singletonMap("t", t), true));
assertNull( exec("Map a = ['thing': params.t]; return a.other?.x", singletonMap("t", t), true));
assertNull( exec("def a = ['thing': params.t]; return a.other?.getX()", singletonMap("t", t), true));
assertNull( exec("def a = ['thing': params.t]; return a.other?.x", singletonMap("t", t), true));
assertEquals(0, exec("Map a = ['other': params.t]; return a.other?.getX()", singletonMap("t", t), true));
assertEquals(0, exec("Map a = ['other': params.t]; return a.other?.x", singletonMap("t", t), true));
assertEquals(0, exec("def a = ['other': params.t]; return a.other?.getX()", singletonMap("t", t), true));
assertEquals(0, exec("def a = ['other': params.t]; return a.other?.x", singletonMap("t", t), true));
// Chains
assertNull( exec("Map a = ['thing': ['cat': params.t]]; return a.other?.cat?.getX()", singletonMap("t", t), true));
assertNull( exec("Map a = ['thing': ['cat': params.t]]; return a.other?.cat?.x", singletonMap("t", t), true));
assertNull( exec("def a = ['thing': ['cat': params.t]]; return a.other?.cat?.getX()", singletonMap("t", t), true));
assertNull( exec("def a = ['thing': ['cat': params.t]]; return a.other?.cat?.x", singletonMap("t", t), true));
assertEquals(0, exec("Map a = ['other': ['cat': params.t]]; return a.other?.cat?.getX()", singletonMap("t", t), true));
assertEquals(0, exec("Map a = ['other': ['cat': params.t]]; return a.other?.cat?.x", singletonMap("t", t), true));
assertEquals(0, exec("def a = ['other': ['cat': params.t]]; return a.other?.cat?.getX()", singletonMap("t", t), true));
assertEquals(0, exec("def a = ['other': ['cat': params.t]]; return a.other?.cat?.x", singletonMap("t", t), true));
// Check that we don't try to cast when the LHS doesn't provide an "expected" value
assertNull(exec(
"def a = [:];\n"
+ "a.missing_length = a.missing?.length();\n"
+ "return a.missing_length", true));
assertEquals(3, exec(
"def a = [:];\n"
+ "a.missing = 'foo';\n"
+ "a.missing_length = a.missing?.length();\n"
+ "return a.missing_length", true));
// Writes, all unsupported at this point
// assertEquals(null, exec("org.elasticsearch.painless.FeatureTest a = null; return a?.x")); // Read field
// assertEquals(null, exec("org.elasticsearch.painless.FeatureTest a = null; a?.x = 7; return a?.x")); // Write field
// assertEquals(null, exec("Map a = null; a?.other = 'wow'; return a?.other")); // Write shortcut
// assertEquals(null, exec("def a = null; a?.other = 'cat'; return a?.other")); // Write shortcut
// assertEquals(null, exec("Map a = ['thing': 'bar']; a.other?.cat = 'no'; return a.other?.cat"));
// assertEquals(null, exec("def a = ['thing': 'bar']; a.other?.cat = 'no'; return a.other?.cat"));
// assertEquals(null, exec("Map a = ['thing': 'bar']; a.other?.cat?.dog = 'wombat'; return a.other?.cat?.dog"));
// assertEquals(null, exec("def a = ['thing': 'bar']; a.other?.cat?.dog = 'wombat'; return a.other?.cat?.dog"));
}
}

View File

@ -59,6 +59,59 @@
- match: { _source.foo: yyy }
- match: { _source.count: 1 }
- do:
update:
index: test_1
type: test
id: 1
body:
script:
lang: painless
inline: "ctx._source.missing_length = ctx._source.missing?.length()"
- match: { _index: test_1 }
- match: { _type: test }
- match: { _id: "1" }
- match: { _version: 4 }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: yyy }
- match: { _source.count: 1 }
- is_false: _source.missing
- is_false: _source.missing_length
- do:
update:
index: test_1
type: test
id: 1
body:
script:
lang: painless
inline: "ctx._source.foo_length = ctx._source.foo?.length()"
- match: { _index: test_1 }
- match: { _type: test }
- match: { _id: "1" }
- match: { _version: 5 }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: yyy }
- match: { _source.foo_length: 3 }
- match: { _source.count: 1 }
- is_false: _source.missing
- is_false: _source.missing_length
---
"Update Script with script error":
- do:

View File

@ -10,6 +10,8 @@ setup:
properties:
foo:
type: keyword
missing:
type: keyword
- do:
index:
index: test
@ -26,14 +28,42 @@ setup:
body:
script_fields:
bar:
script:
script:
inline: "doc['foo'].value + params.x;"
lang: painless
params:
x: "bbb"
- match: { hits.hits.0.fields.bar.0: "aaabbb"}
---
"Scripted Field with a null safe dereference (non-null)":
- do:
search:
body:
script_fields:
bar:
script:
inline: "doc['foo'].value?.length() + params.x;"
params:
x: 5
- match: { hits.hits.0.fields.bar.0: 8}
---
"Scripted Field with a null safe dereference (null)":
# Change this to ?: once we have it implemented
- do:
search:
body:
script_fields:
bar:
script:
inline: "(doc['missing'].value?.length() == null ? 0 : doc['missing'].value?.length()) + params.x;"
params:
x: 5
- match: { hits.hits.0.fields.bar.0: 5}
---
"Scripted Field with script error":
- do:
@ -43,7 +73,6 @@ setup:
script_fields:
bar:
script:
lang: painless
inline: "while (true) {}"
- match: { error.root_cause.0.type: "script_exception" }