1d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar/*
2d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
3d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
4d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * you may not use this file except in compliance with the License.
6d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * You may obtain a copy of the License at
7d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
8d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
10d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * See the License for the specific language governing permissions and
14d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * limitations under the License.
15d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar */
16d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool;
18d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.parser.BindingExpressionBaseVisitor;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.parser.BindingExpressionParser;
21c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mountimport android.databinding.parser.BindingExpressionParser.AndOrOpContext;
22c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mountimport android.databinding.parser.BindingExpressionParser.BinaryOpContext;
23c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mountimport android.databinding.parser.BindingExpressionParser.BitShiftOpContext;
24c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mountimport android.databinding.parser.BindingExpressionParser.InstanceOfOpContext;
25c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mountimport android.databinding.parser.BindingExpressionParser.UnaryOpContext;
266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.expr.CallbackExprModel;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.expr.Expr;
28fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.expr.ExprModel;
29fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.expr.StaticIdentifierExpr;
30fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
31fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
322611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyarimport android.databinding.tool.util.Preconditions;
33d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
3415b6682cf3505b117329e2190967c92a89b179e9Yigit Boyarimport com.android.annotations.NonNull;
35b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mountimport com.google.common.base.Objects;
36b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mount
37b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mountimport org.antlr.v4.runtime.ParserRuleContext;
38b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mountimport org.antlr.v4.runtime.tree.ParseTree;
39b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mountimport org.antlr.v4.runtime.tree.ParseTreeListener;
40b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mountimport org.antlr.v4.runtime.tree.TerminalNode;
41b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mount
426047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.ArrayDeque;
43d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.ArrayList;
44d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.List;
45d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
4615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyarclass ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
476047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private ExprModel mModel;
48c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    private ParseTreeListener mParseTreeListener;
496047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private ArrayDeque<ExprModel> mModelStack = new ArrayDeque<ExprModel>();
5078dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount    private BindingTarget mTarget;
51c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar
5215b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    ExpressionVisitor(ExprModel model) {
53d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        mModel = model;
54d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
55d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
5615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    void setParseTreeListener(ParseTreeListener parseTreeListener) {
57c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        mParseTreeListener = parseTreeListener;
58c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    }
59c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar
6078dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount    public void setBindingTarget(BindingTarget bindingTarget) {
6178dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount        mTarget = bindingTarget;
6278dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount    }
6378dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount
64c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    private void onEnter(ParserRuleContext context) {
65c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        if (mParseTreeListener != null) {
66c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            mParseTreeListener.enterEveryRule(context);
67c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
68c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    }
69c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar
70c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    private void onExit(ParserRuleContext context) {
71c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        if (mParseTreeListener != null) {
72c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            mParseTreeListener.exitEveryRule(context);
73c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
74c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar    }
75c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar
766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private void pushModel(ExprModel model) {
776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.checkNotNull(mModel, "Cannot put empty model to stack");
786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.checkNotNull(model, "Cannot set null model");
796047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mModelStack.push(mModel);
806047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mModel = model;
816047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
826047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
836047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private void popModel() {
846047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.checkNotNull(mModel, "Cannot have empty mdoel stack");
856047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.check(mModelStack.size() > 0, "Cannot have empty model stack");
866047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mModel = mModelStack.pop();
876047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
886047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
896047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
9015b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitRootLambda(@NonNull BindingExpressionParser.RootLambdaContext ctx) {
916047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        try {
926047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onEnter(ctx);
936047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            CallbackExprModel callbackModel = new CallbackExprModel(mModel);
946047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            ExprModel prev = mModel;
956047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            pushModel(callbackModel);
966047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            final BindingExpressionParser.LambdaExpressionContext lambdaCtx = ctx
976047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    .lambdaExpression();
986047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            lambdaCtx.args.accept(this);
996047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return prev.lambdaExpr(lambdaCtx.expression().accept(this), callbackModel);
1006047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } finally {
1016047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            popModel();
1026047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onExit(ctx);
1036047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1046047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1056047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1066047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
1076047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public Expr visitSingleLambdaParameter(
10815b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            @NonNull BindingExpressionParser.SingleLambdaParameterContext ctx) {
1096047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        try {
1106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onEnter(ctx);
1116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
1126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    + " callbacks.");
1136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            // just add it to the callback model as identifier
1146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            ((CallbackExprModel) mModel).callbackArg(ctx.getText());
1156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return null;
1166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } finally {
1176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onExit(ctx);
1186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1206047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
1226047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public Expr visitLambdaParameterList(
12315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            @NonNull BindingExpressionParser.LambdaParameterListContext ctx) {
1246047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        try {
1256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onEnter(ctx);
1266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
1276047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    + " callbacks.");
1286047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            if (ctx.params != null) {
1296047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                for (ParseTree item : ctx.params.children) {
1306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    if (Objects.equal(item.getText(), ",")) {
1316047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                        continue;
1326047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    }
1336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    // just add them to the callback model as identifiers
1346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    ((CallbackExprModel) mModel).callbackArg(item.getText());
1356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                }
1366047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
1376047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return null;
1386047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } finally {
1396047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            onExit(ctx);
1406047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1416047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1426047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
143d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
14415b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitStringLiteral(@NonNull BindingExpressionParser.StringLiteralContext ctx) {
145c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
146c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
147c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final String javaString;
148c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            if (ctx.SingleQuoteString() != null) {
149c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                String str = ctx.SingleQuoteString().getText();
150c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                String contents = str.substring(1, str.length() - 1);
151c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                contents = contents.replace("\"", "\\\"").replace("\\`", "`");
152c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                javaString = '"' + contents + '"';
153c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            } else {
154c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                javaString = ctx.DoubleQuoteString().getText();
155c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            }
156c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.symbol(javaString, String.class);
157c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
158c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
159d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
160d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
161d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
162d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
16315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitRootExpr(@NonNull BindingExpressionParser.RootExprContext ctx) {
164c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
165c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
1666047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            // TODO handle defaults
1676047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return mModel.bindingExpr(ctx.expression().accept(this));
1686047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } catch (Exception e) {
1696047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            System.out.println("Error while parsing! " + ctx.getText());
1706047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            e.printStackTrace();
1716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            throw new RuntimeException(e);
172c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
173c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
174c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
175d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
176d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
177d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
17815b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitGrouping(@NonNull BindingExpressionParser.GroupingContext ctx) {
179d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        try {
180c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
1816047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            Preconditions.check(ctx.children.size() == 3, "Grouping expression should have"
1826047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    + " 3 children. # of children: %d", ctx.children.size());
183b522c7650bf7d9ec566845bc9eb37e761eea853dGeorge Mount            return ctx.children.get(1).accept(this);
184c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
185c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
186d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
187d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
188d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
189d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
19015b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitDotOp(@NonNull BindingExpressionParser.DotOpContext ctx) {
191c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
192c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
193c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
194c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports());
195c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            if (modelClass == null) {
196c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                return mModel.field(ctx.expression().accept(this),
197c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                        ctx.Identifier().getSymbol().getText());
198c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            } else {
199c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                String name = modelClass.toJavaCode();
200c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                StaticIdentifierExpr expr = mModel.staticIdentifier(name);
201c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                expr.setUserDefinedType(name);
202c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                return expr;
203c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            }
204c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
205c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
2068e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        }
207d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
208d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
209d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
21015b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitFunctionRef(@NonNull BindingExpressionParser.FunctionRefContext ctx) {
211c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount        try {
212c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount            onEnter(ctx);
213c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount            return mModel.methodReference(ctx.expression().accept(this),
214c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount                    ctx.Identifier().getSymbol().getText());
215c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount        } finally {
216c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount            onExit(ctx);
217c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount        }
218c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount    }
219c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount
220c0c1dab0b6254e4d27f18c37a72a9e7952e958a0George Mount    @Override
22115b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitQuestionQuestionOp(
22215b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            @NonNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
223c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
224c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
225c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final Expr left = ctx.left.accept(this);
226c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
227c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    ctx.right.accept(this), left);
228c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
229c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
230c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
231d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
232d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
233d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
23415b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitTerminal(@NonNull TerminalNode node) {
235c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
23615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            onEnter((ParserRuleContext) node.getParent());
237c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final int type = node.getSymbol().getType();
238c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            Class classType;
239c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            switch (type) {
240c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.IntegerLiteral:
241c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = int.class;
242c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
243c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.FloatingPointLiteral:
244c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = float.class;
245c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
246c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.BooleanLiteral:
247c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = boolean.class;
248c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
249c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.CharacterLiteral:
250c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = char.class;
251c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
252c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.SingleQuoteString:
253c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.DoubleQuoteString:
254c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = String.class;
255c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
256c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                case BindingExpressionParser.NullLiteral:
257c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    classType = Object.class;
258c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    break;
2596047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                case BindingExpressionParser.VoidLiteral:
2606047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    classType = void.class;
2616047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    break;
262c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                default:
263c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    throw new RuntimeException("cannot create expression from terminal node " +
264c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                            node.toString());
265c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            }
266c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.symbol(node.getText(), classType);
267c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
26815b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            onExit((ParserRuleContext) node.getParent());
269d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
270d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
271d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
272d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
27315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitComparisonOp(@NonNull BindingExpressionParser.ComparisonOpContext ctx) {
274c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
275c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
276c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this));
277c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
278c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
279c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
280d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
281d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
282d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
28315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitIdentifier(@NonNull BindingExpressionParser.IdentifierContext ctx) {
284c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
285c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
286c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.identifier(ctx.getText());
287c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
288c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
289c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
290d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
291d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
292d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
29315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitTernaryOp(@NonNull BindingExpressionParser.TernaryOpContext ctx) {
294c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
295c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
296c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this),
297c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    ctx.iffalse.accept(this));
298c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
299c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
300c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
301c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar
302d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
303d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
304d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
305d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public Expr visitMethodInvocation(
30615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar            @NonNull BindingExpressionParser.MethodInvocationContext ctx) {
307c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
308c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
309c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            List<Expr> args = new ArrayList<Expr>();
310c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            if (ctx.args != null) {
311c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                for (ParseTree item : ctx.args.children) {
3124ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta                    if (Objects.equal(item.getText(), ",")) {
313c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                        continue;
314c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    }
315c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    args.add(item.accept(this));
316d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar                }
317d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar            }
318c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.methodCall(ctx.target.accept(this),
319c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    ctx.Identifier().getText(), args);
320c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
321c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
322d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
323d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
324d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
325d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
32615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitMathOp(@NonNull BindingExpressionParser.MathOpContext ctx) {
327c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
328c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
329c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
330c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
331c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
332c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
333d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
334d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
335c752a5f795baf6df435ef60881316cb748df407cGeorge Mount    @Override
33615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitAndOrOp(@NonNull AndOrOpContext ctx) {
337c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
338c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
339c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.logical(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
340c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
341c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
342c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
343c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    }
344c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount
345c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    @Override
34615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitBinaryOp(@NonNull BinaryOpContext ctx) {
347c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
348c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
349c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
350c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
351c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
352c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
353c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    }
354c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount
355c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    @Override
35615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitBitShiftOp(@NonNull BitShiftOpContext ctx) {
357c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
358c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
359c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.bitshift(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
360c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
361c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
362c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
363c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    }
364c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount
365c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    @Override
36615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitInstanceOfOp(@NonNull InstanceOfOpContext ctx) {
367c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
368c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
369c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.instanceOfOp(ctx.expression().accept(this), ctx.type().getText());
370c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
371c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
372c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
373c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    }
374c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount
375c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    @Override
37615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitUnaryOp(@NonNull UnaryOpContext ctx) {
377c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
378c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
379c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.unary(ctx.op.getText(), ctx.expression().accept(this));
380c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
381c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
382c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
383c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    }
384c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount
385c6bcb7bf9cab139b3141c4644e5b3267deed5213George Mount    @Override
38615b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitResources(@NonNull BindingExpressionParser.ResourcesContext ctx) {
387c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
388c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
389c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final List<Expr> args = new ArrayList<Expr>();
390c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            if (ctx.resourceParameters() != null) {
391c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                for (ParseTree item : ctx.resourceParameters().expressionList().children) {
3924ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta                    if (Objects.equal(item.getText(), ",")) {
393c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                        continue;
394c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    }
395c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    args.add(item.accept(this));
396ae7cb82316e351c488ee3b9c7226602321f34301George Mount                }
397ae7cb82316e351c488ee3b9c7226602321f34301George Mount            }
398c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final String resourceReference = ctx.ResourceReference().getText();
399c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final int colonIndex = resourceReference.indexOf(':');
400c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final int slashIndex = resourceReference.indexOf('/');
401c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final String packageName = colonIndex < 0 ? null :
402c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar                    resourceReference.substring(1, colonIndex).trim();
403c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final int startIndex = Math.max(1, colonIndex + 1);
404c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final String resourceType = resourceReference.substring(startIndex, slashIndex).trim();
405c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            final String resourceName = resourceReference.substring(slashIndex + 1).trim();
40678dc9ae6d67ae94bf3f637eeea0848e4f700b7a3George Mount            return mModel.resourceExpr(mTarget, packageName, resourceType, resourceName, args);
407c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
408c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
409ae7cb82316e351c488ee3b9c7226602321f34301George Mount        }
410c752a5f795baf6df435ef60881316cb748df407cGeorge Mount    }
411c752a5f795baf6df435ef60881316cb748df407cGeorge Mount
4125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    @Override
41315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitBracketOp(@NonNull BindingExpressionParser.BracketOpContext ctx) {
414c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
415c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
416c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1)));
417c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
418c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
419c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
4205cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
4215cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
422e4b93061ac703e48fc2c9994c9059ed016f05559George Mount    @Override
42315b6682cf3505b117329e2190967c92a89b179e9Yigit Boyar    public Expr visitCastOp(@NonNull BindingExpressionParser.CastOpContext ctx) {
424c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        try {
425c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onEnter(ctx);
426c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            return mModel.castExpr(ctx.type().getText(), visit(ctx.expression()));
427c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        } finally {
428c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar            onExit(ctx);
429c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar        }
430e4b93061ac703e48fc2c9994c9059ed016f05559George Mount    }
431d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar}
432