diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java index 33ab695d527..090667b4543 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java @@ -19,21 +19,10 @@ package org.elasticsearch.painless; -import org.antlr.v4.runtime.ParserRuleContext; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.Constructor; -import org.elasticsearch.painless.Definition.Field; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Pair; -import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Definition.Transform; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.PainlessParser.AfterthoughtContext; import org.elasticsearch.painless.PainlessParser.ArgumentsContext; import org.elasticsearch.painless.PainlessParser.AssignmentContext; import org.elasticsearch.painless.PainlessParser.BinaryContext; -import org.elasticsearch.painless.PainlessParser.BlockContext; import org.elasticsearch.painless.PainlessParser.BoolContext; import org.elasticsearch.painless.PainlessParser.BreakContext; import org.elasticsearch.painless.PainlessParser.CastContext; @@ -47,8 +36,8 @@ import org.elasticsearch.painless.PainlessParser.DecltypeContext; import org.elasticsearch.painless.PainlessParser.DeclvarContext; import org.elasticsearch.painless.PainlessParser.DoContext; import org.elasticsearch.painless.PainlessParser.EmptyContext; +import org.elasticsearch.painless.PainlessParser.EmptyscopeContext; import org.elasticsearch.painless.PainlessParser.ExprContext; -import org.elasticsearch.painless.PainlessParser.ExpressionContext; import org.elasticsearch.painless.PainlessParser.ExtbraceContext; import org.elasticsearch.painless.PainlessParser.ExtcallContext; import org.elasticsearch.painless.PainlessParser.ExtcastContext; @@ -75,7 +64,6 @@ import org.elasticsearch.painless.PainlessParser.PreincContext; import org.elasticsearch.painless.PainlessParser.ReturnContext; import org.elasticsearch.painless.PainlessParser.SingleContext; import org.elasticsearch.painless.PainlessParser.SourceContext; -import org.elasticsearch.painless.PainlessParser.StatementContext; import org.elasticsearch.painless.PainlessParser.ThrowContext; import org.elasticsearch.painless.PainlessParser.TrapContext; import org.elasticsearch.painless.PainlessParser.TrueContext; @@ -83,3077 +71,384 @@ import org.elasticsearch.painless.PainlessParser.TryContext; import org.elasticsearch.painless.PainlessParser.UnaryContext; import org.elasticsearch.painless.PainlessParser.WhileContext; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import static org.elasticsearch.painless.PainlessParser.ADD; -import static org.elasticsearch.painless.PainlessParser.BWAND; -import static org.elasticsearch.painless.PainlessParser.BWOR; -import static org.elasticsearch.painless.PainlessParser.BWXOR; -import static org.elasticsearch.painless.PainlessParser.DIV; -import static org.elasticsearch.painless.PainlessParser.LSH; -import static org.elasticsearch.painless.PainlessParser.MUL; -import static org.elasticsearch.painless.PainlessParser.REM; -import static org.elasticsearch.painless.PainlessParser.RSH; -import static org.elasticsearch.painless.PainlessParser.SUB; -import static org.elasticsearch.painless.PainlessParser.USH; - class Analyzer extends PainlessParserBaseVisitor { - private static class Variable { - final String name; - final Type type; - final int slot; - - private Variable(final String name, final Type type, final int slot) { - this.name = name; - this.type = type; - this.slot = slot; - } - } - static void analyze(final Metadata metadata) { new Analyzer(metadata); } - private final Metadata metadata; - private final Definition definition; - private final CompilerSettings settings; - - private final Deque scopes = new ArrayDeque<>(); - private final Deque variables = new ArrayDeque<>(); + private final AnalyzerStatement statement; + private final AnalyzerExpression expression; + private final AnalyzerExternal external; private Analyzer(final Metadata metadata) { - this.metadata = metadata; - definition = metadata.definition; - settings = metadata.settings; + final Definition definition = metadata.definition; - incrementScope(); - addVariable(null, "#this", definition.execType); - metadata.inputValueSlot = addVariable(null, "input", definition.smapType).slot; - metadata.scoreValueSlot = addVariable(null, "_score", definition.floatType).slot; - metadata.loopCounterSlot = addVariable(null, "#loop", definition.intType).slot; + final AnalyzerUtility utility = new AnalyzerUtility(); + final AnalyzerCaster caster = new AnalyzerCaster(definition); + final AnalyzerPromoter promoter = new AnalyzerPromoter(definition); + + statement = new AnalyzerStatement(metadata, this, utility, caster); + expression = new AnalyzerExpression(metadata, this, caster, promoter); + external = new AnalyzerExternal(metadata, this, utility, caster, promoter); + + utility.incrementScope(); + utility.addVariable(null, "#this", definition.execType); + metadata.inputValueSlot = utility.addVariable(null, "input", definition.smapType).slot; + metadata.scoreValueSlot = utility.addVariable(null, "_score", definition.floatType).slot; + metadata.loopCounterSlot = utility.addVariable(null, "#loop", definition.intType).slot; metadata.createStatementMetadata(metadata.root); visit(metadata.root); - decrementScope(); - } - - void incrementScope() { - scopes.push(0); - } - - void decrementScope() { - int remove = scopes.pop(); - - while (remove > 0) { - variables.pop(); - --remove; - } - } - - Variable getVariable(final String name) { - final Iterator itr = variables.iterator(); - - while (itr.hasNext()) { - final Variable variable = itr.next(); - - if (variable.name.equals(name)) { - return variable; - } - } - - return null; - } - - Variable addVariable(final ParserRuleContext source, final String name, final Type type) { - if (getVariable(name) != null) { - if (source == null) { - throw new IllegalArgumentException("Argument name [" + name + "] already defined within the scope."); - } else { - throw new IllegalArgumentException( - Metadata.error(source) + "Variable name [" + name + "] already defined within the scope."); - } - } - - final Variable previous = variables.peekFirst(); - int slot = 0; - - if (previous != null) { - slot += previous.slot + previous.type.type.getSize(); - } - - final Variable variable = new Variable(name, type, slot); - variables.push(variable); - - final int update = scopes.pop() + 1; - scopes.push(update); - - return variable; + utility.decrementScope(); } @Override public Void visitSource(final SourceContext ctx) { - final Metadata.StatementMetadata sourcesmd = metadata.getStatementMetadata(ctx); - final List statectxs = ctx.statement(); - final StatementContext lastctx = statectxs.get(statectxs.size() - 1); - - incrementScope(); - - for (final StatementContext statectx : statectxs) { - if (sourcesmd.allLast) { - throw new IllegalArgumentException(Metadata.error(statectx) + - "Statement will never be executed because all prior paths escape."); - } - - final Metadata.StatementMetadata statesmd = metadata.createStatementMetadata(statectx); - statesmd.lastSource = statectx == lastctx; - visit(statectx); - - sourcesmd.methodEscape = statesmd.methodEscape; - sourcesmd.allLast = statesmd.allLast; - } - - decrementScope(); + statement.processSource(ctx); return null; } @Override public Void visitIf(final IfContext ctx) { - final Metadata.StatementMetadata ifsmd = metadata.getStatementMetadata(ctx); - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.booleanType; - visit(exprctx); - markCast(expremd); - - if (expremd.postConst != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "If statement is not necessary."); - } - - final BlockContext blockctx0 = ctx.block(0); - final Metadata.StatementMetadata blocksmd0 = metadata.createStatementMetadata(blockctx0); - blocksmd0.lastSource = ifsmd.lastSource; - blocksmd0.inLoop = ifsmd.inLoop; - blocksmd0.lastLoop = ifsmd.lastLoop; - incrementScope(); - visit(blockctx0); - decrementScope(); - - ifsmd.anyContinue = blocksmd0.anyContinue; - ifsmd.anyBreak = blocksmd0.anyBreak; - - ifsmd.count = blocksmd0.count; - - if (ctx.ELSE() != null) { - final BlockContext blockctx1 = ctx.block(1); - final Metadata.StatementMetadata blocksmd1 = metadata.createStatementMetadata(blockctx1); - blocksmd1.lastSource = ifsmd.lastSource; - incrementScope(); - visit(blockctx1); - decrementScope(); - - ifsmd.methodEscape = blocksmd0.methodEscape && blocksmd1.methodEscape; - ifsmd.loopEscape = blocksmd0.loopEscape && blocksmd1.loopEscape; - ifsmd.allLast = blocksmd0.allLast && blocksmd1.allLast; - ifsmd.anyContinue |= blocksmd1.anyContinue; - ifsmd.anyBreak |= blocksmd1.anyBreak; - - ifsmd.count = Math.max(ifsmd.count, blocksmd1.count); - } + statement.processIf(ctx); return null; } @Override public Void visitWhile(final WhileContext ctx) { - final Metadata.StatementMetadata whilesmd = metadata.getStatementMetadata(ctx); - - incrementScope(); - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.booleanType; - visit(exprctx); - markCast(expremd); - - boolean continuous = false; - - if (expremd.postConst != null) { - continuous = (boolean)expremd.postConst; - - if (!continuous) { - throw new IllegalArgumentException(Metadata.error(ctx) + "The loop will never be executed."); - } - - if (ctx.empty() != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "The loop will never exit."); - } - } - - final BlockContext blockctx = ctx.block(); - - if (blockctx != null) { - final Metadata.StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); - blocksmd.beginLoop = true; - blocksmd.inLoop = true; - visit(blockctx); - - if (blocksmd.loopEscape && !blocksmd.anyContinue) { - throw new IllegalArgumentException(Metadata.error(ctx) + "All paths escape so the loop is not necessary."); - } - - if (continuous && !blocksmd.anyBreak) { - whilesmd.methodEscape = true; - whilesmd.allLast = true; - } - } - - whilesmd.count = 1; - - decrementScope(); + statement.processWhile(ctx); return null; } @Override public Void visitDo(final DoContext ctx) { - final Metadata.StatementMetadata dosmd = metadata.getStatementMetadata(ctx); - - incrementScope(); - - final BlockContext blockctx = ctx.block(); - final Metadata.StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); - blocksmd.beginLoop = true; - blocksmd.inLoop = true; - visit(blockctx); - - if (blocksmd.loopEscape && !blocksmd.anyContinue) { - throw new IllegalArgumentException(Metadata.error(ctx) + "All paths escape so the loop is not necessary."); - } - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.booleanType; - visit(exprctx); - markCast(expremd); - - if (expremd.postConst != null) { - final boolean continuous = (boolean)expremd.postConst; - - if (!continuous) { - throw new IllegalArgumentException(Metadata.error(ctx) + "All paths escape so the loop is not necessary."); - } - - if (!blocksmd.anyBreak) { - dosmd.methodEscape = true; - dosmd.allLast = true; - } - } - - dosmd.count = 1; - - decrementScope(); + statement.processDo(ctx); return null; } @Override public Void visitFor(final ForContext ctx) { - final Metadata.StatementMetadata forsmd = metadata.getStatementMetadata(ctx); - boolean continuous = false; - - incrementScope(); - - final InitializerContext initctx = ctx.initializer(); - - if (initctx != null) { - metadata.createStatementMetadata(initctx); - visit(initctx); - } - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - - if (exprctx != null) { - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.booleanType; - visit(exprctx); - markCast(expremd); - - if (expremd.postConst != null) { - continuous = (boolean)expremd.postConst; - - if (!continuous) { - throw new IllegalArgumentException(Metadata.error(ctx) + "The loop will never be executed."); - } - - if (ctx.empty() != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "The loop is continuous."); - } - } - } else { - continuous = true; - } - - final AfterthoughtContext atctx = ctx.afterthought(); - - if (atctx != null) { - metadata.createStatementMetadata(atctx); - visit(atctx); - } - - final BlockContext blockctx = ctx.block(); - - if (blockctx != null) { - final Metadata.StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); - blocksmd.beginLoop = true; - blocksmd.inLoop = true; - visit(blockctx); - - if (blocksmd.loopEscape && !blocksmd.anyContinue) { - throw new IllegalArgumentException(Metadata.error(ctx) + "All paths escape so the loop is not necessary."); - } - - if (continuous && !blocksmd.anyBreak) { - forsmd.methodEscape = true; - forsmd.allLast = true; - } - } - - forsmd.count = 1; - - decrementScope(); + statement.processFor(ctx); return null; } @Override public Void visitDecl(final DeclContext ctx) { - final Metadata.StatementMetadata declsmd = metadata.getStatementMetadata(ctx); - - final DeclarationContext declctx = ctx.declaration(); - metadata.createStatementMetadata(declctx); - visit(declctx); - - declsmd.count = 1; + statement.processDecl(ctx); return null; } @Override public Void visitContinue(final ContinueContext ctx) { - final Metadata.StatementMetadata continuesmd = metadata.getStatementMetadata(ctx); - - if (!continuesmd.inLoop) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Cannot have a continue statement outside of a loop."); - } - - if (continuesmd.lastLoop) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unnessary continue statement at the end of a loop."); - } - - continuesmd.allLast = true; - continuesmd.anyContinue = true; - - continuesmd.count = 1; + statement.processContinue(ctx); return null; } @Override public Void visitBreak(final BreakContext ctx) { - final Metadata.StatementMetadata breaksmd = metadata.getStatementMetadata(ctx); - - if (!breaksmd.inLoop) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Cannot have a break statement outside of a loop."); - } - - breaksmd.loopEscape = true; - breaksmd.allLast = true; - breaksmd.anyBreak = true; - - breaksmd.count = 1; + statement.processBreak(ctx); return null; } @Override public Void visitReturn(final ReturnContext ctx) { - final Metadata.StatementMetadata returnsmd = metadata.getStatementMetadata(ctx); - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.objectType; - visit(exprctx); - markCast(expremd); - - returnsmd.methodEscape = true; - returnsmd.loopEscape = true; - returnsmd.allLast = true; - - returnsmd.count = 1; + statement.processReturn(ctx); return null; } @Override public Void visitTry(final TryContext ctx) { - final Metadata.StatementMetadata trysmd = metadata.getStatementMetadata(ctx); - - final BlockContext blockctx = ctx.block(); - final Metadata.StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); - blocksmd.lastSource = trysmd.lastSource; - blocksmd.inLoop = trysmd.inLoop; - blocksmd.lastLoop = trysmd.lastLoop; - incrementScope(); - visit(blockctx); - decrementScope(); - - trysmd.methodEscape = blocksmd.methodEscape; - trysmd.loopEscape = blocksmd.loopEscape; - trysmd.allLast = blocksmd.allLast; - trysmd.anyContinue = blocksmd.anyContinue; - trysmd.anyBreak = blocksmd.anyBreak; - - int trapcount = 0; - - for (final TrapContext trapctx : ctx.trap()) { - final Metadata.StatementMetadata trapsmd = metadata.createStatementMetadata(trapctx); - trapsmd.lastSource = trysmd.lastSource; - trapsmd.inLoop = trysmd.inLoop; - trapsmd.lastLoop = trysmd.lastLoop; - incrementScope(); - visit(trapctx); - decrementScope(); - - trysmd.methodEscape &= trapsmd.methodEscape; - trysmd.loopEscape &= trapsmd.loopEscape; - trysmd.allLast &= trapsmd.allLast; - trysmd.anyContinue |= trapsmd.anyContinue; - trysmd.anyBreak |= trapsmd.anyBreak; - - trapcount = Math.max(trapcount, trapsmd.count); - } - - trysmd.count = blocksmd.count + trapcount; + statement.processTry(ctx); return null; } @Override public Void visitThrow(final ThrowContext ctx) { - final Metadata.StatementMetadata throwsmd = metadata.getStatementMetadata(ctx); - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = definition.exceptionType; - visit(exprctx); - markCast(expremd); - - throwsmd.methodEscape = true; - throwsmd.loopEscape = true; - throwsmd.allLast = true; - - throwsmd.count = 1; + statement.processThrow(ctx); return null; } @Override public Void visitExpr(final ExprContext ctx) { - final Metadata.StatementMetadata exprsmd = metadata.getStatementMetadata(ctx); - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.read = exprsmd.lastSource; - visit(exprctx); - - if (!expremd.statement && !exprsmd.lastSource) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Not a statement."); - } - - final boolean rtn = exprsmd.lastSource && expremd.from.sort != Sort.VOID; - exprsmd.methodEscape = rtn; - exprsmd.loopEscape = rtn; - exprsmd.allLast = rtn; - expremd.to = rtn ? definition.objectType : expremd.from; - markCast(expremd); - - exprsmd.count = 1; + statement.processExpr(ctx); return null; } @Override public Void visitMultiple(final MultipleContext ctx) { - final Metadata.StatementMetadata multiplesmd = metadata.getStatementMetadata(ctx); - final List statectxs = ctx.statement(); - final StatementContext lastctx = statectxs.get(statectxs.size() - 1); - - for (StatementContext statectx : statectxs) { - if (multiplesmd.allLast) { - throw new IllegalArgumentException(Metadata.error(statectx) + - "Statement will never be executed because all prior paths escape."); - } - - final Metadata.StatementMetadata statesmd = metadata.createStatementMetadata(statectx); - statesmd.lastSource = multiplesmd.lastSource && statectx == lastctx; - statesmd.inLoop = multiplesmd.inLoop; - statesmd.lastLoop = (multiplesmd.beginLoop || multiplesmd.lastLoop) && statectx == lastctx; - visit(statectx); - - multiplesmd.methodEscape = statesmd.methodEscape; - multiplesmd.loopEscape = statesmd.loopEscape; - multiplesmd.allLast = statesmd.allLast; - multiplesmd.anyContinue |= statesmd.anyContinue; - multiplesmd.anyBreak |= statesmd.anyBreak; - - multiplesmd.count += statesmd.count; - } + statement.processMultiple(ctx); return null; } @Override public Void visitSingle(final SingleContext ctx) { - final Metadata.StatementMetadata singlesmd = metadata.getStatementMetadata(ctx); - - final StatementContext statectx = ctx.statement(); - final Metadata.StatementMetadata statesmd = metadata.createStatementMetadata(statectx); - statesmd.lastSource = singlesmd.lastSource; - statesmd.inLoop = singlesmd.inLoop; - statesmd.lastLoop = singlesmd.beginLoop || singlesmd.lastLoop; - visit(statectx); - - singlesmd.methodEscape = statesmd.methodEscape; - singlesmd.loopEscape = statesmd.loopEscape; - singlesmd.allLast = statesmd.allLast; - singlesmd.anyContinue = statesmd.anyContinue; - singlesmd.anyBreak = statesmd.anyBreak; - - singlesmd.count = statesmd.count; + statement.processSingle(ctx); return null; } @Override public Void visitEmpty(final EmptyContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected parser state."); + throw new UnsupportedOperationException(AnalyzerUtility.error(ctx) + "Unexpected state."); } @Override - public Void visitInitializer(InitializerContext ctx) { - final DeclarationContext declctx = ctx.declaration(); - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); + public Void visitEmptyscope(final EmptyscopeContext ctx) { + throw new UnsupportedOperationException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } - if (declctx != null) { - metadata.createStatementMetadata(declctx); - visit(declctx); - } else if (exprctx != null) { - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.read = false; - visit(exprctx); - - expremd.to = expremd.from; - markCast(expremd); - - if (!expremd.statement) { - throw new IllegalArgumentException(Metadata.error(exprctx) + - "The intializer of a for loop must be a statement."); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } + @Override + public Void visitInitializer(final InitializerContext ctx) { + statement.processInitializer(ctx); return null; } @Override - public Void visitAfterthought(AfterthoughtContext ctx) { - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - - if (exprctx != null) { - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.read = false; - visit(exprctx); - - expremd.to = expremd.from; - markCast(expremd); - - if (!expremd.statement) { - throw new IllegalArgumentException(Metadata.error(exprctx) + - "The afterthought of a for loop must be a statement."); - } - } + public Void visitAfterthought(final AfterthoughtContext ctx) { + statement.processAfterthought(ctx); return null; } @Override public Void visitDeclaration(final DeclarationContext ctx) { - final DecltypeContext decltypectx = ctx.decltype(); - final Metadata.ExpressionMetadata decltypeemd = metadata.createExpressionMetadata(decltypectx); - visit(decltypectx); - - for (final DeclvarContext declvarctx : ctx.declvar()) { - final Metadata.ExpressionMetadata declvaremd = metadata.createExpressionMetadata(declvarctx); - declvaremd.to = decltypeemd.from; - visit(declvarctx); - } + statement.processDeclaration(ctx); return null; } @Override public Void visitDecltype(final DecltypeContext ctx) { - final Metadata.ExpressionMetadata decltypeemd = metadata.getExpressionMetadata(ctx); - - final String name = ctx.getText(); - decltypeemd.from = definition.getType(name); + statement.processDecltype(ctx); return null; } @Override public Void visitDeclvar(final DeclvarContext ctx) { - final Metadata.ExpressionMetadata declvaremd = metadata.getExpressionMetadata(ctx); - - final String name = ctx.ID().getText(); - declvaremd.postConst = addVariable(ctx, name, declvaremd.to).slot; - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - - if (exprctx != null) { - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = declvaremd.to; - visit(exprctx); - markCast(expremd); - } + statement.processDeclvar(ctx); return null; } @Override public Void visitTrap(final TrapContext ctx) { - final Metadata.StatementMetadata trapsmd = metadata.getStatementMetadata(ctx); - - final String type = ctx.TYPE().getText(); - trapsmd.exception = definition.getType(type); - - try { - trapsmd.exception.clazz.asSubclass(Exception.class); - } catch (final ClassCastException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid exception type [" + trapsmd.exception.name + "]."); - } - - final String id = ctx.ID().getText(); - trapsmd.slot = addVariable(ctx, id, trapsmd.exception).slot; - - final BlockContext blockctx = ctx.block(); - - if (blockctx != null) { - final Metadata.StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); - blocksmd.lastSource = trapsmd.lastSource; - blocksmd.inLoop = trapsmd.inLoop; - blocksmd.lastLoop = trapsmd.lastLoop; - visit(blockctx); - - trapsmd.methodEscape = blocksmd.methodEscape; - trapsmd.loopEscape = blocksmd.loopEscape; - trapsmd.allLast = blocksmd.allLast; - trapsmd.anyContinue = blocksmd.anyContinue; - trapsmd.anyBreak = blocksmd.anyBreak; - } else if (ctx.emptyscope() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } + statement.processTrap(ctx); return null; } @Override public Void visitPrecedence(final PrecedenceContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected parser state."); + throw new UnsupportedOperationException(AnalyzerUtility.error(ctx) + "Unexpected state."); } @Override public Void visitNumeric(final NumericContext ctx) { - final Metadata.ExpressionMetadata numericemd = metadata.getExpressionMetadata(ctx); - final boolean negate = ctx.parent instanceof UnaryContext && ((UnaryContext)ctx.parent).SUB() != null; - - if (ctx.DECIMAL() != null) { - final String svalue = (negate ? "-" : "") + ctx.DECIMAL().getText(); - - if (svalue.endsWith("f") || svalue.endsWith("F")) { - try { - numericemd.from = definition.floatType; - numericemd.preConst = Float.parseFloat(svalue.substring(0, svalue.length() - 1)); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid float constant [" + svalue + "]."); - } - } else { - try { - numericemd.from = definition.doubleType; - numericemd.preConst = Double.parseDouble(svalue); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid double constant [" + svalue + "]."); - } - } - } else { - String svalue = negate ? "-" : ""; - int radix; - - if (ctx.OCTAL() != null) { - svalue += ctx.OCTAL().getText(); - radix = 8; - } else if (ctx.INTEGER() != null) { - svalue += ctx.INTEGER().getText(); - radix = 10; - } else if (ctx.HEX() != null) { - svalue += ctx.HEX().getText(); - radix = 16; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - if (svalue.endsWith("d") || svalue.endsWith("D")) { - try { - numericemd.from = definition.doubleType; - numericemd.preConst = Double.parseDouble(svalue.substring(0, svalue.length() - 1)); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid float constant [" + svalue + "]."); - } - } else if (svalue.endsWith("f") || svalue.endsWith("F")) { - try { - numericemd.from = definition.floatType; - numericemd.preConst = Float.parseFloat(svalue.substring(0, svalue.length() - 1)); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid float constant [" + svalue + "]."); - } - } else if (svalue.endsWith("l") || svalue.endsWith("L")) { - try { - numericemd.from = definition.longType; - numericemd.preConst = Long.parseLong(svalue.substring(0, svalue.length() - 1), radix); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid long constant [" + svalue + "]."); - } - } else { - try { - final Type type = numericemd.to; - final Sort sort = type == null ? Sort.INT : type.sort; - final int value = Integer.parseInt(svalue, radix); - - if (sort == Sort.BYTE && value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - numericemd.from = definition.byteType; - numericemd.preConst = (byte)value; - } else if (sort == Sort.CHAR && value >= Character.MIN_VALUE && value <= Character.MAX_VALUE) { - numericemd.from = definition.charType; - numericemd.preConst = (char)value; - } else if (sort == Sort.SHORT && value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - numericemd.from = definition.shortType; - numericemd.preConst = (short)value; - } else { - numericemd.from = definition.intType; - numericemd.preConst = value; - } - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Invalid int constant [" + svalue + "]."); - } - } - } + expression.processNumeric(ctx); return null; } @Override public Void visitChar(final CharContext ctx) { - final Metadata.ExpressionMetadata charemd = metadata.getExpressionMetadata(ctx); - - if (ctx.CHAR() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - charemd.preConst = ctx.CHAR().getText().charAt(0); - charemd.from = definition.charType; + expression.processChar(ctx); return null; } @Override public Void visitTrue(final TrueContext ctx) { - final Metadata.ExpressionMetadata trueemd = metadata.getExpressionMetadata(ctx); - - if (ctx.TRUE() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - trueemd.preConst = true; - trueemd.from = definition.booleanType; + expression.processTrue(ctx); return null; } @Override public Void visitFalse(final FalseContext ctx) { - final Metadata.ExpressionMetadata falseemd = metadata.getExpressionMetadata(ctx); - - if (ctx.FALSE() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - falseemd.preConst = false; - falseemd.from = definition.booleanType; + expression.processFalse(ctx); return null; } @Override public Void visitNull(final NullContext ctx) { - final Metadata.ExpressionMetadata nullemd = metadata.getExpressionMetadata(ctx); - - if (ctx.NULL() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - nullemd.isNull = true; - - if (nullemd.to != null) { - if (nullemd.to.sort.primitive) { - throw new IllegalArgumentException("Cannot cast null to a primitive type [" + nullemd.to.name + "]."); - } - - nullemd.from = nullemd.to; - } else { - nullemd.from = definition.objectType; - } + expression.processNull(ctx); return null; } @Override public Void visitExternal(final ExternalContext ctx) { - final Metadata.ExpressionMetadata extemd = metadata.getExpressionMetadata(ctx); - - final ExtstartContext extstartctx = ctx.extstart(); - final Metadata.ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); - extstartemd.read = extemd.read; - visit(extstartctx); - - extemd.statement = extstartemd.statement; - extemd.preConst = extstartemd.constant; - extemd.from = extstartemd.current; - extemd.typesafe = extstartemd.current.sort != Sort.DEF; + expression.processExternal(ctx); return null; } @Override public Void visitPostinc(final PostincContext ctx) { - final Metadata.ExpressionMetadata postincemd = metadata.getExpressionMetadata(ctx); - - final ExtstartContext extstartctx = ctx.extstart(); - final Metadata.ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); - extstartemd.read = postincemd.read; - extstartemd.storeExpr = ctx.increment(); - extstartemd.token = ADD; - extstartemd.post = true; - visit(extstartctx); - - postincemd.statement = true; - postincemd.from = extstartemd.read ? extstartemd.current : definition.voidType; - postincemd.typesafe = extstartemd.current.sort != Sort.DEF; + expression.processPostinc(ctx); return null; } @Override public Void visitPreinc(final PreincContext ctx) { - final Metadata.ExpressionMetadata preincemd = metadata.getExpressionMetadata(ctx); - - final ExtstartContext extstartctx = ctx.extstart(); - final Metadata.ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); - extstartemd.read = preincemd.read; - extstartemd.storeExpr = ctx.increment(); - extstartemd.token = ADD; - extstartemd.pre = true; - visit(extstartctx); - - preincemd.statement = true; - preincemd.from = extstartemd.read ? extstartemd.current : definition.voidType; - preincemd.typesafe = extstartemd.current.sort != Sort.DEF; + expression.processPreinc(ctx); return null; } @Override public Void visitUnary(final UnaryContext ctx) { - final Metadata.ExpressionMetadata unaryemd = metadata.getExpressionMetadata(ctx); - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - - if (ctx.BOOLNOT() != null) { - expremd.to = definition.booleanType; - visit(exprctx); - markCast(expremd); - - if (expremd.postConst != null) { - unaryemd.preConst = !(boolean)expremd.postConst; - } - - unaryemd.from = definition.booleanType; - } else if (ctx.BWNOT() != null || ctx.ADD() != null || ctx.SUB() != null) { - visit(exprctx); - - final Type promote = promoteNumeric(expremd.from, ctx.BWNOT() == null, true); - - if (promote == null) { - throw new ClassCastException("Cannot apply [" + ctx.getChild(0).getText() + "] " + - "operation to type [" + expremd.from.name + "]."); - } - - expremd.to = promote; - markCast(expremd); - - if (expremd.postConst != null) { - final Sort sort = promote.sort; - - if (ctx.BWNOT() != null) { - if (sort == Sort.INT) { - unaryemd.preConst = ~(int)expremd.postConst; - } else if (sort == Sort.LONG) { - unaryemd.preConst = ~(long)expremd.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.SUB() != null) { - if (exprctx instanceof NumericContext) { - unaryemd.preConst = expremd.postConst; - } else { - if (sort == Sort.INT) { - if (settings.getNumericOverflow()) { - unaryemd.preConst = -(int)expremd.postConst; - } else { - unaryemd.preConst = Math.negateExact((int)expremd.postConst); - } - } else if (sort == Sort.LONG) { - if (settings.getNumericOverflow()) { - unaryemd.preConst = -(long)expremd.postConst; - } else { - unaryemd.preConst = Math.negateExact((long)expremd.postConst); - } - } else if (sort == Sort.FLOAT) { - unaryemd.preConst = -(float)expremd.postConst; - } else if (sort == Sort.DOUBLE) { - unaryemd.preConst = -(double)expremd.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - } else if (ctx.ADD() != null) { - if (sort == Sort.INT) { - unaryemd.preConst = +(int)expremd.postConst; - } else if (sort == Sort.LONG) { - unaryemd.preConst = +(long)expremd.postConst; - } else if (sort == Sort.FLOAT) { - unaryemd.preConst = +(float)expremd.postConst; - } else if (sort == Sort.DOUBLE) { - unaryemd.preConst = +(double)expremd.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - - unaryemd.from = promote; - unaryemd.typesafe = expremd.typesafe; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } + expression.processUnary(ctx); return null; } @Override public Void visitCast(final CastContext ctx) { - final Metadata.ExpressionMetadata castemd = metadata.getExpressionMetadata(ctx); - - final DecltypeContext decltypectx = ctx.decltype(); - final Metadata.ExpressionMetadata decltypemd = metadata.createExpressionMetadata(decltypectx); - visit(decltypectx); - - final Type type = decltypemd.from; - castemd.from = type; - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = type; - expremd.explicit = true; - visit(exprctx); - markCast(expremd); - - if (expremd.postConst != null) { - castemd.preConst = expremd.postConst; - } - - castemd.typesafe = expremd.typesafe && castemd.from.sort != Sort.DEF; + expression.processCast(ctx); return null; } @Override public Void visitBinary(final BinaryContext ctx) { - final Metadata.ExpressionMetadata binaryemd = metadata.getExpressionMetadata(ctx); - - final ExpressionContext exprctx0 = metadata.updateExpressionTree(ctx.expression(0)); - final Metadata.ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); - visit(exprctx0); - - final ExpressionContext exprctx1 = metadata.updateExpressionTree(ctx.expression(1)); - final Metadata.ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); - visit(exprctx1); - - final boolean decimal = ctx.MUL() != null || ctx.DIV() != null || ctx.REM() != null || ctx.SUB() != null; - final boolean add = ctx.ADD() != null; - final boolean xor = ctx.BWXOR() != null; - final Type promote = add ? promoteAdd(expremd0.from, expremd1.from) : - xor ? promoteXor(expremd0.from, expremd1.from) : - promoteNumeric(expremd0.from, expremd1.from, decimal, true); - - if (promote == null) { - throw new ClassCastException("Cannot apply [" + ctx.getChild(1).getText() + "] " + - "operation to types [" + expremd0.from.name + "] and [" + expremd1.from.name + "]."); - } - - final Sort sort = promote.sort; - expremd0.to = add && sort == Sort.STRING ? expremd0.from : promote; - expremd1.to = add && sort == Sort.STRING ? expremd1.from : promote; - markCast(expremd0); - markCast(expremd1); - - if (expremd0.postConst != null && expremd1.postConst != null) { - if (ctx.MUL() != null) { - if (sort == Sort.INT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (int)expremd0.postConst * (int)expremd1.postConst; - } else { - binaryemd.preConst = Math.multiplyExact((int)expremd0.postConst, (int)expremd1.postConst); - } - } else if (sort == Sort.LONG) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (long)expremd0.postConst * (long)expremd1.postConst; - } else { - binaryemd.preConst = Math.multiplyExact((long)expremd0.postConst, (long)expremd1.postConst); - } - } else if (sort == Sort.FLOAT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (float)expremd0.postConst * (float)expremd1.postConst; - } else { - binaryemd.preConst = Utility.multiplyWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); - } - } else if (sort == Sort.DOUBLE) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (double)expremd0.postConst * (double)expremd1.postConst; - } else { - binaryemd.preConst = Utility.multiplyWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.DIV() != null) { - if (sort == Sort.INT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (int)expremd0.postConst / (int)expremd1.postConst; - } else { - binaryemd.preConst = Utility.divideWithoutOverflow((int)expremd0.postConst, (int)expremd1.postConst); - } - } else if (sort == Sort.LONG) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (long)expremd0.postConst / (long)expremd1.postConst; - } else { - binaryemd.preConst = Utility.divideWithoutOverflow((long)expremd0.postConst, (long)expremd1.postConst); - } - } else if (sort == Sort.FLOAT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (float)expremd0.postConst / (float)expremd1.postConst; - } else { - binaryemd.preConst = Utility.divideWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); - } - } else if (sort == Sort.DOUBLE) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (double)expremd0.postConst / (double)expremd1.postConst; - } else { - binaryemd.preConst = Utility.divideWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.REM() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst % (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst % (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (float)expremd0.postConst % (float)expremd1.postConst; - } else { - binaryemd.preConst = Utility.remainderWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); - } - } else if (sort == Sort.DOUBLE) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (double)expremd0.postConst % (double)expremd1.postConst; - } else { - binaryemd.preConst = Utility.remainderWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.ADD() != null) { - if (sort == Sort.INT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (int)expremd0.postConst + (int)expremd1.postConst; - } else { - binaryemd.preConst = Math.addExact((int)expremd0.postConst, (int)expremd1.postConst); - } - } else if (sort == Sort.LONG) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (long)expremd0.postConst + (long)expremd1.postConst; - } else { - binaryemd.preConst = Math.addExact((long)expremd0.postConst, (long)expremd1.postConst); - } - } else if (sort == Sort.FLOAT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (float)expremd0.postConst + (float)expremd1.postConst; - } else { - binaryemd.preConst = Utility.addWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); - } - } else if (sort == Sort.DOUBLE) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (double)expremd0.postConst + (double)expremd1.postConst; - } else { - binaryemd.preConst = Utility.addWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); - } - } else if (sort == Sort.STRING) { - binaryemd.preConst = "" + expremd0.postConst + expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.SUB() != null) { - if (sort == Sort.INT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (int)expremd0.postConst - (int)expremd1.postConst; - } else { - binaryemd.preConst = Math.subtractExact((int)expremd0.postConst, (int)expremd1.postConst); - } - } else if (sort == Sort.LONG) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (long)expremd0.postConst - (long)expremd1.postConst; - } else { - binaryemd.preConst = Math.subtractExact((long)expremd0.postConst, (long)expremd1.postConst); - } - } else if (sort == Sort.FLOAT) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (float)expremd0.postConst - (float)expremd1.postConst; - } else { - binaryemd.preConst = Utility.subtractWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); - } - } else if (sort == Sort.DOUBLE) { - if (settings.getNumericOverflow()) { - binaryemd.preConst = (double)expremd0.postConst - (double)expremd1.postConst; - } else { - binaryemd.preConst = Utility.subtractWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.LSH() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst << (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst << (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.RSH() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst >> (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst >> (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.USH() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst >>> (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst >>> (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.BWAND() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst & (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst & (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.BWXOR() != null) { - if (sort == Sort.BOOL) { - binaryemd.preConst = (boolean)expremd0.postConst ^ (boolean)expremd1.postConst; - } else if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst ^ (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst ^ (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else if (ctx.BWOR() != null) { - if (sort == Sort.INT) { - binaryemd.preConst = (int)expremd0.postConst | (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - binaryemd.preConst = (long)expremd0.postConst | (long)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - - binaryemd.from = promote; - binaryemd.typesafe = expremd0.typesafe && expremd1.typesafe; + expression.processBinary(ctx); return null; } @Override public Void visitComp(final CompContext ctx) { - final Metadata.ExpressionMetadata compemd = metadata.getExpressionMetadata(ctx); - final boolean equality = ctx.EQ() != null || ctx.NE() != null; - final boolean reference = ctx.EQR() != null || ctx.NER() != null; - - final ExpressionContext exprctx0 = metadata.updateExpressionTree(ctx.expression(0)); - final Metadata.ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); - visit(exprctx0); - - final ExpressionContext exprctx1 = metadata.updateExpressionTree(ctx.expression(1)); - final Metadata.ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); - visit(exprctx1); - - if (expremd0.isNull && expremd1.isNull) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unnecessary comparison of null constants."); - } - - final Type promote = equality ? promoteEquality(expremd0.from, expremd1.from) : - reference ? promoteReference(expremd0.from, expremd1.from) : - promoteNumeric(expremd0.from, expremd1.from, true, true); - - if (promote == null) { - throw new ClassCastException("Cannot apply [" + ctx.getChild(1).getText() + "] " + - "operation to types [" + expremd0.from.name + "] and [" + expremd1.from.name + "]."); - } - - expremd0.to = promote; - expremd1.to = promote; - markCast(expremd0); - markCast(expremd1); - - if (expremd0.postConst != null && expremd1.postConst != null) { - final Sort sort = promote.sort; - - if (ctx.EQ() != null || ctx.EQR() != null) { - if (sort == Sort.BOOL) { - compemd.preConst = (boolean)expremd0.postConst == (boolean)expremd1.postConst; - } else if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst == (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst == (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst == (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst == (double)expremd1.postConst; - } else { - if (ctx.EQ() != null && !expremd0.isNull && !expremd1.isNull) { - compemd.preConst = expremd0.postConst.equals(expremd1.postConst); - } else if (ctx.EQR() != null) { - compemd.preConst = expremd0.postConst == expremd1.postConst; - } - } - } else if (ctx.NE() != null || ctx.NER() != null) { - if (sort == Sort.BOOL) { - compemd.preConst = (boolean)expremd0.postConst != (boolean)expremd1.postConst; - } else if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst != (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst != (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst != (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst != (double)expremd1.postConst; - } else { - if (ctx.NE() != null && !expremd0.isNull && !expremd1.isNull) { - compemd.preConst = expremd0.postConst.equals(expremd1.postConst); - } else if (ctx.NER() != null) { - compemd.preConst = expremd0.postConst == expremd1.postConst; - } - } - } else if (ctx.GTE() != null) { - if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst >= (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst >= (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst >= (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst >= (double)expremd1.postConst; - } - } else if (ctx.GT() != null) { - if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst > (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst > (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst > (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst > (double)expremd1.postConst; - } - } else if (ctx.LTE() != null) { - if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst <= (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst <= (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst <= (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst <= (double)expremd1.postConst; - } - } else if (ctx.LT() != null) { - if (sort == Sort.INT) { - compemd.preConst = (int)expremd0.postConst < (int)expremd1.postConst; - } else if (sort == Sort.LONG) { - compemd.preConst = (long)expremd0.postConst < (long)expremd1.postConst; - } else if (sort == Sort.FLOAT) { - compemd.preConst = (float)expremd0.postConst < (float)expremd1.postConst; - } else if (sort == Sort.DOUBLE) { - compemd.preConst = (double)expremd0.postConst < (double)expremd1.postConst; - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - - compemd.from = definition.booleanType; - compemd.typesafe = expremd0.typesafe && expremd1.typesafe; + expression.processComp(ctx); return null; } @Override public Void visitBool(final BoolContext ctx) { - final Metadata.ExpressionMetadata boolemd = metadata.getExpressionMetadata(ctx); - - final ExpressionContext exprctx0 = metadata.updateExpressionTree(ctx.expression(0)); - final Metadata.ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); - expremd0.to = definition.booleanType; - visit(exprctx0); - markCast(expremd0); - - final ExpressionContext exprctx1 = metadata.updateExpressionTree(ctx.expression(1)); - final Metadata.ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); - expremd1.to = definition.booleanType; - visit(exprctx1); - markCast(expremd1); - - if (expremd0.postConst != null && expremd1.postConst != null) { - if (ctx.BOOLAND() != null) { - boolemd.preConst = (boolean)expremd0.postConst && (boolean)expremd1.postConst; - } else if (ctx.BOOLOR() != null) { - boolemd.preConst = (boolean)expremd0.postConst || (boolean)expremd1.postConst; - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - - boolemd.from = definition.booleanType; - boolemd.typesafe = expremd0.typesafe && expremd1.typesafe; + expression.processBool(ctx); return null; } @Override public Void visitConditional(final ConditionalContext ctx) { - final Metadata.ExpressionMetadata condemd = metadata.getExpressionMetadata(ctx); - - final ExpressionContext exprctx0 = metadata.updateExpressionTree(ctx.expression(0)); - final Metadata.ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); - expremd0.to = definition.booleanType; - visit(exprctx0); - markCast(expremd0); - - if (expremd0.postConst != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unnecessary conditional statement."); - } - - final ExpressionContext exprctx1 = metadata.updateExpressionTree(ctx.expression(1)); - final Metadata.ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); - expremd1.to = condemd.to; - expremd1.explicit = condemd.explicit; - visit(exprctx1); - - final ExpressionContext exprctx2 = metadata.updateExpressionTree(ctx.expression(2)); - final Metadata.ExpressionMetadata expremd2 = metadata.createExpressionMetadata(exprctx2); - expremd2.to = condemd.to; - expremd2.explicit = condemd.explicit; - visit(exprctx2); - - if (condemd.to == null) { - final Type promote = promoteConditional(expremd1.from, expremd2.from, expremd1.preConst, expremd2.preConst); - - expremd1.to = promote; - expremd2.to = promote; - condemd.from = promote; - } else { - condemd.from = condemd.to; - } - - markCast(expremd1); - markCast(expremd2); - - condemd.typesafe = expremd0.typesafe && expremd1.typesafe; + expression.processConditional(ctx); return null; } @Override public Void visitAssignment(final AssignmentContext ctx) { - final Metadata.ExpressionMetadata assignemd = metadata.getExpressionMetadata(ctx); - - final ExtstartContext extstartctx = ctx.extstart(); - final Metadata.ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); - - extstartemd.read = assignemd.read; - extstartemd.storeExpr = metadata.updateExpressionTree(ctx.expression()); - - if (ctx.AMUL() != null) { - extstartemd.token = MUL; - } else if (ctx.ADIV() != null) { - extstartemd.token = DIV; - } else if (ctx.AREM() != null) { - extstartemd.token = REM; - } else if (ctx.AADD() != null) { - extstartemd.token = ADD; - } else if (ctx.ASUB() != null) { - extstartemd.token = SUB; - } else if (ctx.ALSH() != null) { - extstartemd.token = LSH; - } else if (ctx.AUSH() != null) { - extstartemd.token = USH; - } else if (ctx.ARSH() != null) { - extstartemd.token = RSH; - } else if (ctx.AAND() != null) { - extstartemd.token = BWAND; - } else if (ctx.AXOR() != null) { - extstartemd.token = BWXOR; - } else if (ctx.AOR() != null) { - extstartemd.token = BWOR; - } - - visit(extstartctx); - - assignemd.statement = true; - assignemd.from = extstartemd.read ? extstartemd.current : definition.voidType; - assignemd.typesafe = extstartemd.current.sort != Sort.DEF; + expression.processAssignment(ctx); return null; } @Override public Void visitExtstart(final ExtstartContext ctx) { - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - if (precctx != null) { - metadata.createExtNodeMetadata(ctx, precctx); - visit(precctx); - } else if (castctx != null) { - metadata.createExtNodeMetadata(ctx, castctx); - visit(castctx); - } else if (typectx != null) { - metadata.createExtNodeMetadata(ctx, typectx); - visit(typectx); - } else if (varctx != null) { - metadata.createExtNodeMetadata(ctx, varctx); - visit(varctx); - } else if (newctx != null) { - metadata.createExtNodeMetadata(ctx, newctx); - visit(newctx); - } else if (stringctx != null) { - metadata.createExtNodeMetadata(ctx, stringctx); - visit(stringctx); - } else { - throw new IllegalStateException(); - } + external.processExtstart(ctx); return null; } @Override public Void visitExtprec(final ExtprecContext ctx) { - final Metadata.ExtNodeMetadata precenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = precenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null || bracectx != null) { - ++parentemd.scope; - } - - if (precctx != null) { - metadata.createExtNodeMetadata(parent, precctx); - visit(precctx); - } else if (castctx != null) { - metadata.createExtNodeMetadata(parent, castctx); - visit(castctx); - } else if (typectx != null) { - metadata.createExtNodeMetadata(parent, typectx); - visit(typectx); - } else if (varctx != null) { - metadata.createExtNodeMetadata(parent, varctx); - visit(varctx); - } else if (newctx != null) { - metadata.createExtNodeMetadata(parent, newctx); - visit(newctx); - } else if (stringctx != null) { - metadata.createExtNodeMetadata(ctx, stringctx); - visit(stringctx); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - parentemd.statement = false; - - if (dotctx != null) { - --parentemd.scope; - - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - --parentemd.scope; - - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + external.processExtprec(ctx); return null; } @Override public Void visitExtcast(final ExtcastContext ctx) { - final Metadata.ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = castenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - if (precctx != null) { - metadata.createExtNodeMetadata(parent, precctx); - visit(precctx); - } else if (castctx != null) { - metadata.createExtNodeMetadata(parent, castctx); - visit(castctx); - } else if (typectx != null) { - metadata.createExtNodeMetadata(parent, typectx); - visit(typectx); - } else if (varctx != null) { - metadata.createExtNodeMetadata(parent, varctx); - visit(varctx); - } else if (newctx != null) { - metadata.createExtNodeMetadata(parent, newctx); - visit(newctx); - } else if (stringctx != null) { - metadata.createExtNodeMetadata(ctx, stringctx); - visit(stringctx); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - final DecltypeContext declctx = ctx.decltype(); - final Metadata.ExpressionMetadata declemd = metadata.createExpressionMetadata(declctx); - visit(declctx); - - castenmd.castTo = getLegalCast(ctx, parentemd.current, declemd.from, true); - castenmd.type = declemd.from; - parentemd.current = declemd.from; - parentemd.statement = false; + external.processExtcast(ctx); return null; } @Override public Void visitExtbrace(final ExtbraceContext ctx) { - final Metadata.ExtNodeMetadata braceenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = braceenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final boolean array = parentemd.current.sort == Sort.ARRAY; - final boolean def = parentemd.current.sort == Sort.DEF; - boolean map = false; - boolean list = false; - - try { - parentemd.current.clazz.asSubclass(Map.class); - map = true; - } catch (ClassCastException exception) { - // Do nothing. - } - - try { - parentemd.current.clazz.asSubclass(List.class); - list = true; - } catch (ClassCastException exception) { - // Do nothing. - } - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - braceenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - - if (array || def) { - expremd.to = array ? definition.intType : definition.objectType; - visit(exprctx); - markCast(expremd); - - braceenmd.target = "#brace"; - braceenmd.type = def ? definition.defType : - definition.getType(parentemd.current.struct, parentemd.current.type.getDimensions() - 1); - analyzeLoadStoreExternal(ctx); - parentemd.current = braceenmd.type; - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } - } else { - final boolean store = braceenmd.last && parentemd.storeExpr != null; - final boolean get = parentemd.read || parentemd.token > 0 || !braceenmd.last; - final boolean set = braceenmd.last && store; - - Method getter; - Method setter; - Type valuetype; - Type settype; - - if (map) { - getter = parentemd.current.struct.methods.get("get"); - setter = parentemd.current.struct.methods.get("put"); - - if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal map get shortcut for type [" + parentemd.current.name + "]."); - } - - if (setter != null && setter.arguments.size() != 2) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal map set shortcut for type [" + parentemd.current.name + "]."); - } - - if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0)) - || !getter.rtn.equals(setter.arguments.get(1)))) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Shortcut argument types must match."); - } - - valuetype = setter != null ? setter.arguments.get(0) : getter != null ? getter.arguments.get(0) : null; - settype = setter == null ? null : setter.arguments.get(1); - } else if (list) { - getter = parentemd.current.struct.methods.get("get"); - setter = parentemd.current.struct.methods.get("set"); - - if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || - getter.arguments.get(0).sort != Sort.INT)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal list get shortcut for type [" + parentemd.current.name + "]."); - } - - if (setter != null && (setter.arguments.size() != 2 || setter.arguments.get(0).sort != Sort.INT)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal list set shortcut for type [" + parentemd.current.name + "]."); - } - - if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0)) - || !getter.rtn.equals(setter.arguments.get(1)))) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Shortcut argument types must match."); - } - - valuetype = definition.intType; - settype = setter == null ? null : setter.arguments.get(1); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - - if ((get || set) && (!get || getter != null) && (!set || setter != null)) { - expremd.to = valuetype; - visit(exprctx); - markCast(expremd); - - braceenmd.target = new Object[] {getter, setter, true, null}; - braceenmd.type = get ? getter.rtn : settype; - analyzeLoadStoreExternal(ctx); - parentemd.current = get ? getter.rtn : setter.rtn; - } - } - - if (braceenmd.target == null) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Attempting to address a non-array type [" + parentemd.current.name + "] as an array."); - } + external.processExtbrace(ctx); return null; } @Override public Void visitExtdot(final ExtdotContext ctx) { - final Metadata.ExtNodeMetadata dotemnd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = dotemnd.parent; - - final ExtcallContext callctx = ctx.extcall(); - final ExtfieldContext fieldctx = ctx.extfield(); - - if (callctx != null) { - metadata.createExtNodeMetadata(parent, callctx); - visit(callctx); - } else if (fieldctx != null) { - metadata.createExtNodeMetadata(parent, fieldctx); - visit(fieldctx); - } + external.processExtdot(ctx); return null; } @Override public Void visitExttype(final ExttypeContext ctx) { - final Metadata.ExtNodeMetadata typeenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = typeenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - if (parentemd.current != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unexpected static type."); - } - - final String typestr = ctx.TYPE().getText(); - typeenmd.type = definition.getType(typestr); - parentemd.current = typeenmd.type; - parentemd.statik = true; - - final ExtdotContext dotctx = ctx.extdot(); - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); + external.processExttype(ctx); return null; } @Override public Void visitExtcall(final ExtcallContext ctx) { - final Metadata.ExtNodeMetadata callenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = callenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - callenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - - final String name = ctx.EXTID().getText(); - - if (parentemd.current.sort == Sort.ARRAY) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unexpected call [" + name + "] on an array."); - } else if (callenmd.last && parentemd.storeExpr != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Cannot assign a value to a call [" + name + "]."); - } - - final Struct struct = parentemd.current.struct; - final List arguments = ctx.arguments().expression(); - final int size = arguments.size(); - Type[] types; - - final Method method = parentemd.statik ? struct.functions.get(name) : struct.methods.get(name); - final boolean def = parentemd.current.sort == Sort.DEF; - - if (method == null && !def) { - throw new IllegalArgumentException( - Metadata.error(ctx) + "Unknown call [" + name + "] on type [" + struct.name + "]."); - } else if (method != null) { - types = new Type[method.arguments.size()]; - method.arguments.toArray(types); - - callenmd.target = method; - callenmd.type = method.rtn; - parentemd.statement = !parentemd.read && callenmd.last; - parentemd.current = method.rtn; - - if (size != types.length) { - throw new IllegalArgumentException(Metadata.error(ctx) + "When calling [" + name + "] on type " + - "[" + struct.name + "] expected [" + types.length + "] arguments," + - " but found [" + arguments.size() + "]."); - } - } else { - types = new Type[arguments.size()]; - Arrays.fill(types, definition.defType); - - callenmd.target = name; - callenmd.type = definition.defType; - parentemd.statement = !parentemd.read && callenmd.last; - parentemd.current = callenmd.type; - } - - for (int argument = 0; argument < size; ++argument) { - final ExpressionContext exprctx = metadata.updateExpressionTree(arguments.get(argument)); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = types[argument]; - visit(exprctx); - markCast(expremd); - } - - parentemd.statik = false; - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + external.processExtcall(ctx); return null; } @Override public Void visitExtvar(final ExtvarContext ctx) { - final Metadata.ExtNodeMetadata varenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = varenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final String name = ctx.ID().getText(); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (parentemd.current != null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected variable [" + name + "] load."); - } - - varenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - - final Variable variable = getVariable(name); - - if (variable == null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unknown variable [" + name + "]."); - } - - varenmd.target = variable.slot; - varenmd.type = variable.type; - analyzeLoadStoreExternal(ctx); - parentemd.current = varenmd.type; - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + external.processExtvar(ctx); return null; } @Override public Void visitExtfield(final ExtfieldContext ctx) { - final Metadata.ExtNodeMetadata memberenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = memberenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - if (ctx.EXTID() == null && ctx.EXTINTEGER() == null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unexpected parser state."); - } - - final String value = ctx.EXTID() == null ? ctx.EXTINTEGER().getText() : ctx.EXTID().getText(); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - memberenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - final boolean store = memberenmd.last && parentemd.storeExpr != null; - - if (parentemd.current == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected field [" + value + "] load."); - } - - if (parentemd.current.sort == Sort.ARRAY) { - if ("length".equals(value)) { - if (!parentemd.read) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Must read array field [length]."); - } else if (store) { - throw new IllegalArgumentException( - Metadata.error(ctx) + "Cannot write to read-only array field [length]."); - } - - memberenmd.target = "#length"; - memberenmd.type = definition.intType; - parentemd.current = definition.intType; - } else { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unexpected array field [" + value + "]."); - } - } else if (parentemd.current.sort == Sort.DEF) { - memberenmd.target = value; - memberenmd.type = definition.defType; - analyzeLoadStoreExternal(ctx); - parentemd.current = memberenmd.type; - } else { - final Struct struct = parentemd.current.struct; - final Field field = parentemd.statik ? struct.statics.get(value) : struct.members.get(value); - - if (field != null) { - if (store && java.lang.reflect.Modifier.isFinal(field.reflect.getModifiers())) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Cannot write to read-only" + - " field [" + value + "] for type [" + struct.name + "]."); - } - - memberenmd.target = field; - memberenmd.type = field.type; - analyzeLoadStoreExternal(ctx); - parentemd.current = memberenmd.type; - } else { - final boolean get = parentemd.read || parentemd.token > 0 || !memberenmd.last; - final boolean set = memberenmd.last && store; - - Method getter = struct.methods.get("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1)); - Method setter = struct.methods.get("set" + Character.toUpperCase(value.charAt(0)) + value.substring(1)); - Object constant = null; - - if (getter != null && (getter.rtn.sort == Sort.VOID || !getter.arguments.isEmpty())) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal get shortcut on field [" + value + "] for type [" + struct.name + "]."); - } - - if (setter != null && (setter.rtn.sort != Sort.VOID || setter.arguments.size() != 1)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal set shortcut on field [" + value + "] for type [" + struct.name + "]."); - } - - Type settype = setter == null ? null : setter.arguments.get(0); - - if (getter == null && setter == null) { - if (ctx.EXTID() != null) { - try { - parentemd.current.clazz.asSubclass(Map.class); - - getter = parentemd.current.struct.methods.get("get"); - setter = parentemd.current.struct.methods.get("put"); - - if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || - getter.arguments.get(0).sort != Sort.STRING)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal map get shortcut [" + value + "] for type [" + struct.name + "]."); - } - - if (setter != null && (setter.arguments.size() != 2 || - setter.arguments.get(0).sort != Sort.STRING)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal map set shortcut [" + value + "] for type [" + struct.name + "]."); - } - - if (getter != null && setter != null && !getter.rtn.equals(setter.arguments.get(1))) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Shortcut argument types must match."); - } - - settype = setter == null ? null : setter.arguments.get(1); - constant = value; - } catch (ClassCastException exception) { - //Do nothing. - } - } else if (ctx.EXTINTEGER() != null) { - try { - parentemd.current.clazz.asSubclass(List.class); - - getter = parentemd.current.struct.methods.get("get"); - setter = parentemd.current.struct.methods.get("set"); - - if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || - getter.arguments.get(0).sort != Sort.INT)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal list get shortcut [" + value + "] for type [" + struct.name + "]."); - } - - if (setter != null && (setter.rtn.sort != Sort.VOID || setter.arguments.size() != 2 || - setter.arguments.get(0).sort != Sort.INT)) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal list set shortcut [" + value + "] for type [" + struct.name + "]."); - } - - if (getter != null && setter != null && !getter.rtn.equals(setter.arguments.get(1))) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Shortcut argument types must match."); - } - - settype = setter == null ? null : setter.arguments.get(1); - - try { - constant = Integer.parseInt(value); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException(Metadata.error(ctx) + - "Illegal list shortcut value [" + value + "]."); - } - } catch (ClassCastException exception) { - //Do nothing. - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected parser state."); - } - } - - if ((get || set) && (!get || getter != null) && (!set || setter != null)) { - memberenmd.target = new Object[] {getter, setter, constant != null, constant}; - memberenmd.type = get ? getter.rtn : settype; - analyzeLoadStoreExternal(ctx); - parentemd.current = get ? getter.rtn : setter.rtn; - } - } - - if (memberenmd.target == null) { - throw new IllegalArgumentException( - Metadata.error(ctx) + "Unknown field [" + value + "] for type [" + struct.name + "]."); - } - } - - parentemd.statik = false; - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + external.processExtfield(ctx); return null; } @Override - public Void visitExtnew(ExtnewContext ctx) { - final Metadata.ExtNodeMetadata newenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = newenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - newenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - - final String name = ctx.TYPE().getText(); - final Struct struct = definition.structs.get(name); - - if (parentemd.current != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unexpected new call."); - } else if (struct == null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Specified type [" + name + "] not found."); - } else if (newenmd.last && parentemd.storeExpr != null) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Cannot assign a value to a new call."); - } - - final boolean newclass = ctx.arguments() != null; - final boolean newarray = !ctx.expression().isEmpty(); - - final List arguments = newclass ? ctx.arguments().expression() : ctx.expression(); - final int size = arguments.size(); - - Type[] types; - - if (newarray) { - if (!parentemd.read) { - throw new IllegalArgumentException(Metadata.error(ctx) + "A newly created array must be assigned."); - } - - types = new Type[size]; - Arrays.fill(types, definition.intType); - - newenmd.target = "#makearray"; - - if (size > 1) { - newenmd.type = definition.getType(struct, size); - parentemd.current = newenmd.type; - } else if (size == 1) { - newenmd.type = definition.getType(struct, 0); - parentemd.current = definition.getType(struct, 1); - } else { - throw new IllegalArgumentException(Metadata.error(ctx) + "A newly created array cannot have zero dimensions."); - } - } else if (newclass) { - final Constructor constructor = struct.constructors.get("new"); - - if (constructor != null) { - types = new Type[constructor.arguments.size()]; - constructor.arguments.toArray(types); - - newenmd.target = constructor; - newenmd.type = definition.getType(struct, 0); - parentemd.statement = !parentemd.read && newenmd.last; - parentemd.current = newenmd.type; - } else { - throw new IllegalArgumentException( - Metadata.error(ctx) + "Unknown new call on type [" + struct.name + "]."); - } - } else { - throw new IllegalArgumentException(Metadata.error(ctx) + "Unknown parser state."); - } - - if (size != types.length) { - throw new IllegalArgumentException(Metadata.error(ctx) + "When calling [" + name + "] on type " + - "[" + struct.name + "] expected [" + types.length + "] arguments," + - " but found [" + arguments.size() + "]."); - } - - for (int argument = 0; argument < size; ++argument) { - final ExpressionContext exprctx = metadata.updateExpressionTree(arguments.get(argument)); - final Metadata.ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); - expremd.to = types[argument]; - visit(exprctx); - markCast(expremd); - } - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + public Void visitExtnew(final ExtnewContext ctx) { + external.processExtnew(ctx); return null; } @Override public Void visitExtstring(final ExtstringContext ctx) { - final Metadata.ExtNodeMetadata memberenmd = metadata.getExtNodeMetadata(ctx); - final ParserRuleContext parent = memberenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - final String string = ctx.STRING().getText(); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - memberenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; - final boolean store = memberenmd.last && parentemd.storeExpr != null; - - if (parentemd.current != null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected String constant [" + string + "]."); - } - - if (!parentemd.read) { - throw new IllegalArgumentException(Metadata.error(ctx) + "Must read String constant [" + string + "]."); - } else if (store) { - throw new IllegalArgumentException( - Metadata.error(ctx) + "Cannot write to read-only String constant [" + string + "]."); - } - - memberenmd.target = string; - memberenmd.type = definition.stringType; - parentemd.current = definition.stringType; - - if (memberenmd.last) { - parentemd.constant = string; - } - - if (dotctx != null) { - metadata.createExtNodeMetadata(parent, dotctx); - visit(dotctx); - } else if (bracectx != null) { - metadata.createExtNodeMetadata(parent, bracectx); - visit(bracectx); - } + external.processExtstring(ctx); return null; } @Override public Void visitArguments(final ArgumentsContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected parser state."); + throw new UnsupportedOperationException(AnalyzerUtility.error(ctx) + "Unexpected state."); } @Override - public Void visitIncrement(IncrementContext ctx) { - final Metadata.ExpressionMetadata incremd = metadata.getExpressionMetadata(ctx); - final Sort sort = incremd.to == null ? null : incremd.to.sort; - final boolean positive = ctx.INCR() != null; - - if (incremd.to == null) { - incremd.preConst = positive ? 1 : -1; - incremd.from = definition.intType; - } else { - switch (sort) { - case LONG: - incremd.preConst = positive ? 1L : -1L; - incremd.from = definition.longType; - break; - case FLOAT: - incremd.preConst = positive ? 1.0F : -1.0F; - incremd.from = definition.floatType; - break; - case DOUBLE: - incremd.preConst = positive ? 1.0 : -1.0; - incremd.from = definition.doubleType; - break; - default: - incremd.preConst = positive ? 1 : -1; - incremd.from = definition.intType; - } - } + public Void visitIncrement(final IncrementContext ctx) { + expression.processIncrement(ctx); return null; } - - private void analyzeLoadStoreExternal(final ParserRuleContext source) { - final Metadata.ExtNodeMetadata extenmd = metadata.getExtNodeMetadata(source); - final ParserRuleContext parent = extenmd.parent; - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(parent); - - if (extenmd.last && parentemd.storeExpr != null) { - final ParserRuleContext store = parentemd.storeExpr; - final Metadata.ExpressionMetadata storeemd = metadata.createExpressionMetadata(parentemd.storeExpr); - final int token = parentemd.token; - - if (token > 0) { - visit(store); - - final boolean add = token == ADD; - final boolean xor = token == BWAND || token == BWXOR || token == BWOR; - final boolean decimal = token == MUL || token == DIV || token == REM || token == SUB; - - extenmd.promote = add ? promoteAdd(extenmd.type, storeemd.from) : - xor ? promoteXor(extenmd.type, storeemd.from) : - promoteNumeric(extenmd.type, storeemd.from, decimal, true); - - if (extenmd.promote == null) { - throw new IllegalArgumentException("Cannot apply compound assignment to " + - "types [" + extenmd.type.name + "] and [" + storeemd.from.name + "]."); - } - - extenmd.castFrom = getLegalCast(source, extenmd.type, extenmd.promote, false); - extenmd.castTo = getLegalCast(source, extenmd.promote, extenmd.type, true); - - storeemd.to = add && extenmd.promote.sort == Sort.STRING ? storeemd.from : extenmd.promote; - markCast(storeemd); - } else { - storeemd.to = extenmd.type; - visit(store); - markCast(storeemd); - } - } - } - - private void markCast(final Metadata.ExpressionMetadata emd) { - if (emd.from == null) { - throw new IllegalStateException(Metadata.error(emd.source) + "From cast type should never be null."); - } - - if (emd.to != null) { - emd.cast = getLegalCast(emd.source, emd.from, emd.to, emd.explicit || !emd.typesafe); - - if (emd.preConst != null && emd.to.sort.constant) { - emd.postConst = constCast(emd.source, emd.preConst, emd.cast); - } - } else { - throw new IllegalStateException(Metadata.error(emd.source) + "To cast type should never be null."); - } - } - - private Cast getLegalCast(final ParserRuleContext source, final Type from, final Type to, final boolean explicit) { - final Cast cast = new Cast(from, to); - - if (from.equals(to)) { - return cast; - } - - if (from.sort == Sort.DEF && to.sort != Sort.VOID || from.sort != Sort.VOID && to.sort == Sort.DEF) { - final Transform transform = definition.transforms.get(cast); - - if (transform != null) { - return transform; - } - - return cast; - } - - switch (from.sort) { - case BOOL: - switch (to.sort) { - case OBJECT: - case BOOL_OBJ: - return checkTransform(source, cast); - } - - break; - case BYTE: - switch (to.sort) { - case SHORT: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - return cast; - case CHAR: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case BYTE_OBJ: - case SHORT_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case SHORT: - switch (to.sort) { - case INT: - case LONG: - case FLOAT: - case DOUBLE: - return cast; - case BYTE: - case CHAR: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case SHORT_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case CHAR: - switch (to.sort) { - case INT: - case LONG: - case FLOAT: - case DOUBLE: - return cast; - case BYTE: - case SHORT: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case CHAR_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case SHORT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case INT: - switch (to.sort) { - case LONG: - case FLOAT: - case DOUBLE: - return cast; - case BYTE: - case SHORT: - case CHAR: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case LONG: - switch (to.sort) { - case FLOAT: - case DOUBLE: - return cast; - case BYTE: - case SHORT: - case CHAR: - case INT: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case FLOAT: - switch (to.sort) { - case DOUBLE: - return cast; - case BYTE: - case SHORT: - case CHAR: - case INT: - case LONG: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - case LONG_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case DOUBLE: - switch (to.sort) { - case BYTE: - case SHORT: - case CHAR: - case INT: - case LONG: - case FLOAT: - if (explicit) - return cast; - - break; - case OBJECT: - case NUMBER: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case OBJECT: - case NUMBER: - switch (to.sort) { - case BYTE: - case SHORT: - case CHAR: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case BOOL_OBJ: - switch (to.sort) { - case BOOL: - return checkTransform(source, cast); - } - - break; - case BYTE_OBJ: - switch (to.sort) { - case BYTE: - case SHORT: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - case SHORT_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case CHAR: - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case SHORT_OBJ: - switch (to.sort) { - case SHORT: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE: - case CHAR: - case BYTE_OBJ: - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case CHAR_OBJ: - switch (to.sort) { - case CHAR: - case INT: - case LONG: - case FLOAT: - case DOUBLE: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE: - case SHORT: - case BYTE_OBJ: - case SHORT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case INT_OBJ: - switch (to.sort) { - case INT: - case LONG: - case FLOAT: - case DOUBLE: - case LONG_OBJ: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE: - case SHORT: - case CHAR: - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case LONG_OBJ: - switch (to.sort) { - case LONG: - case FLOAT: - case DOUBLE: - case FLOAT_OBJ: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE: - case SHORT: - case CHAR: - case INT: - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case FLOAT_OBJ: - switch (to.sort) { - case FLOAT: - case DOUBLE: - case DOUBLE_OBJ: - return checkTransform(source, cast); - case BYTE: - case SHORT: - case CHAR: - case INT: - case LONG: - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - case LONG_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - case DOUBLE_OBJ: - switch (to.sort) { - case DOUBLE: - return checkTransform(source, cast); - case BYTE: - case SHORT: - case CHAR: - case INT: - case LONG: - case FLOAT: - case BYTE_OBJ: - case SHORT_OBJ: - case CHAR_OBJ: - case INT_OBJ: - case LONG_OBJ: - case FLOAT_OBJ: - if (explicit) - return checkTransform(source, cast); - - break; - } - - break; - } - - try { - from.clazz.asSubclass(to.clazz); - - return cast; - } catch (final ClassCastException cce0) { - try { - if (explicit) { - to.clazz.asSubclass(from.clazz); - - return cast; - } else { - throw new ClassCastException( - Metadata.error(source) + "Cannot cast from [" + from.name + "] to [" + to.name + "]."); - } - } catch (final ClassCastException cce1) { - throw new ClassCastException( - Metadata.error(source) + "Cannot cast from [" + from.name + "] to [" + to.name + "]."); - } - } - } - - private Transform checkTransform(final ParserRuleContext source, final Cast cast) { - final Transform transform = definition.transforms.get(cast); - - if (transform == null) { - throw new ClassCastException( - Metadata.error(source) + "Cannot cast from [" + cast.from.name + "] to [" + cast.to.name + "]."); - } - - return transform; - } - - private Object constCast(final ParserRuleContext source, final Object constant, final Cast cast) { - if (cast instanceof Transform) { - final Transform transform = (Transform)cast; - return invokeTransform(source, transform, constant); - } else { - final Sort fsort = cast.from.sort; - final Sort tsort = cast.to.sort; - - if (fsort == tsort) { - return constant; - } else if (fsort.numeric && tsort.numeric) { - Number number; - - if (fsort == Sort.CHAR) { - number = (int)(char)constant; - } else { - number = (Number)constant; - } - - switch (tsort) { - case BYTE: return number.byteValue(); - case SHORT: return number.shortValue(); - case CHAR: return (char)number.intValue(); - case INT: return number.intValue(); - case LONG: return number.longValue(); - case FLOAT: return number.floatValue(); - case DOUBLE: return number.doubleValue(); - default: - throw new IllegalStateException(Metadata.error(source) + "Expected numeric type for cast."); - } - } else { - throw new IllegalStateException(Metadata.error(source) + "No valid constant cast from " + - "[" + cast.from.clazz.getCanonicalName() + "] to " + - "[" + cast.to.clazz.getCanonicalName() + "]."); - } - } - } - - private Object invokeTransform(final ParserRuleContext source, final Transform transform, final Object object) { - final Method method = transform.method; - final java.lang.reflect.Method jmethod = method.reflect; - final int modifiers = jmethod.getModifiers(); - - try { - if (java.lang.reflect.Modifier.isStatic(modifiers)) { - return jmethod.invoke(null, object); - } else { - return jmethod.invoke(object); - } - } catch (IllegalAccessException | IllegalArgumentException | - java.lang.reflect.InvocationTargetException | NullPointerException | - ExceptionInInitializerError exception) { - throw new IllegalStateException(Metadata.error(source) + "Unable to invoke transform to cast constant from " + - "[" + transform.from.name + "] to [" + transform.to.name + "]."); - } - } - - private Type promoteNumeric(final Type from, boolean decimal, boolean primitive) { - final Sort sort = from.sort; - - if (sort == Sort.DEF) { - return definition.defType; - } else if ((sort == Sort.DOUBLE || sort == Sort.DOUBLE_OBJ || sort == Sort.NUMBER) && decimal) { - return primitive ? definition.doubleType : definition.doubleobjType; - } else if ((sort == Sort.FLOAT || sort == Sort.FLOAT_OBJ) && decimal) { - return primitive ? definition.floatType : definition.floatobjType; - } else if (sort == Sort.LONG || sort == Sort.LONG_OBJ || sort == Sort.NUMBER) { - return primitive ? definition.longType : definition.longobjType; - } else if (sort.numeric) { - return primitive ? definition.intType : definition.intobjType; - } - - return null; - } - - private Type promoteNumeric(final Type from0, final Type from1, boolean decimal, boolean primitive) { - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0 == Sort.DEF || sort1 == Sort.DEF) { - return definition.defType; - } - - if (decimal) { - if (sort0 == Sort.DOUBLE || sort0 == Sort.DOUBLE_OBJ || sort0 == Sort.NUMBER || - sort1 == Sort.DOUBLE || sort1 == Sort.DOUBLE_OBJ || sort1 == Sort.NUMBER) { - return primitive ? definition.doubleType : definition.doubleobjType; - } else if (sort0 == Sort.FLOAT || sort0 == Sort.FLOAT_OBJ || sort1 == Sort.FLOAT || sort1 == Sort.FLOAT_OBJ) { - return primitive ? definition.floatType : definition.floatobjType; - } - } - - if (sort0 == Sort.LONG || sort0 == Sort.LONG_OBJ || sort0 == Sort.NUMBER || - sort1 == Sort.LONG || sort1 == Sort.LONG_OBJ || sort1 == Sort.NUMBER) { - return primitive ? definition.longType : definition.longobjType; - } else if (sort0.numeric && sort1.numeric) { - return primitive ? definition.intType : definition.intobjType; - } - - return null; - } - - private Type promoteAdd(final Type from0, final Type from1) { - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0 == Sort.STRING || sort1 == Sort.STRING) { - return definition.stringType; - } - - return promoteNumeric(from0, from1, true, true); - } - - private Type promoteXor(final Type from0, final Type from1) { - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0.bool || sort1.bool) { - return definition.booleanType; - } - - return promoteNumeric(from0, from1, false, true); - } - - private Type promoteEquality(final Type from0, final Type from1) { - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0 == Sort.DEF || sort1 == Sort.DEF) { - return definition.defType; - } - - final boolean primitive = sort0.primitive && sort1.primitive; - - if (sort0.bool && sort1.bool) { - return primitive ? definition.booleanType : definition.booleanobjType; - } - - if (sort0.numeric && sort1.numeric) { - return promoteNumeric(from0, from1, true, primitive); - } - - return definition.objectType; - } - - private Type promoteReference(final Type from0, final Type from1) { - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0 == Sort.DEF || sort1 == Sort.DEF) { - return definition.defType; - } - - if (sort0.primitive && sort1.primitive) { - if (sort0.bool && sort1.bool) { - return definition.booleanType; - } - - if (sort0.numeric && sort1.numeric) { - return promoteNumeric(from0, from1, true, true); - } - } - - return definition.objectType; - } - - private Type promoteConditional(final Type from0, final Type from1, final Object const0, final Object const1) { - if (from0.equals(from1)) { - return from0; - } - - final Sort sort0 = from0.sort; - final Sort sort1 = from1.sort; - - if (sort0 == Sort.DEF || sort1 == Sort.DEF) { - return definition.defType; - } - - final boolean primitive = sort0.primitive && sort1.primitive; - - if (sort0.bool && sort1.bool) { - return primitive ? definition.booleanType : definition.booleanobjType; - } - - if (sort0.numeric && sort1.numeric) { - if (sort0 == Sort.DOUBLE || sort0 == Sort.DOUBLE_OBJ || sort1 == Sort.DOUBLE || sort1 == Sort.DOUBLE_OBJ) { - return primitive ? definition.doubleType : definition.doubleobjType; - } else if (sort0 == Sort.FLOAT || sort0 == Sort.FLOAT_OBJ || sort1 == Sort.FLOAT || sort1 == Sort.FLOAT_OBJ) { - return primitive ? definition.floatType : definition.floatobjType; - } else if (sort0 == Sort.LONG || sort0 == Sort.LONG_OBJ || sort1 == Sort.LONG || sort1 == Sort.LONG_OBJ) { - return sort0.primitive && sort1.primitive ? definition.longType : definition.longobjType; - } else { - if (sort0 == Sort.BYTE || sort0 == Sort.BYTE_OBJ) { - if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { - return primitive ? definition.byteType : definition.byteobjType; - } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { - if (const1 != null) { - final short constant = (short)const1; - - if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.shortType : definition.shortobjType; - } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { - if (const1 != null) { - final int constant = (int)const1; - - if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } - } else if (sort0 == Sort.SHORT || sort0 == Sort.SHORT_OBJ) { - if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { - if (const0 != null) { - final short constant = (short)const0; - - if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.shortType : definition.shortobjType; - } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { - return primitive ? definition.shortType : definition.shortobjType; - } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { - if (const1 != null) { - final int constant = (int)const1; - - if (constant <= Short.MAX_VALUE && constant >= Short.MIN_VALUE) { - return primitive ? definition.shortType : definition.shortobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } - } else if (sort0 == Sort.CHAR || sort0 == Sort.CHAR_OBJ) { - if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { - return primitive ? definition.charType : definition.charobjType; - } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { - if (const1 != null) { - final int constant = (int)const1; - - if (constant <= Character.MAX_VALUE && constant >= Character.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } - } else if (sort0 == Sort.INT || sort0 == Sort.INT_OBJ) { - if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { - if (const0 != null) { - final int constant = (int)const0; - - if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { - if (const0 != null) { - final int constant = (int)const0; - - if (constant <= Short.MAX_VALUE && constant >= Short.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { - if (const0 != null) { - final int constant = (int)const0; - - if (constant <= Character.MAX_VALUE && constant >= Character.MIN_VALUE) { - return primitive ? definition.byteType : definition.byteobjType; - } - } - - return primitive ? definition.intType : definition.intobjType; - } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { - return primitive ? definition.intType : definition.intobjType; - } - } - } - } - - final Pair pair = new Pair(from0, from1); - final Type bound = definition.bounds.get(pair); - - return bound == null ? definition.objectType : bound; - } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java new file mode 100644 index 00000000000..46a510bc6bb --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java @@ -0,0 +1,563 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Transform; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; + +class AnalyzerCaster { + private final Definition definition; + + AnalyzerCaster(final Definition definition) { + this.definition = definition; + } + + void markCast(final ExpressionMetadata emd) { + if (emd.from == null) { + throw new IllegalStateException(AnalyzerUtility.error(emd.source) + "From cast type should never be null."); + } + + if (emd.to != null) { + emd.cast = getLegalCast(emd.source, emd.from, emd.to, emd.explicit || !emd.typesafe); + + if (emd.preConst != null && emd.to.sort.constant) { + emd.postConst = constCast(emd.source, emd.preConst, emd.cast); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(emd.source) + "To cast type should never be null."); + } + } + + Cast getLegalCast(final ParserRuleContext source, final Type from, final Type to, final boolean explicit) { + final Cast cast = new Cast(from, to); + + if (from.equals(to)) { + return cast; + } + + if (from.sort == Sort.DEF && to.sort != Sort.VOID || from.sort != Sort.VOID && to.sort == Sort.DEF) { + final Transform transform = definition.transforms.get(cast); + + if (transform != null) { + return transform; + } + + return cast; + } + + switch (from.sort) { + case BOOL: + switch (to.sort) { + case OBJECT: + case BOOL_OBJ: + return checkTransform(source, cast); + } + + break; + case BYTE: + switch (to.sort) { + case SHORT: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + return cast; + case CHAR: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case BYTE_OBJ: + case SHORT_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case SHORT: + switch (to.sort) { + case INT: + case LONG: + case FLOAT: + case DOUBLE: + return cast; + case BYTE: + case CHAR: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case SHORT_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case CHAR: + switch (to.sort) { + case INT: + case LONG: + case FLOAT: + case DOUBLE: + return cast; + case BYTE: + case SHORT: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case CHAR_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case SHORT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case INT: + switch (to.sort) { + case LONG: + case FLOAT: + case DOUBLE: + return cast; + case BYTE: + case SHORT: + case CHAR: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case LONG: + switch (to.sort) { + case FLOAT: + case DOUBLE: + return cast; + case BYTE: + case SHORT: + case CHAR: + case INT: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case FLOAT: + switch (to.sort) { + case DOUBLE: + return cast; + case BYTE: + case SHORT: + case CHAR: + case INT: + case LONG: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + case LONG_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case DOUBLE: + switch (to.sort) { + case BYTE: + case SHORT: + case CHAR: + case INT: + case LONG: + case FLOAT: + if (explicit) + return cast; + + break; + case OBJECT: + case NUMBER: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case OBJECT: + case NUMBER: + switch (to.sort) { + case BYTE: + case SHORT: + case CHAR: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case BOOL_OBJ: + switch (to.sort) { + case BOOL: + return checkTransform(source, cast); + } + + break; + case BYTE_OBJ: + switch (to.sort) { + case BYTE: + case SHORT: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + case SHORT_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case CHAR: + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case SHORT_OBJ: + switch (to.sort) { + case SHORT: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE: + case CHAR: + case BYTE_OBJ: + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case CHAR_OBJ: + switch (to.sort) { + case CHAR: + case INT: + case LONG: + case FLOAT: + case DOUBLE: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE: + case SHORT: + case BYTE_OBJ: + case SHORT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case INT_OBJ: + switch (to.sort) { + case INT: + case LONG: + case FLOAT: + case DOUBLE: + case LONG_OBJ: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE: + case SHORT: + case CHAR: + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case LONG_OBJ: + switch (to.sort) { + case LONG: + case FLOAT: + case DOUBLE: + case FLOAT_OBJ: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE: + case SHORT: + case CHAR: + case INT: + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case FLOAT_OBJ: + switch (to.sort) { + case FLOAT: + case DOUBLE: + case DOUBLE_OBJ: + return checkTransform(source, cast); + case BYTE: + case SHORT: + case CHAR: + case INT: + case LONG: + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + case LONG_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + case DOUBLE_OBJ: + switch (to.sort) { + case DOUBLE: + return checkTransform(source, cast); + case BYTE: + case SHORT: + case CHAR: + case INT: + case LONG: + case FLOAT: + case BYTE_OBJ: + case SHORT_OBJ: + case CHAR_OBJ: + case INT_OBJ: + case LONG_OBJ: + case FLOAT_OBJ: + if (explicit) + return checkTransform(source, cast); + + break; + } + + break; + } + + try { + from.clazz.asSubclass(to.clazz); + + return cast; + } catch (final ClassCastException cce0) { + try { + if (explicit) { + to.clazz.asSubclass(from.clazz); + + return cast; + } else { + throw new ClassCastException( + AnalyzerUtility.error(source) + "Cannot cast from [" + from.name + "] to [" + to.name + "]."); + } + } catch (final ClassCastException cce1) { + throw new ClassCastException( + AnalyzerUtility.error(source) + "Cannot cast from [" + from.name + "] to [" + to.name + "]."); + } + } + } + + private Transform checkTransform(final ParserRuleContext source, final Cast cast) { + final Transform transform = definition.transforms.get(cast); + + if (transform == null) { + throw new ClassCastException( + AnalyzerUtility.error(source) + "Cannot cast from [" + cast.from.name + "] to [" + cast.to.name + "]."); + } + + return transform; + } + + private Object constCast(final ParserRuleContext source, final Object constant, final Cast cast) { + if (cast instanceof Transform) { + final Transform transform = (Transform)cast; + return invokeTransform(source, transform, constant); + } else { + final Sort fsort = cast.from.sort; + final Sort tsort = cast.to.sort; + + if (fsort == tsort) { + return constant; + } else if (fsort.numeric && tsort.numeric) { + Number number; + + if (fsort == Sort.CHAR) { + number = (int)(char)constant; + } else { + number = (Number)constant; + } + + switch (tsort) { + case BYTE: return number.byteValue(); + case SHORT: return number.shortValue(); + case CHAR: return (char)number.intValue(); + case INT: return number.intValue(); + case LONG: return number.longValue(); + case FLOAT: return number.floatValue(); + case DOUBLE: return number.doubleValue(); + default: + throw new IllegalStateException(AnalyzerUtility.error(source) + "Expected numeric type for cast."); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(source) + "No valid constant cast from " + + "[" + cast.from.clazz.getCanonicalName() + "] to " + + "[" + cast.to.clazz.getCanonicalName() + "]."); + } + } + } + + private Object invokeTransform(final ParserRuleContext source, final Transform transform, final Object object) { + final Method method = transform.method; + final java.lang.reflect.Method jmethod = method.reflect; + final int modifiers = jmethod.getModifiers(); + + try { + if (java.lang.reflect.Modifier.isStatic(modifiers)) { + return jmethod.invoke(null, object); + } else { + return jmethod.invoke(object); + } + } catch (IllegalAccessException | IllegalArgumentException | + java.lang.reflect.InvocationTargetException | NullPointerException | + ExceptionInInitializerError exception) { + throw new IllegalStateException(AnalyzerUtility.error(source) + "Unable to invoke transform to cast constant from " + + "[" + transform.from.name + "] to [" + transform.to.name + "]."); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExpression.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExpression.java new file mode 100644 index 00000000000..3e74259fecf --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExpression.java @@ -0,0 +1,868 @@ +/* + * 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; + +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.Metadata.ExternalMetadata; +import org.elasticsearch.painless.PainlessParser.AssignmentContext; +import org.elasticsearch.painless.PainlessParser.BinaryContext; +import org.elasticsearch.painless.PainlessParser.BoolContext; +import org.elasticsearch.painless.PainlessParser.CastContext; +import org.elasticsearch.painless.PainlessParser.CharContext; +import org.elasticsearch.painless.PainlessParser.CompContext; +import org.elasticsearch.painless.PainlessParser.ConditionalContext; +import org.elasticsearch.painless.PainlessParser.DecltypeContext; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ExternalContext; +import org.elasticsearch.painless.PainlessParser.ExtstartContext; +import org.elasticsearch.painless.PainlessParser.FalseContext; +import org.elasticsearch.painless.PainlessParser.IncrementContext; +import org.elasticsearch.painless.PainlessParser.NullContext; +import org.elasticsearch.painless.PainlessParser.NumericContext; +import org.elasticsearch.painless.PainlessParser.PostincContext; +import org.elasticsearch.painless.PainlessParser.PreincContext; +import org.elasticsearch.painless.PainlessParser.TrueContext; +import org.elasticsearch.painless.PainlessParser.UnaryContext; + +import static org.elasticsearch.painless.PainlessParser.ADD; +import static org.elasticsearch.painless.PainlessParser.BWAND; +import static org.elasticsearch.painless.PainlessParser.BWOR; +import static org.elasticsearch.painless.PainlessParser.BWXOR; +import static org.elasticsearch.painless.PainlessParser.DIV; +import static org.elasticsearch.painless.PainlessParser.LSH; +import static org.elasticsearch.painless.PainlessParser.MUL; +import static org.elasticsearch.painless.PainlessParser.REM; +import static org.elasticsearch.painless.PainlessParser.RSH; +import static org.elasticsearch.painless.PainlessParser.SUB; +import static org.elasticsearch.painless.PainlessParser.USH; + +class AnalyzerExpression { + private final Metadata metadata; + private final Definition definition; + private final CompilerSettings settings; + + private final Analyzer analyzer; + private final AnalyzerCaster caster; + private final AnalyzerPromoter promoter; + + AnalyzerExpression(final Metadata metadata, final Analyzer analyzer, + final AnalyzerCaster caster, final AnalyzerPromoter promoter) { + this.metadata = metadata; + this.definition = metadata.definition; + this.settings = metadata.settings; + + this.analyzer = analyzer; + this.caster = caster; + this.promoter = promoter; + } + + void processNumeric(final NumericContext ctx) { + final ExpressionMetadata numericemd = metadata.getExpressionMetadata(ctx); + final boolean negate = ctx.parent instanceof UnaryContext && ((UnaryContext)ctx.parent).SUB() != null; + + if (ctx.DECIMAL() != null) { + final String svalue = (negate ? "-" : "") + ctx.DECIMAL().getText(); + + if (svalue.endsWith("f") || svalue.endsWith("F")) { + try { + numericemd.from = definition.floatType; + numericemd.preConst = Float.parseFloat(svalue.substring(0, svalue.length() - 1)); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid float constant [" + svalue + "]."); + } + } else { + try { + numericemd.from = definition.doubleType; + numericemd.preConst = Double.parseDouble(svalue); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid double constant [" + svalue + "]."); + } + } + } else { + String svalue = negate ? "-" : ""; + int radix; + + if (ctx.OCTAL() != null) { + svalue += ctx.OCTAL().getText(); + radix = 8; + } else if (ctx.INTEGER() != null) { + svalue += ctx.INTEGER().getText(); + radix = 10; + } else if (ctx.HEX() != null) { + svalue += ctx.HEX().getText(); + radix = 16; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + if (svalue.endsWith("d") || svalue.endsWith("D")) { + try { + numericemd.from = definition.doubleType; + numericemd.preConst = Double.parseDouble(svalue.substring(0, svalue.length() - 1)); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid float constant [" + svalue + "]."); + } + } else if (svalue.endsWith("f") || svalue.endsWith("F")) { + try { + numericemd.from = definition.floatType; + numericemd.preConst = Float.parseFloat(svalue.substring(0, svalue.length() - 1)); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid float constant [" + svalue + "]."); + } + } else if (svalue.endsWith("l") || svalue.endsWith("L")) { + try { + numericemd.from = definition.longType; + numericemd.preConst = Long.parseLong(svalue.substring(0, svalue.length() - 1), radix); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid long constant [" + svalue + "]."); + } + } else { + try { + final Type type = numericemd.to; + final Sort sort = type == null ? Sort.INT : type.sort; + final int value = Integer.parseInt(svalue, radix); + + if (sort == Sort.BYTE && value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + numericemd.from = definition.byteType; + numericemd.preConst = (byte)value; + } else if (sort == Sort.CHAR && value >= Character.MIN_VALUE && value <= Character.MAX_VALUE) { + numericemd.from = definition.charType; + numericemd.preConst = (char)value; + } else if (sort == Sort.SHORT && value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + numericemd.from = definition.shortType; + numericemd.preConst = (short)value; + } else { + numericemd.from = definition.intType; + numericemd.preConst = value; + } + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid int constant [" + svalue + "]."); + } + } + } + } + + void processChar(final CharContext ctx) { + final ExpressionMetadata charemd = metadata.getExpressionMetadata(ctx); + + if (ctx.CHAR() == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + charemd.preConst = ctx.CHAR().getText().charAt(0); + charemd.from = definition.charType; + } + + void processTrue(final TrueContext ctx) { + final ExpressionMetadata trueemd = metadata.getExpressionMetadata(ctx); + + if (ctx.TRUE() == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + trueemd.preConst = true; + trueemd.from = definition.booleanType; + } + + void processFalse(final FalseContext ctx) { + final ExpressionMetadata falseemd = metadata.getExpressionMetadata(ctx); + + if (ctx.FALSE() == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + falseemd.preConst = false; + falseemd.from = definition.booleanType; + } + + void processNull(final NullContext ctx) { + final ExpressionMetadata nullemd = metadata.getExpressionMetadata(ctx); + + if (ctx.NULL() == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + nullemd.isNull = true; + + if (nullemd.to != null) { + if (nullemd.to.sort.primitive) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Cannot cast null to a primitive type [" + nullemd.to.name + "]."); + } + + nullemd.from = nullemd.to; + } else { + nullemd.from = definition.objectType; + } + } + + void processExternal(final ExternalContext ctx) { + final ExpressionMetadata extemd = metadata.getExpressionMetadata(ctx); + + final ExtstartContext extstartctx = ctx.extstart(); + final ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); + extstartemd.read = extemd.read; + analyzer.visit(extstartctx); + + extemd.statement = extstartemd.statement; + extemd.preConst = extstartemd.constant; + extemd.from = extstartemd.current; + extemd.typesafe = extstartemd.current.sort != Sort.DEF; + } + + void processPostinc(final PostincContext ctx) { + final ExpressionMetadata postincemd = metadata.getExpressionMetadata(ctx); + + final ExtstartContext extstartctx = ctx.extstart(); + final ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); + extstartemd.read = postincemd.read; + extstartemd.storeExpr = ctx.increment(); + extstartemd.token = ADD; + extstartemd.post = true; + analyzer.visit(extstartctx); + + postincemd.statement = true; + postincemd.from = extstartemd.read ? extstartemd.current : definition.voidType; + postincemd.typesafe = extstartemd.current.sort != Sort.DEF; + } + + void processPreinc(final PreincContext ctx) { + final ExpressionMetadata preincemd = metadata.getExpressionMetadata(ctx); + + final ExtstartContext extstartctx = ctx.extstart(); + final ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); + extstartemd.read = preincemd.read; + extstartemd.storeExpr = ctx.increment(); + extstartemd.token = ADD; + extstartemd.pre = true; + analyzer.visit(extstartctx); + + preincemd.statement = true; + preincemd.from = extstartemd.read ? extstartemd.current : definition.voidType; + preincemd.typesafe = extstartemd.current.sort != Sort.DEF; + } + + void processUnary(final UnaryContext ctx) { + final ExpressionMetadata unaryemd = metadata.getExpressionMetadata(ctx); + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + + if (ctx.BOOLNOT() != null) { + expremd.to = definition.booleanType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + if (expremd.postConst != null) { + unaryemd.preConst = !(boolean)expremd.postConst; + } + + unaryemd.from = definition.booleanType; + } else if (ctx.BWNOT() != null || ctx.ADD() != null || ctx.SUB() != null) { + analyzer.visit(exprctx); + + final Type promote = promoter.promoteNumeric(expremd.from, ctx.BWNOT() == null, true); + + if (promote == null) { + throw new ClassCastException(AnalyzerUtility.error(ctx) + "Cannot apply [" + ctx.getChild(0).getText() + "] " + + "operation to type [" + expremd.from.name + "]."); + } + + expremd.to = promote; + caster.markCast(expremd); + + if (expremd.postConst != null) { + final Sort sort = promote.sort; + + if (ctx.BWNOT() != null) { + if (sort == Sort.INT) { + unaryemd.preConst = ~(int)expremd.postConst; + } else if (sort == Sort.LONG) { + unaryemd.preConst = ~(long)expremd.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.SUB() != null) { + if (exprctx instanceof NumericContext) { + unaryemd.preConst = expremd.postConst; + } else { + if (sort == Sort.INT) { + if (settings.getNumericOverflow()) { + unaryemd.preConst = -(int)expremd.postConst; + } else { + unaryemd.preConst = Math.negateExact((int)expremd.postConst); + } + } else if (sort == Sort.LONG) { + if (settings.getNumericOverflow()) { + unaryemd.preConst = -(long)expremd.postConst; + } else { + unaryemd.preConst = Math.negateExact((long)expremd.postConst); + } + } else if (sort == Sort.FLOAT) { + unaryemd.preConst = -(float)expremd.postConst; + } else if (sort == Sort.DOUBLE) { + unaryemd.preConst = -(double)expremd.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + } else if (ctx.ADD() != null) { + if (sort == Sort.INT) { + unaryemd.preConst = +(int)expremd.postConst; + } else if (sort == Sort.LONG) { + unaryemd.preConst = +(long)expremd.postConst; + } else if (sort == Sort.FLOAT) { + unaryemd.preConst = +(float)expremd.postConst; + } else if (sort == Sort.DOUBLE) { + unaryemd.preConst = +(double)expremd.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + unaryemd.from = promote; + unaryemd.typesafe = expremd.typesafe; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + void processCast(final CastContext ctx) { + final ExpressionMetadata castemd = metadata.getExpressionMetadata(ctx); + + final DecltypeContext decltypectx = ctx.decltype(); + final ExpressionMetadata decltypemd = metadata.createExpressionMetadata(decltypectx); + analyzer.visit(decltypectx); + + final Type type = decltypemd.from; + castemd.from = type; + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = type; + expremd.explicit = true; + analyzer.visit(exprctx); + caster.markCast(expremd); + + if (expremd.postConst != null) { + castemd.preConst = expremd.postConst; + } + + castemd.typesafe = expremd.typesafe && castemd.from.sort != Sort.DEF; + } + + void processBinary(final BinaryContext ctx) { + final ExpressionMetadata binaryemd = metadata.getExpressionMetadata(ctx); + + final ExpressionContext exprctx0 = AnalyzerUtility.updateExpressionTree(ctx.expression(0)); + final ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); + analyzer.visit(exprctx0); + + final ExpressionContext exprctx1 = AnalyzerUtility.updateExpressionTree(ctx.expression(1)); + final ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); + analyzer.visit(exprctx1); + + final boolean decimal = ctx.MUL() != null || ctx.DIV() != null || ctx.REM() != null || ctx.SUB() != null; + final boolean add = ctx.ADD() != null; + final boolean xor = ctx.BWXOR() != null; + final Type promote = add ? promoter.promoteAdd(expremd0.from, expremd1.from) : + xor ? promoter.promoteXor(expremd0.from, expremd1.from) : + promoter.promoteNumeric(expremd0.from, expremd1.from, decimal, true); + + if (promote == null) { + throw new ClassCastException(AnalyzerUtility.error(ctx) + "Cannot apply [" + ctx.getChild(1).getText() + "] " + + "operation to types [" + expremd0.from.name + "] and [" + expremd1.from.name + "]."); + } + + final Sort sort = promote.sort; + expremd0.to = add && sort == Sort.STRING ? expremd0.from : promote; + expremd1.to = add && sort == Sort.STRING ? expremd1.from : promote; + caster.markCast(expremd0); + caster.markCast(expremd1); + + if (expremd0.postConst != null && expremd1.postConst != null) { + if (ctx.MUL() != null) { + if (sort == Sort.INT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (int)expremd0.postConst * (int)expremd1.postConst; + } else { + binaryemd.preConst = Math.multiplyExact((int)expremd0.postConst, (int)expremd1.postConst); + } + } else if (sort == Sort.LONG) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (long)expremd0.postConst * (long)expremd1.postConst; + } else { + binaryemd.preConst = Math.multiplyExact((long)expremd0.postConst, (long)expremd1.postConst); + } + } else if (sort == Sort.FLOAT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (float)expremd0.postConst * (float)expremd1.postConst; + } else { + binaryemd.preConst = Utility.multiplyWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); + } + } else if (sort == Sort.DOUBLE) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (double)expremd0.postConst * (double)expremd1.postConst; + } else { + binaryemd.preConst = Utility.multiplyWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.DIV() != null) { + if (sort == Sort.INT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (int)expremd0.postConst / (int)expremd1.postConst; + } else { + binaryemd.preConst = Utility.divideWithoutOverflow((int)expremd0.postConst, (int)expremd1.postConst); + } + } else if (sort == Sort.LONG) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (long)expremd0.postConst / (long)expremd1.postConst; + } else { + binaryemd.preConst = Utility.divideWithoutOverflow((long)expremd0.postConst, (long)expremd1.postConst); + } + } else if (sort == Sort.FLOAT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (float)expremd0.postConst / (float)expremd1.postConst; + } else { + binaryemd.preConst = Utility.divideWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); + } + } else if (sort == Sort.DOUBLE) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (double)expremd0.postConst / (double)expremd1.postConst; + } else { + binaryemd.preConst = Utility.divideWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.REM() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst % (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst % (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (float)expremd0.postConst % (float)expremd1.postConst; + } else { + binaryemd.preConst = Utility.remainderWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); + } + } else if (sort == Sort.DOUBLE) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (double)expremd0.postConst % (double)expremd1.postConst; + } else { + binaryemd.preConst = Utility.remainderWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.ADD() != null) { + if (sort == Sort.INT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (int)expremd0.postConst + (int)expremd1.postConst; + } else { + binaryemd.preConst = Math.addExact((int)expremd0.postConst, (int)expremd1.postConst); + } + } else if (sort == Sort.LONG) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (long)expremd0.postConst + (long)expremd1.postConst; + } else { + binaryemd.preConst = Math.addExact((long)expremd0.postConst, (long)expremd1.postConst); + } + } else if (sort == Sort.FLOAT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (float)expremd0.postConst + (float)expremd1.postConst; + } else { + binaryemd.preConst = Utility.addWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); + } + } else if (sort == Sort.DOUBLE) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (double)expremd0.postConst + (double)expremd1.postConst; + } else { + binaryemd.preConst = Utility.addWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); + } + } else if (sort == Sort.STRING) { + binaryemd.preConst = "" + expremd0.postConst + expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.SUB() != null) { + if (sort == Sort.INT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (int)expremd0.postConst - (int)expremd1.postConst; + } else { + binaryemd.preConst = Math.subtractExact((int)expremd0.postConst, (int)expremd1.postConst); + } + } else if (sort == Sort.LONG) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (long)expremd0.postConst - (long)expremd1.postConst; + } else { + binaryemd.preConst = Math.subtractExact((long)expremd0.postConst, (long)expremd1.postConst); + } + } else if (sort == Sort.FLOAT) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (float)expremd0.postConst - (float)expremd1.postConst; + } else { + binaryemd.preConst = Utility.subtractWithoutOverflow((float)expremd0.postConst, (float)expremd1.postConst); + } + } else if (sort == Sort.DOUBLE) { + if (settings.getNumericOverflow()) { + binaryemd.preConst = (double)expremd0.postConst - (double)expremd1.postConst; + } else { + binaryemd.preConst = Utility.subtractWithoutOverflow((double)expremd0.postConst, (double)expremd1.postConst); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.LSH() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst << (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst << (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.RSH() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst >> (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst >> (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.USH() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst >>> (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst >>> (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.BWAND() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst & (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst & (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.BWXOR() != null) { + if (sort == Sort.BOOL) { + binaryemd.preConst = (boolean)expremd0.postConst ^ (boolean)expremd1.postConst; + } else if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst ^ (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst ^ (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.BWOR() != null) { + if (sort == Sort.INT) { + binaryemd.preConst = (int)expremd0.postConst | (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + binaryemd.preConst = (long)expremd0.postConst | (long)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + binaryemd.from = promote; + binaryemd.typesafe = expremd0.typesafe && expremd1.typesafe; + } + + void processComp(final CompContext ctx) { + final ExpressionMetadata compemd = metadata.getExpressionMetadata(ctx); + final boolean equality = ctx.EQ() != null || ctx.NE() != null; + final boolean reference = ctx.EQR() != null || ctx.NER() != null; + + final ExpressionContext exprctx0 = AnalyzerUtility.updateExpressionTree(ctx.expression(0)); + final ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); + analyzer.visit(exprctx0); + + final ExpressionContext exprctx1 = AnalyzerUtility.updateExpressionTree(ctx.expression(1)); + final ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); + analyzer.visit(exprctx1); + + if (expremd0.isNull && expremd1.isNull) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unnecessary comparison of null constants."); + } + + final Type promote = equality ? promoter.promoteEquality(expremd0.from, expremd1.from) : + reference ? promoter.promoteReference(expremd0.from, expremd1.from) : + promoter.promoteNumeric(expremd0.from, expremd1.from, true, true); + + if (promote == null) { + throw new ClassCastException(AnalyzerUtility.error(ctx) + "Cannot apply [" + ctx.getChild(1).getText() + "] " + + "operation to types [" + expremd0.from.name + "] and [" + expremd1.from.name + "]."); + } + + expremd0.to = promote; + expremd1.to = promote; + caster.markCast(expremd0); + caster.markCast(expremd1); + + if (expremd0.postConst != null && expremd1.postConst != null) { + final Sort sort = promote.sort; + + if (ctx.EQ() != null || ctx.EQR() != null) { + if (sort == Sort.BOOL) { + compemd.preConst = (boolean)expremd0.postConst == (boolean)expremd1.postConst; + } else if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst == (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst == (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst == (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst == (double)expremd1.postConst; + } else { + if (ctx.EQ() != null && !expremd0.isNull && !expremd1.isNull) { + compemd.preConst = expremd0.postConst.equals(expremd1.postConst); + } else if (ctx.EQR() != null) { + compemd.preConst = expremd0.postConst == expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + } else if (ctx.NE() != null || ctx.NER() != null) { + if (sort == Sort.BOOL) { + compemd.preConst = (boolean)expremd0.postConst != (boolean)expremd1.postConst; + } else if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst != (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst != (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst != (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst != (double)expremd1.postConst; + } else { + if (ctx.NE() != null && !expremd0.isNull && !expremd1.isNull) { + compemd.preConst = expremd0.postConst.equals(expremd1.postConst); + } else if (ctx.NER() != null) { + compemd.preConst = expremd0.postConst == expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + } else if (ctx.GTE() != null) { + if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst >= (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst >= (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst >= (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst >= (double)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.GT() != null) { + if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst > (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst > (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst > (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst > (double)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.LTE() != null) { + if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst <= (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst <= (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst <= (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst <= (double)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else if (ctx.LT() != null) { + if (sort == Sort.INT) { + compemd.preConst = (int)expremd0.postConst < (int)expremd1.postConst; + } else if (sort == Sort.LONG) { + compemd.preConst = (long)expremd0.postConst < (long)expremd1.postConst; + } else if (sort == Sort.FLOAT) { + compemd.preConst = (float)expremd0.postConst < (float)expremd1.postConst; + } else if (sort == Sort.DOUBLE) { + compemd.preConst = (double)expremd0.postConst < (double)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + compemd.from = definition.booleanType; + compemd.typesafe = expremd0.typesafe && expremd1.typesafe; + } + + void processBool(final BoolContext ctx) { + final ExpressionMetadata boolemd = metadata.getExpressionMetadata(ctx); + + final ExpressionContext exprctx0 = AnalyzerUtility.updateExpressionTree(ctx.expression(0)); + final ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); + expremd0.to = definition.booleanType; + analyzer.visit(exprctx0); + caster.markCast(expremd0); + + final ExpressionContext exprctx1 = AnalyzerUtility.updateExpressionTree(ctx.expression(1)); + final ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); + expremd1.to = definition.booleanType; + analyzer.visit(exprctx1); + caster.markCast(expremd1); + + if (expremd0.postConst != null && expremd1.postConst != null) { + if (ctx.BOOLAND() != null) { + boolemd.preConst = (boolean)expremd0.postConst && (boolean)expremd1.postConst; + } else if (ctx.BOOLOR() != null) { + boolemd.preConst = (boolean)expremd0.postConst || (boolean)expremd1.postConst; + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + boolemd.from = definition.booleanType; + boolemd.typesafe = expremd0.typesafe && expremd1.typesafe; + } + + void processConditional(final ConditionalContext ctx) { + final ExpressionMetadata condemd = metadata.getExpressionMetadata(ctx); + + final ExpressionContext exprctx0 = AnalyzerUtility.updateExpressionTree(ctx.expression(0)); + final ExpressionMetadata expremd0 = metadata.createExpressionMetadata(exprctx0); + expremd0.to = definition.booleanType; + analyzer.visit(exprctx0); + caster.markCast(expremd0); + + if (expremd0.postConst != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unnecessary conditional statement."); + } + + final ExpressionContext exprctx1 = AnalyzerUtility.updateExpressionTree(ctx.expression(1)); + final ExpressionMetadata expremd1 = metadata.createExpressionMetadata(exprctx1); + expremd1.to = condemd.to; + expremd1.explicit = condemd.explicit; + analyzer.visit(exprctx1); + + final ExpressionContext exprctx2 = AnalyzerUtility.updateExpressionTree(ctx.expression(2)); + final ExpressionMetadata expremd2 = metadata.createExpressionMetadata(exprctx2); + expremd2.to = condemd.to; + expremd2.explicit = condemd.explicit; + analyzer.visit(exprctx2); + + if (condemd.to == null) { + final Type promote = promoter.promoteConditional(expremd1.from, expremd2.from, expremd1.preConst, expremd2.preConst); + + expremd1.to = promote; + expremd2.to = promote; + condemd.from = promote; + } else { + condemd.from = condemd.to; + } + + caster.markCast(expremd1); + caster.markCast(expremd2); + + condemd.typesafe = expremd0.typesafe && expremd1.typesafe; + } + + void processAssignment(final AssignmentContext ctx) { + final ExpressionMetadata assignemd = metadata.getExpressionMetadata(ctx); + + final ExtstartContext extstartctx = ctx.extstart(); + final ExternalMetadata extstartemd = metadata.createExternalMetadata(extstartctx); + + extstartemd.read = assignemd.read; + extstartemd.storeExpr = AnalyzerUtility.updateExpressionTree(ctx.expression()); + + if (ctx.AMUL() != null) { + extstartemd.token = MUL; + } else if (ctx.ADIV() != null) { + extstartemd.token = DIV; + } else if (ctx.AREM() != null) { + extstartemd.token = REM; + } else if (ctx.AADD() != null) { + extstartemd.token = ADD; + } else if (ctx.ASUB() != null) { + extstartemd.token = SUB; + } else if (ctx.ALSH() != null) { + extstartemd.token = LSH; + } else if (ctx.AUSH() != null) { + extstartemd.token = USH; + } else if (ctx.ARSH() != null) { + extstartemd.token = RSH; + } else if (ctx.AAND() != null) { + extstartemd.token = BWAND; + } else if (ctx.AXOR() != null) { + extstartemd.token = BWXOR; + } else if (ctx.AOR() != null) { + extstartemd.token = BWOR; + } + + analyzer.visit(extstartctx); + + assignemd.statement = true; + assignemd.from = extstartemd.read ? extstartemd.current : definition.voidType; + assignemd.typesafe = extstartemd.current.sort != Sort.DEF; + } + + void processIncrement(final IncrementContext ctx) { + final ExpressionMetadata incremd = metadata.getExpressionMetadata(ctx); + final Sort sort = incremd.to == null ? null : incremd.to.sort; + final boolean positive = ctx.INCR() != null; + + if (incremd.to == null) { + incremd.preConst = positive ? 1 : -1; + incremd.from = definition.intType; + } else { + switch (sort) { + case LONG: + incremd.preConst = positive ? 1L : -1L; + incremd.from = definition.longType; + break; + case FLOAT: + incremd.preConst = positive ? 1.0F : -1.0F; + incremd.from = definition.floatType; + break; + case DOUBLE: + incremd.preConst = positive ? 1.0 : -1.0; + incremd.from = definition.doubleType; + break; + default: + incremd.preConst = positive ? 1 : -1; + incremd.from = definition.intType; + } + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExternal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExternal.java new file mode 100644 index 00000000000..db3ab06e785 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerExternal.java @@ -0,0 +1,816 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.elasticsearch.painless.AnalyzerUtility.Variable; +import org.elasticsearch.painless.Definition.Constructor; +import org.elasticsearch.painless.Definition.Field; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.Metadata.ExtNodeMetadata; +import org.elasticsearch.painless.Metadata.ExternalMetadata; +import org.elasticsearch.painless.PainlessParser.DecltypeContext; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ExtbraceContext; +import org.elasticsearch.painless.PainlessParser.ExtcallContext; +import org.elasticsearch.painless.PainlessParser.ExtcastContext; +import org.elasticsearch.painless.PainlessParser.ExtdotContext; +import org.elasticsearch.painless.PainlessParser.ExtfieldContext; +import org.elasticsearch.painless.PainlessParser.ExtnewContext; +import org.elasticsearch.painless.PainlessParser.ExtprecContext; +import org.elasticsearch.painless.PainlessParser.ExtstartContext; +import org.elasticsearch.painless.PainlessParser.ExtstringContext; +import org.elasticsearch.painless.PainlessParser.ExttypeContext; +import org.elasticsearch.painless.PainlessParser.ExtvarContext; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.painless.PainlessParser.ADD; +import static org.elasticsearch.painless.PainlessParser.BWAND; +import static org.elasticsearch.painless.PainlessParser.BWOR; +import static org.elasticsearch.painless.PainlessParser.BWXOR; +import static org.elasticsearch.painless.PainlessParser.DIV; +import static org.elasticsearch.painless.PainlessParser.MUL; +import static org.elasticsearch.painless.PainlessParser.REM; +import static org.elasticsearch.painless.PainlessParser.SUB; + +class AnalyzerExternal { + private final Metadata metadata; + private final Definition definition; + + private final Analyzer analyzer; + private final AnalyzerUtility utility; + private final AnalyzerCaster caster; + private final AnalyzerPromoter promoter; + + AnalyzerExternal(final Metadata metadata, final Analyzer analyzer, final AnalyzerUtility utility, + final AnalyzerCaster caster, final AnalyzerPromoter promoter) { + this.metadata = metadata; + this.definition = metadata.definition; + + this.analyzer = analyzer; + this.utility = utility; + this.caster = caster; + this.promoter = promoter; + } + + void processExtstart(final ExtstartContext ctx) { + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + if (precctx != null) { + metadata.createExtNodeMetadata(ctx, precctx); + analyzer.visit(precctx); + } else if (castctx != null) { + metadata.createExtNodeMetadata(ctx, castctx); + analyzer.visit(castctx); + } else if (typectx != null) { + metadata.createExtNodeMetadata(ctx, typectx); + analyzer.visit(typectx); + } else if (varctx != null) { + metadata.createExtNodeMetadata(ctx, varctx); + analyzer.visit(varctx); + } else if (newctx != null) { + metadata.createExtNodeMetadata(ctx, newctx); + analyzer.visit(newctx); + } else if (stringctx != null) { + metadata.createExtNodeMetadata(ctx, stringctx); + analyzer.visit(stringctx); + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + void processExtprec(final ExtprecContext ctx) { + final ExtNodeMetadata precenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = precenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null || bracectx != null) { + ++parentemd.scope; + } + + if (precctx != null) { + metadata.createExtNodeMetadata(parent, precctx); + analyzer.visit(precctx); + } else if (castctx != null) { + metadata.createExtNodeMetadata(parent, castctx); + analyzer.visit(castctx); + } else if (typectx != null) { + metadata.createExtNodeMetadata(parent, typectx); + analyzer.visit(typectx); + } else if (varctx != null) { + metadata.createExtNodeMetadata(parent, varctx); + analyzer.visit(varctx); + } else if (newctx != null) { + metadata.createExtNodeMetadata(parent, newctx); + analyzer.visit(newctx); + } else if (stringctx != null) { + metadata.createExtNodeMetadata(ctx, stringctx); + analyzer.visit(stringctx); + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + parentemd.statement = false; + + if (dotctx != null) { + --parentemd.scope; + + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + --parentemd.scope; + + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + void processExtcast(final ExtcastContext ctx) { + final ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = castenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + if (precctx != null) { + metadata.createExtNodeMetadata(parent, precctx); + analyzer.visit(precctx); + } else if (castctx != null) { + metadata.createExtNodeMetadata(parent, castctx); + analyzer.visit(castctx); + } else if (typectx != null) { + metadata.createExtNodeMetadata(parent, typectx); + analyzer.visit(typectx); + } else if (varctx != null) { + metadata.createExtNodeMetadata(parent, varctx); + analyzer.visit(varctx); + } else if (newctx != null) { + metadata.createExtNodeMetadata(parent, newctx); + analyzer.visit(newctx); + } else if (stringctx != null) { + metadata.createExtNodeMetadata(ctx, stringctx); + analyzer.visit(stringctx); + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + final DecltypeContext declctx = ctx.decltype(); + final ExpressionMetadata declemd = metadata.createExpressionMetadata(declctx); + analyzer.visit(declctx); + + castenmd.castTo = caster.getLegalCast(ctx, parentemd.current, declemd.from, true); + castenmd.type = declemd.from; + parentemd.current = declemd.from; + parentemd.statement = false; + } + + void processExtbrace(final ExtbraceContext ctx) { + final ExtNodeMetadata braceenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = braceenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final boolean array = parentemd.current.sort == Sort.ARRAY; + final boolean def = parentemd.current.sort == Sort.DEF; + boolean map = false; + boolean list = false; + + try { + parentemd.current.clazz.asSubclass(Map.class); + map = true; + } catch (final ClassCastException exception) { + // Do nothing. + } + + try { + parentemd.current.clazz.asSubclass(List.class); + list = true; + } catch (final ClassCastException exception) { + // Do nothing. + } + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + braceenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + + if (array || def) { + expremd.to = array ? definition.intType : definition.objectType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + braceenmd.target = "#brace"; + braceenmd.type = def ? definition.defType : + definition.getType(parentemd.current.struct, parentemd.current.type.getDimensions() - 1); + analyzeLoadStoreExternal(ctx); + parentemd.current = braceenmd.type; + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } else { + final boolean store = braceenmd.last && parentemd.storeExpr != null; + final boolean get = parentemd.read || parentemd.token > 0 || !braceenmd.last; + final boolean set = braceenmd.last && store; + + Method getter; + Method setter; + Type valuetype; + Type settype; + + if (map) { + getter = parentemd.current.struct.methods.get("get"); + setter = parentemd.current.struct.methods.get("put"); + + if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal map get shortcut for type [" + parentemd.current.name + "]."); + } + + if (setter != null && setter.arguments.size() != 2) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal map set shortcut for type [" + parentemd.current.name + "]."); + } + + if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0)) + || !getter.rtn.equals(setter.arguments.get(1)))) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Shortcut argument types must match."); + } + + valuetype = setter != null ? setter.arguments.get(0) : getter != null ? getter.arguments.get(0) : null; + settype = setter == null ? null : setter.arguments.get(1); + } else if (list) { + getter = parentemd.current.struct.methods.get("get"); + setter = parentemd.current.struct.methods.get("set"); + + if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || + getter.arguments.get(0).sort != Sort.INT)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal list get shortcut for type [" + parentemd.current.name + "]."); + } + + if (setter != null && (setter.arguments.size() != 2 || setter.arguments.get(0).sort != Sort.INT)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal list set shortcut for type [" + parentemd.current.name + "]."); + } + + if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0)) + || !getter.rtn.equals(setter.arguments.get(1)))) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Shortcut argument types must match."); + } + + valuetype = definition.intType; + settype = setter == null ? null : setter.arguments.get(1); + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + if ((get || set) && (!get || getter != null) && (!set || setter != null)) { + expremd.to = valuetype; + analyzer.visit(exprctx); + caster.markCast(expremd); + + braceenmd.target = new Object[] {getter, setter, true, null}; + braceenmd.type = get ? getter.rtn : settype; + analyzeLoadStoreExternal(ctx); + parentemd.current = get ? getter.rtn : setter.rtn; + } + } + + if (braceenmd.target == null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Attempting to address a non-array type [" + parentemd.current.name + "] as an array."); + } + } + + void processExtdot(final ExtdotContext ctx) { + final ExtNodeMetadata dotemnd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = dotemnd.parent; + + final ExtcallContext callctx = ctx.extcall(); + final ExtfieldContext fieldctx = ctx.extfield(); + + if (callctx != null) { + metadata.createExtNodeMetadata(parent, callctx); + analyzer.visit(callctx); + } else if (fieldctx != null) { + metadata.createExtNodeMetadata(parent, fieldctx); + analyzer.visit(fieldctx); + } + } + + void processExttype(final ExttypeContext ctx) { + final ExtNodeMetadata typeenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = typeenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + if (parentemd.current != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unexpected static type."); + } + + final String typestr = ctx.TYPE().getText(); + typeenmd.type = definition.getType(typestr); + parentemd.current = typeenmd.type; + parentemd.statik = true; + + final ExtdotContext dotctx = ctx.extdot(); + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } + + void processExtcall(final ExtcallContext ctx) { + final ExtNodeMetadata callenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = callenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + callenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + + final String name = ctx.EXTID().getText(); + + if (parentemd.current.sort == Sort.ARRAY) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unexpected call [" + name + "] on an array."); + } else if (callenmd.last && parentemd.storeExpr != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Cannot assign a value to a call [" + name + "]."); + } + + final Struct struct = parentemd.current.struct; + final List arguments = ctx.arguments().expression(); + final int size = arguments.size(); + Type[] types; + + final Method method = parentemd.statik ? struct.functions.get(name) : struct.methods.get(name); + final boolean def = parentemd.current.sort == Sort.DEF; + + if (method == null && !def) { + throw new IllegalArgumentException( + AnalyzerUtility.error(ctx) + "Unknown call [" + name + "] on type [" + struct.name + "]."); + } else if (method != null) { + types = new Type[method.arguments.size()]; + method.arguments.toArray(types); + + callenmd.target = method; + callenmd.type = method.rtn; + parentemd.statement = !parentemd.read && callenmd.last; + parentemd.current = method.rtn; + + if (size != types.length) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "When calling [" + name + "] on type " + + "[" + struct.name + "] expected [" + types.length + "] arguments," + + " but found [" + arguments.size() + "]."); + } + } else { + types = new Type[arguments.size()]; + Arrays.fill(types, definition.defType); + + callenmd.target = name; + callenmd.type = definition.defType; + parentemd.statement = !parentemd.read && callenmd.last; + parentemd.current = callenmd.type; + } + + for (int argument = 0; argument < size; ++argument) { + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(arguments.get(argument)); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = types[argument]; + analyzer.visit(exprctx); + caster.markCast(expremd); + } + + parentemd.statik = false; + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + void processExtvar(final ExtvarContext ctx) { + final ExtNodeMetadata varenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = varenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final String name = ctx.ID().getText(); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (parentemd.current != null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected variable [" + name + "] load."); + } + + varenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + + final Variable variable = utility.getVariable(name); + + if (variable == null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unknown variable [" + name + "]."); + } + + varenmd.target = variable.slot; + varenmd.type = variable.type; + analyzeLoadStoreExternal(ctx); + parentemd.current = varenmd.type; + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + void processExtfield(final ExtfieldContext ctx) { + final ExtNodeMetadata memberenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = memberenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + if (ctx.EXTID() == null && ctx.EXTINTEGER() == null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + + final String value = ctx.EXTID() == null ? ctx.EXTINTEGER().getText() : ctx.EXTID().getText(); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + memberenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + final boolean store = memberenmd.last && parentemd.storeExpr != null; + + if (parentemd.current == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected field [" + value + "] load."); + } + + if (parentemd.current.sort == Sort.ARRAY) { + if ("length".equals(value)) { + if (!parentemd.read) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Must read array field [length]."); + } else if (store) { + throw new IllegalArgumentException( + AnalyzerUtility.error(ctx) + "Cannot write to read-only array field [length]."); + } + + memberenmd.target = "#length"; + memberenmd.type = definition.intType; + parentemd.current = definition.intType; + } else { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unexpected array field [" + value + "]."); + } + } else if (parentemd.current.sort == Sort.DEF) { + memberenmd.target = value; + memberenmd.type = definition.defType; + analyzeLoadStoreExternal(ctx); + parentemd.current = memberenmd.type; + } else { + final Struct struct = parentemd.current.struct; + final Field field = parentemd.statik ? struct.statics.get(value) : struct.members.get(value); + + if (field != null) { + if (store && java.lang.reflect.Modifier.isFinal(field.reflect.getModifiers())) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Cannot write to read-only" + + " field [" + value + "] for type [" + struct.name + "]."); + } + + memberenmd.target = field; + memberenmd.type = field.type; + analyzeLoadStoreExternal(ctx); + parentemd.current = memberenmd.type; + } else { + final boolean get = parentemd.read || parentemd.token > 0 || !memberenmd.last; + final boolean set = memberenmd.last && store; + + Method getter = struct.methods.get("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1)); + Method setter = struct.methods.get("set" + Character.toUpperCase(value.charAt(0)) + value.substring(1)); + Object constant = null; + + if (getter != null && (getter.rtn.sort == Sort.VOID || !getter.arguments.isEmpty())) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal get shortcut on field [" + value + "] for type [" + struct.name + "]."); + } + + if (setter != null && (setter.rtn.sort != Sort.VOID || setter.arguments.size() != 1)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal set shortcut on field [" + value + "] for type [" + struct.name + "]."); + } + + Type settype = setter == null ? null : setter.arguments.get(0); + + if (getter == null && setter == null) { + if (ctx.EXTID() != null) { + try { + parentemd.current.clazz.asSubclass(Map.class); + + getter = parentemd.current.struct.methods.get("get"); + setter = parentemd.current.struct.methods.get("put"); + + if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || + getter.arguments.get(0).sort != Sort.STRING)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal map get shortcut [" + value + "] for type [" + struct.name + "]."); + } + + if (setter != null && (setter.arguments.size() != 2 || + setter.arguments.get(0).sort != Sort.STRING)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal map set shortcut [" + value + "] for type [" + struct.name + "]."); + } + + if (getter != null && setter != null && !getter.rtn.equals(setter.arguments.get(1))) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Shortcut argument types must match."); + } + + settype = setter == null ? null : setter.arguments.get(1); + constant = value; + } catch (ClassCastException exception) { + //Do nothing. + } + } else if (ctx.EXTINTEGER() != null) { + try { + parentemd.current.clazz.asSubclass(List.class); + + getter = parentemd.current.struct.methods.get("get"); + setter = parentemd.current.struct.methods.get("set"); + + if (getter != null && (getter.rtn.sort == Sort.VOID || getter.arguments.size() != 1 || + getter.arguments.get(0).sort != Sort.INT)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal list get shortcut [" + value + "] for type [" + struct.name + "]."); + } + + if (setter != null && (setter.rtn.sort != Sort.VOID || setter.arguments.size() != 2 || + setter.arguments.get(0).sort != Sort.INT)) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal list set shortcut [" + value + "] for type [" + struct.name + "]."); + } + + if (getter != null && setter != null && !getter.rtn.equals(setter.arguments.get(1))) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Shortcut argument types must match."); + } + + settype = setter == null ? null : setter.arguments.get(1); + + try { + constant = Integer.parseInt(value); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + + "Illegal list shortcut value [" + value + "]."); + } + } catch (ClassCastException exception) { + //Do nothing. + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + if ((get || set) && (!get || getter != null) && (!set || setter != null)) { + memberenmd.target = new Object[] {getter, setter, constant != null, constant}; + memberenmd.type = get ? getter.rtn : settype; + analyzeLoadStoreExternal(ctx); + parentemd.current = get ? getter.rtn : setter.rtn; + } + } + + if (memberenmd.target == null) { + throw new IllegalArgumentException( + AnalyzerUtility.error(ctx) + "Unknown field [" + value + "] for type [" + struct.name + "]."); + } + } + + parentemd.statik = false; + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + void processExtnew(final ExtnewContext ctx) { + final ExtNodeMetadata newenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = newenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + newenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + + final String name = ctx.TYPE().getText(); + final Struct struct = definition.structs.get(name); + + if (parentemd.current != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unexpected new call."); + } else if (struct == null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Specified type [" + name + "] not found."); + } else if (newenmd.last && parentemd.storeExpr != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Cannot assign a value to a new call."); + } + + final boolean newclass = ctx.arguments() != null; + final boolean newarray = !ctx.expression().isEmpty(); + + final List arguments = newclass ? ctx.arguments().expression() : ctx.expression(); + final int size = arguments.size(); + + Type[] types; + + if (newarray) { + if (!parentemd.read) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "A newly created array must be assigned."); + } + + types = new Type[size]; + Arrays.fill(types, definition.intType); + + newenmd.target = "#makearray"; + + if (size > 1) { + newenmd.type = definition.getType(struct, size); + parentemd.current = newenmd.type; + } else if (size == 1) { + newenmd.type = definition.getType(struct, 0); + parentemd.current = definition.getType(struct, 1); + } else { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "A newly created array cannot have zero dimensions."); + } + } else if (newclass) { + final Constructor constructor = struct.constructors.get("new"); + + if (constructor != null) { + types = new Type[constructor.arguments.size()]; + constructor.arguments.toArray(types); + + newenmd.target = constructor; + newenmd.type = definition.getType(struct, 0); + parentemd.statement = !parentemd.read && newenmd.last; + parentemd.current = newenmd.type; + } else { + throw new IllegalArgumentException( + AnalyzerUtility.error(ctx) + "Unknown new call on type [" + struct.name + "]."); + } + } else { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unknown state."); + } + + if (size != types.length) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "When calling [" + name + "] on type " + + "[" + struct.name + "] expected [" + types.length + "] arguments," + + " but found [" + arguments.size() + "]."); + } + + for (int argument = 0; argument < size; ++argument) { + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(arguments.get(argument)); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = types[argument]; + analyzer.visit(exprctx); + caster.markCast(expremd); + } + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + void processExtstring(final ExtstringContext ctx) { + final ExtNodeMetadata memberenmd = metadata.getExtNodeMetadata(ctx); + final ParserRuleContext parent = memberenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + final String string = ctx.STRING().getText(); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + memberenmd.last = parentemd.scope == 0 && dotctx == null && bracectx == null; + final boolean store = memberenmd.last && parentemd.storeExpr != null; + + if (parentemd.current != null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected String constant [" + string + "]."); + } + + if (!parentemd.read) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Must read String constant [" + string + "]."); + } else if (store) { + throw new IllegalArgumentException( + AnalyzerUtility.error(ctx) + "Cannot write to read-only String constant [" + string + "]."); + } + + memberenmd.target = string; + memberenmd.type = definition.stringType; + parentemd.current = definition.stringType; + + if (memberenmd.last) { + parentemd.constant = string; + } + + if (dotctx != null) { + metadata.createExtNodeMetadata(parent, dotctx); + analyzer.visit(dotctx); + } else if (bracectx != null) { + metadata.createExtNodeMetadata(parent, bracectx); + analyzer.visit(bracectx); + } + } + + private void analyzeLoadStoreExternal(final ParserRuleContext source) { + final ExtNodeMetadata extenmd = metadata.getExtNodeMetadata(source); + final ParserRuleContext parent = extenmd.parent; + final ExternalMetadata parentemd = metadata.getExternalMetadata(parent); + + if (extenmd.last && parentemd.storeExpr != null) { + final ParserRuleContext store = parentemd.storeExpr; + final ExpressionMetadata storeemd = metadata.createExpressionMetadata(parentemd.storeExpr); + final int token = parentemd.token; + + if (token > 0) { + analyzer.visit(store); + + final boolean add = token == ADD; + final boolean xor = token == BWAND || token == BWXOR || token == BWOR; + final boolean decimal = token == MUL || token == DIV || token == REM || token == SUB; + + extenmd.promote = add ? promoter.promoteAdd(extenmd.type, storeemd.from) : + xor ? promoter.promoteXor(extenmd.type, storeemd.from) : + promoter.promoteNumeric(extenmd.type, storeemd.from, decimal, true); + + if (extenmd.promote == null) { + throw new IllegalArgumentException("Cannot apply compound assignment to " + + "types [" + extenmd.type.name + "] and [" + storeemd.from.name + "]."); + } + + extenmd.castFrom = caster.getLegalCast(source, extenmd.type, extenmd.promote, false); + extenmd.castTo = caster.getLegalCast(source, extenmd.promote, extenmd.type, true); + + storeemd.to = add && extenmd.promote.sort == Sort.STRING ? storeemd.from : extenmd.promote; + caster.markCast(storeemd); + } else { + storeemd.to = extenmd.type; + analyzer.visit(store); + caster.markCast(storeemd); + } + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerPromoter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerPromoter.java new file mode 100644 index 00000000000..ff77fb06d93 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerPromoter.java @@ -0,0 +1,281 @@ +/* + * 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; + +import org.elasticsearch.painless.Definition.Pair; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; + +class AnalyzerPromoter { + private final Definition definition; + + AnalyzerPromoter(final Definition definition) { + this.definition = definition; + } + + Type promoteNumeric(final Type from, final boolean decimal, final boolean primitive) { + final Sort sort = from.sort; + + if (sort == Sort.DEF) { + return definition.defType; + } else if ((sort == Sort.DOUBLE || sort == Sort.DOUBLE_OBJ || sort == Sort.NUMBER) && decimal) { + return primitive ? definition.doubleType : definition.doubleobjType; + } else if ((sort == Sort.FLOAT || sort == Sort.FLOAT_OBJ) && decimal) { + return primitive ? definition.floatType : definition.floatobjType; + } else if (sort == Sort.LONG || sort == Sort.LONG_OBJ || sort == Sort.NUMBER) { + return primitive ? definition.longType : definition.longobjType; + } else if (sort.numeric) { + return primitive ? definition.intType : definition.intobjType; + } + + return null; + } + + Type promoteNumeric(final Type from0, final Type from1, final boolean decimal, final boolean primitive) { + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0 == Sort.DEF || sort1 == Sort.DEF) { + return definition.defType; + } + + if (decimal) { + if (sort0 == Sort.DOUBLE || sort0 == Sort.DOUBLE_OBJ || sort0 == Sort.NUMBER || + sort1 == Sort.DOUBLE || sort1 == Sort.DOUBLE_OBJ || sort1 == Sort.NUMBER) { + return primitive ? definition.doubleType : definition.doubleobjType; + } else if (sort0 == Sort.FLOAT || sort0 == Sort.FLOAT_OBJ || sort1 == Sort.FLOAT || sort1 == Sort.FLOAT_OBJ) { + return primitive ? definition.floatType : definition.floatobjType; + } + } + + if (sort0 == Sort.LONG || sort0 == Sort.LONG_OBJ || sort0 == Sort.NUMBER || + sort1 == Sort.LONG || sort1 == Sort.LONG_OBJ || sort1 == Sort.NUMBER) { + return primitive ? definition.longType : definition.longobjType; + } else if (sort0.numeric && sort1.numeric) { + return primitive ? definition.intType : definition.intobjType; + } + + return null; + } + + Type promoteAdd(final Type from0, final Type from1) { + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0 == Sort.STRING || sort1 == Sort.STRING) { + return definition.stringType; + } + + return promoteNumeric(from0, from1, true, true); + } + + Type promoteXor(final Type from0, final Type from1) { + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0.bool || sort1.bool) { + return definition.booleanType; + } + + return promoteNumeric(from0, from1, false, true); + } + + Type promoteEquality(final Type from0, final Type from1) { + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0 == Sort.DEF || sort1 == Sort.DEF) { + return definition.defType; + } + + final boolean primitive = sort0.primitive && sort1.primitive; + + if (sort0.bool && sort1.bool) { + return primitive ? definition.booleanType : definition.booleanobjType; + } + + if (sort0.numeric && sort1.numeric) { + return promoteNumeric(from0, from1, true, primitive); + } + + return definition.objectType; + } + + Type promoteReference(final Type from0, final Type from1) { + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0 == Sort.DEF || sort1 == Sort.DEF) { + return definition.defType; + } + + if (sort0.primitive && sort1.primitive) { + if (sort0.bool && sort1.bool) { + return definition.booleanType; + } + + if (sort0.numeric && sort1.numeric) { + return promoteNumeric(from0, from1, true, true); + } + } + + return definition.objectType; + } + + Type promoteConditional(final Type from0, final Type from1, final Object const0, final Object const1) { + if (from0.equals(from1)) { + return from0; + } + + final Sort sort0 = from0.sort; + final Sort sort1 = from1.sort; + + if (sort0 == Sort.DEF || sort1 == Sort.DEF) { + return definition.defType; + } + + final boolean primitive = sort0.primitive && sort1.primitive; + + if (sort0.bool && sort1.bool) { + return primitive ? definition.booleanType : definition.booleanobjType; + } + + if (sort0.numeric && sort1.numeric) { + if (sort0 == Sort.DOUBLE || sort0 == Sort.DOUBLE_OBJ || sort1 == Sort.DOUBLE || sort1 == Sort.DOUBLE_OBJ) { + return primitive ? definition.doubleType : definition.doubleobjType; + } else if (sort0 == Sort.FLOAT || sort0 == Sort.FLOAT_OBJ || sort1 == Sort.FLOAT || sort1 == Sort.FLOAT_OBJ) { + return primitive ? definition.floatType : definition.floatobjType; + } else if (sort0 == Sort.LONG || sort0 == Sort.LONG_OBJ || sort1 == Sort.LONG || sort1 == Sort.LONG_OBJ) { + return sort0.primitive && sort1.primitive ? definition.longType : definition.longobjType; + } else { + if (sort0 == Sort.BYTE || sort0 == Sort.BYTE_OBJ) { + if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { + return primitive ? definition.byteType : definition.byteobjType; + } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { + if (const1 != null) { + final short constant = (short)const1; + + if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.shortType : definition.shortobjType; + } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { + if (const1 != null) { + final int constant = (int)const1; + + if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } + } else if (sort0 == Sort.SHORT || sort0 == Sort.SHORT_OBJ) { + if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { + if (const0 != null) { + final short constant = (short)const0; + + if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.shortType : definition.shortobjType; + } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { + return primitive ? definition.shortType : definition.shortobjType; + } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { + if (const1 != null) { + final int constant = (int)const1; + + if (constant <= Short.MAX_VALUE && constant >= Short.MIN_VALUE) { + return primitive ? definition.shortType : definition.shortobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } + } else if (sort0 == Sort.CHAR || sort0 == Sort.CHAR_OBJ) { + if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { + return primitive ? definition.charType : definition.charobjType; + } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { + if (const1 != null) { + final int constant = (int)const1; + + if (constant <= Character.MAX_VALUE && constant >= Character.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } + } else if (sort0 == Sort.INT || sort0 == Sort.INT_OBJ) { + if (sort1 == Sort.BYTE || sort1 == Sort.BYTE_OBJ) { + if (const0 != null) { + final int constant = (int)const0; + + if (constant <= Byte.MAX_VALUE && constant >= Byte.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.SHORT || sort1 == Sort.SHORT_OBJ) { + if (const0 != null) { + final int constant = (int)const0; + + if (constant <= Short.MAX_VALUE && constant >= Short.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.CHAR || sort1 == Sort.CHAR_OBJ) { + if (const0 != null) { + final int constant = (int)const0; + + if (constant <= Character.MAX_VALUE && constant >= Character.MIN_VALUE) { + return primitive ? definition.byteType : definition.byteobjType; + } + } + + return primitive ? definition.intType : definition.intobjType; + } else if (sort1 == Sort.INT || sort1 == Sort.INT_OBJ) { + return primitive ? definition.intType : definition.intobjType; + } + } + } + } + + final Pair pair = new Pair(from0, from1); + final Type bound = definition.bounds.get(pair); + + return bound == null ? definition.objectType : bound; + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerStatement.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerStatement.java new file mode 100644 index 00000000000..e44336035e6 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerStatement.java @@ -0,0 +1,581 @@ +/* + * 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; + +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.Metadata.StatementMetadata; +import org.elasticsearch.painless.PainlessParser.AfterthoughtContext; +import org.elasticsearch.painless.PainlessParser.BlockContext; +import org.elasticsearch.painless.PainlessParser.BreakContext; +import org.elasticsearch.painless.PainlessParser.ContinueContext; +import org.elasticsearch.painless.PainlessParser.DeclContext; +import org.elasticsearch.painless.PainlessParser.DeclarationContext; +import org.elasticsearch.painless.PainlessParser.DecltypeContext; +import org.elasticsearch.painless.PainlessParser.DeclvarContext; +import org.elasticsearch.painless.PainlessParser.DoContext; +import org.elasticsearch.painless.PainlessParser.ExprContext; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ForContext; +import org.elasticsearch.painless.PainlessParser.IfContext; +import org.elasticsearch.painless.PainlessParser.InitializerContext; +import org.elasticsearch.painless.PainlessParser.MultipleContext; +import org.elasticsearch.painless.PainlessParser.ReturnContext; +import org.elasticsearch.painless.PainlessParser.SingleContext; +import org.elasticsearch.painless.PainlessParser.SourceContext; +import org.elasticsearch.painless.PainlessParser.StatementContext; +import org.elasticsearch.painless.PainlessParser.ThrowContext; +import org.elasticsearch.painless.PainlessParser.TrapContext; +import org.elasticsearch.painless.PainlessParser.TryContext; +import org.elasticsearch.painless.PainlessParser.WhileContext; + +import java.util.List; + +class AnalyzerStatement { + private final Metadata metadata; + private final Definition definition; + + private final Analyzer analyzer; + private final AnalyzerUtility utility; + private final AnalyzerCaster caster; + + AnalyzerStatement(final Metadata metadata, final Analyzer analyzer, + final AnalyzerUtility utility, final AnalyzerCaster caster) { + this.metadata = metadata; + this.definition = metadata.definition; + + this.analyzer = analyzer; + this.utility = utility; + this.caster = caster; + } + + void processSource(final SourceContext ctx) { + final StatementMetadata sourcesmd = metadata.getStatementMetadata(ctx); + final List statectxs = ctx.statement(); + final StatementContext lastctx = statectxs.get(statectxs.size() - 1); + + utility.incrementScope(); + + for (final StatementContext statectx : statectxs) { + if (sourcesmd.allLast) { + throw new IllegalArgumentException(AnalyzerUtility.error(statectx) + + "Statement will never be executed because all prior paths escape."); + } + + final StatementMetadata statesmd = metadata.createStatementMetadata(statectx); + statesmd.lastSource = statectx == lastctx; + analyzer.visit(statectx); + + sourcesmd.methodEscape = statesmd.methodEscape; + sourcesmd.allLast = statesmd.allLast; + } + + utility.decrementScope(); + } + + void processIf(final IfContext ctx) { + final StatementMetadata ifsmd = metadata.getStatementMetadata(ctx); + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.booleanType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + if (expremd.postConst != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "If statement is not necessary."); + } + + final BlockContext blockctx0 = ctx.block(0); + final StatementMetadata blocksmd0 = metadata.createStatementMetadata(blockctx0); + blocksmd0.lastSource = ifsmd.lastSource; + blocksmd0.inLoop = ifsmd.inLoop; + blocksmd0.lastLoop = ifsmd.lastLoop; + utility.incrementScope(); + analyzer.visit(blockctx0); + utility.decrementScope(); + + ifsmd.anyContinue = blocksmd0.anyContinue; + ifsmd.anyBreak = blocksmd0.anyBreak; + + ifsmd.count = blocksmd0.count; + + if (ctx.ELSE() != null) { + final BlockContext blockctx1 = ctx.block(1); + final StatementMetadata blocksmd1 = metadata.createStatementMetadata(blockctx1); + blocksmd1.lastSource = ifsmd.lastSource; + utility.incrementScope(); + analyzer.visit(blockctx1); + utility.decrementScope(); + + ifsmd.methodEscape = blocksmd0.methodEscape && blocksmd1.methodEscape; + ifsmd.loopEscape = blocksmd0.loopEscape && blocksmd1.loopEscape; + ifsmd.allLast = blocksmd0.allLast && blocksmd1.allLast; + ifsmd.anyContinue |= blocksmd1.anyContinue; + ifsmd.anyBreak |= blocksmd1.anyBreak; + + ifsmd.count = Math.max(ifsmd.count, blocksmd1.count); + } + } + + void processWhile(final WhileContext ctx) { + final StatementMetadata whilesmd = metadata.getStatementMetadata(ctx); + + utility.incrementScope(); + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.booleanType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + boolean continuous = false; + + if (expremd.postConst != null) { + continuous = (boolean)expremd.postConst; + + if (!continuous) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "The loop will never be executed."); + } + + if (ctx.empty() != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "The loop will never exit."); + } + } + + final BlockContext blockctx = ctx.block(); + + if (blockctx != null) { + final StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); + blocksmd.beginLoop = true; + blocksmd.inLoop = true; + analyzer.visit(blockctx); + + if (blocksmd.loopEscape && !blocksmd.anyContinue) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "All paths escape so the loop is not necessary."); + } + + if (continuous && !blocksmd.anyBreak) { + whilesmd.methodEscape = true; + whilesmd.allLast = true; + } + } + + whilesmd.count = 1; + + utility.decrementScope(); + } + + void processDo(final DoContext ctx) { + final StatementMetadata dosmd = metadata.getStatementMetadata(ctx); + + utility.incrementScope(); + + final BlockContext blockctx = ctx.block(); + final StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); + blocksmd.beginLoop = true; + blocksmd.inLoop = true; + analyzer.visit(blockctx); + + if (blocksmd.loopEscape && !blocksmd.anyContinue) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "All paths escape so the loop is not necessary."); + } + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.booleanType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + if (expremd.postConst != null) { + final boolean continuous = (boolean)expremd.postConst; + + if (!continuous) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "All paths escape so the loop is not necessary."); + } + + if (!blocksmd.anyBreak) { + dosmd.methodEscape = true; + dosmd.allLast = true; + } + } + + dosmd.count = 1; + + utility.decrementScope(); + } + + void processFor(final ForContext ctx) { + final StatementMetadata forsmd = metadata.getStatementMetadata(ctx); + boolean continuous = false; + + utility.incrementScope(); + + final InitializerContext initctx = ctx.initializer(); + + if (initctx != null) { + metadata.createStatementMetadata(initctx); + analyzer.visit(initctx); + } + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + + if (exprctx != null) { + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.booleanType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + if (expremd.postConst != null) { + continuous = (boolean)expremd.postConst; + + if (!continuous) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "The loop will never be executed."); + } + + if (ctx.empty() != null) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "The loop is continuous."); + } + } + } else { + continuous = true; + } + + final AfterthoughtContext atctx = ctx.afterthought(); + + if (atctx != null) { + metadata.createStatementMetadata(atctx); + analyzer.visit(atctx); + } + + final BlockContext blockctx = ctx.block(); + + if (blockctx != null) { + final StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); + blocksmd.beginLoop = true; + blocksmd.inLoop = true; + analyzer.visit(blockctx); + + if (blocksmd.loopEscape && !blocksmd.anyContinue) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "All paths escape so the loop is not necessary."); + } + + if (continuous && !blocksmd.anyBreak) { + forsmd.methodEscape = true; + forsmd.allLast = true; + } + } + + forsmd.count = 1; + + utility.decrementScope(); + } + + void processDecl(final DeclContext ctx) { + final StatementMetadata declsmd = metadata.getStatementMetadata(ctx); + + final DeclarationContext declctx = ctx.declaration(); + metadata.createStatementMetadata(declctx); + analyzer.visit(declctx); + + declsmd.count = 1; + } + + void processContinue(final ContinueContext ctx) { + final StatementMetadata continuesmd = metadata.getStatementMetadata(ctx); + + if (!continuesmd.inLoop) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Cannot have a continue statement outside of a loop."); + } + + if (continuesmd.lastLoop) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Unnecessary continue statement at the end of a loop."); + } + + continuesmd.allLast = true; + continuesmd.anyContinue = true; + + continuesmd.count = 1; + } + + void processBreak(final BreakContext ctx) { + final StatementMetadata breaksmd = metadata.getStatementMetadata(ctx); + + if (!breaksmd.inLoop) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Cannot have a break statement outside of a loop."); + } + + breaksmd.loopEscape = true; + breaksmd.allLast = true; + breaksmd.anyBreak = true; + + breaksmd.count = 1; + } + + void processReturn(final ReturnContext ctx) { + final StatementMetadata returnsmd = metadata.getStatementMetadata(ctx); + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.objectType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + returnsmd.methodEscape = true; + returnsmd.loopEscape = true; + returnsmd.allLast = true; + + returnsmd.count = 1; + } + + void processTry(final TryContext ctx) { + final StatementMetadata trysmd = metadata.getStatementMetadata(ctx); + + final BlockContext blockctx = ctx.block(); + final StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); + blocksmd.lastSource = trysmd.lastSource; + blocksmd.inLoop = trysmd.inLoop; + blocksmd.lastLoop = trysmd.lastLoop; + utility.incrementScope(); + analyzer.visit(blockctx); + utility.decrementScope(); + + trysmd.methodEscape = blocksmd.methodEscape; + trysmd.loopEscape = blocksmd.loopEscape; + trysmd.allLast = blocksmd.allLast; + trysmd.anyContinue = blocksmd.anyContinue; + trysmd.anyBreak = blocksmd.anyBreak; + + int trapcount = 0; + + for (final TrapContext trapctx : ctx.trap()) { + final StatementMetadata trapsmd = metadata.createStatementMetadata(trapctx); + trapsmd.lastSource = trysmd.lastSource; + trapsmd.inLoop = trysmd.inLoop; + trapsmd.lastLoop = trysmd.lastLoop; + utility.incrementScope(); + analyzer.visit(trapctx); + utility.decrementScope(); + + trysmd.methodEscape &= trapsmd.methodEscape; + trysmd.loopEscape &= trapsmd.loopEscape; + trysmd.allLast &= trapsmd.allLast; + trysmd.anyContinue |= trapsmd.anyContinue; + trysmd.anyBreak |= trapsmd.anyBreak; + + trapcount = Math.max(trapcount, trapsmd.count); + } + + trysmd.count = blocksmd.count + trapcount; + } + + void processThrow(final ThrowContext ctx) { + final StatementMetadata throwsmd = metadata.getStatementMetadata(ctx); + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = definition.exceptionType; + analyzer.visit(exprctx); + caster.markCast(expremd); + + throwsmd.methodEscape = true; + throwsmd.loopEscape = true; + throwsmd.allLast = true; + + throwsmd.count = 1; + } + + void processExpr(final ExprContext ctx) { + final StatementMetadata exprsmd = metadata.getStatementMetadata(ctx); + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.read = exprsmd.lastSource; + analyzer.visit(exprctx); + + if (!expremd.statement && !exprsmd.lastSource) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Not a statement."); + } + + final boolean rtn = exprsmd.lastSource && expremd.from.sort != Sort.VOID; + exprsmd.methodEscape = rtn; + exprsmd.loopEscape = rtn; + exprsmd.allLast = rtn; + expremd.to = rtn ? definition.objectType : expremd.from; + caster.markCast(expremd); + + exprsmd.count = 1; + } + + void processMultiple(final MultipleContext ctx) { + final StatementMetadata multiplesmd = metadata.getStatementMetadata(ctx); + final List statectxs = ctx.statement(); + final StatementContext lastctx = statectxs.get(statectxs.size() - 1); + + for (StatementContext statectx : statectxs) { + if (multiplesmd.allLast) { + throw new IllegalArgumentException(AnalyzerUtility.error(statectx) + + "Statement will never be executed because all prior paths escape."); + } + + final StatementMetadata statesmd = metadata.createStatementMetadata(statectx); + statesmd.lastSource = multiplesmd.lastSource && statectx == lastctx; + statesmd.inLoop = multiplesmd.inLoop; + statesmd.lastLoop = (multiplesmd.beginLoop || multiplesmd.lastLoop) && statectx == lastctx; + analyzer.visit(statectx); + + multiplesmd.methodEscape = statesmd.methodEscape; + multiplesmd.loopEscape = statesmd.loopEscape; + multiplesmd.allLast = statesmd.allLast; + multiplesmd.anyContinue |= statesmd.anyContinue; + multiplesmd.anyBreak |= statesmd.anyBreak; + + multiplesmd.count += statesmd.count; + } + } + + void processSingle(final SingleContext ctx) { + final StatementMetadata singlesmd = metadata.getStatementMetadata(ctx); + + final StatementContext statectx = ctx.statement(); + final StatementMetadata statesmd = metadata.createStatementMetadata(statectx); + statesmd.lastSource = singlesmd.lastSource; + statesmd.inLoop = singlesmd.inLoop; + statesmd.lastLoop = singlesmd.beginLoop || singlesmd.lastLoop; + analyzer.visit(statectx); + + singlesmd.methodEscape = statesmd.methodEscape; + singlesmd.loopEscape = statesmd.loopEscape; + singlesmd.allLast = statesmd.allLast; + singlesmd.anyContinue = statesmd.anyContinue; + singlesmd.anyBreak = statesmd.anyBreak; + + singlesmd.count = statesmd.count; + } + + void processInitializer(InitializerContext ctx) { + final DeclarationContext declctx = ctx.declaration(); + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + + if (declctx != null) { + metadata.createStatementMetadata(declctx); + analyzer.visit(declctx); + } else if (exprctx != null) { + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.read = false; + analyzer.visit(exprctx); + + expremd.to = expremd.from; + caster.markCast(expremd); + + if (!expremd.statement) { + throw new IllegalArgumentException(AnalyzerUtility.error(exprctx) + + "The initializer of a for loop must be a statement."); + } + } else { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } + + void processAfterthought(AfterthoughtContext ctx) { + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + + if (exprctx != null) { + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.read = false; + analyzer.visit(exprctx); + + expremd.to = expremd.from; + caster.markCast(expremd); + + if (!expremd.statement) { + throw new IllegalArgumentException(AnalyzerUtility.error(exprctx) + + "The afterthought of a for loop must be a statement."); + } + } + } + + void processDeclaration(final DeclarationContext ctx) { + final DecltypeContext decltypectx = ctx.decltype(); + final ExpressionMetadata decltypeemd = metadata.createExpressionMetadata(decltypectx); + analyzer.visit(decltypectx); + + for (final DeclvarContext declvarctx : ctx.declvar()) { + final ExpressionMetadata declvaremd = metadata.createExpressionMetadata(declvarctx); + declvaremd.to = decltypeemd.from; + analyzer.visit(declvarctx); + } + } + + void processDecltype(final DecltypeContext ctx) { + final ExpressionMetadata decltypeemd = metadata.getExpressionMetadata(ctx); + final String name = ctx.getText(); + decltypeemd.from = definition.getType(name); + } + + void processDeclvar(final DeclvarContext ctx) { + final ExpressionMetadata declvaremd = metadata.getExpressionMetadata(ctx); + + final String name = ctx.ID().getText(); + declvaremd.postConst = utility.addVariable(ctx, name, declvaremd.to).slot; + + final ExpressionContext exprctx = AnalyzerUtility.updateExpressionTree(ctx.expression()); + + if (exprctx != null) { + final ExpressionMetadata expremd = metadata.createExpressionMetadata(exprctx); + expremd.to = declvaremd.to; + analyzer.visit(exprctx); + caster.markCast(expremd); + } + } + + void processTrap(final TrapContext ctx) { + final StatementMetadata trapsmd = metadata.getStatementMetadata(ctx); + + final String type = ctx.TYPE().getText(); + trapsmd.exception = definition.getType(type); + + try { + trapsmd.exception.clazz.asSubclass(Exception.class); + } catch (final ClassCastException exception) { + throw new IllegalArgumentException(AnalyzerUtility.error(ctx) + "Invalid exception type [" + trapsmd.exception.name + "]."); + } + + final String id = ctx.ID().getText(); + trapsmd.slot = utility.addVariable(ctx, id, trapsmd.exception).slot; + + final BlockContext blockctx = ctx.block(); + + if (blockctx != null) { + final StatementMetadata blocksmd = metadata.createStatementMetadata(blockctx); + blocksmd.lastSource = trapsmd.lastSource; + blocksmd.inLoop = trapsmd.inLoop; + blocksmd.lastLoop = trapsmd.lastLoop; + analyzer.visit(blockctx); + + trapsmd.methodEscape = blocksmd.methodEscape; + trapsmd.loopEscape = blocksmd.loopEscape; + trapsmd.allLast = blocksmd.allLast; + trapsmd.anyContinue = blocksmd.anyContinue; + trapsmd.anyBreak = blocksmd.anyBreak; + } else if (ctx.emptyscope() == null) { + throw new IllegalStateException(AnalyzerUtility.error(ctx) + "Unexpected state."); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerUtility.java new file mode 100644 index 00000000000..11fb669f190 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerUtility.java @@ -0,0 +1,144 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTree; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.PrecedenceContext; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +class AnalyzerUtility { + static class Variable { + final String name; + final Type type; + final int slot; + + private Variable(final String name, final Type type, final int slot) { + this.name = name; + this.type = type; + this.slot = slot; + } + } + + /** + * A utility method to output consistent error messages. + * @param ctx The ANTLR node the error occurred in. + * @return The error message with tacked on line number and character position. + */ + static String error(final ParserRuleContext ctx) { + return "Analyzer Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: "; + } + + /** + * The ANTLR parse tree is modified in one single case; a parent node needs to check a child node to see if it's + * a precedence node, and if so, it must be removed from the tree permanently. Once the ANTLR tree is built, + * precedence nodes are no longer necessary to maintain the correct ordering of the tree, so they only + * add a level of indirection where complicated decisions about metadata passing would have to be made. This + * method removes the need for those decisions. + * @param source The child ANTLR node to check for precedence. + * @return The updated child ANTLR node. + */ + static ExpressionContext updateExpressionTree(ExpressionContext source) { + // Check to see if the ANTLR node is a precedence node. + if (source instanceof PainlessParser.PrecedenceContext) { + final ParserRuleContext parent = source.getParent(); + int index = 0; + + // Mark the index of the source node within the list of child nodes from the parent. + for (final ParseTree child : parent.children) { + if (child == source) { + break; + } + + ++index; + } + + // If there are multiple precedence nodes in a row, remove them all. + while (source instanceof PrecedenceContext) { + source = ((PrecedenceContext)source).expression(); + } + + // Update the parent node with the child of the precedence node. + parent.children.set(index, source); + } + + return source; + } + + private final Deque scopes = new ArrayDeque<>(); + private final Deque variables = new ArrayDeque<>(); + + void incrementScope() { + scopes.push(0); + } + + void decrementScope() { + int remove = scopes.pop(); + + while (remove > 0) { + variables.pop(); + --remove; + } + } + + Variable getVariable(final String name) { + final Iterator itr = variables.iterator(); + + while (itr.hasNext()) { + final Variable variable = itr.next(); + + if (variable.name.equals(name)) { + return variable; + } + } + + return null; + } + + Variable addVariable(final ParserRuleContext source, final String name, final Type type) { + if (getVariable(name) != null) { + if (source == null) { + throw new IllegalArgumentException("Argument name [" + name + "] already defined within the scope."); + } else { + throw new IllegalArgumentException(error(source) + "Variable name [" + name + "] already defined within the scope."); + } + } + + final Variable previous = variables.peekFirst(); + int slot = 0; + + if (previous != null) { + slot += previous.slot + previous.type.type.getSize(); + } + + final Variable variable = new Variable(name, type, slot); + variables.push(variable); + + final int update = scopes.pop() + 1; + scopes.push(update); + + return variable; + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index d1b0e2dc6fe..3d8123a4800 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -160,7 +160,7 @@ final class Compiler { // throw new RuntimeException(e); // } - final Class clazz = loader.define(Writer.CLASS_NAME, bytes); + final Class clazz = loader.define(WriterConstants.CLASS_NAME, bytes); final java.lang.reflect.Constructor constructor = clazz.getConstructor(Definition.class, String.class, String.class); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Metadata.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Metadata.java index cace48ff433..e38d6da7d98 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Metadata.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Metadata.java @@ -20,11 +20,8 @@ package org.elasticsearch.painless; import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTree; import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.PainlessParser.ExpressionContext; -import org.elasticsearch.painless.PainlessParser.PrecedenceContext; import java.util.HashMap; import java.util.Map; @@ -37,7 +34,6 @@ import java.util.Map; * the root of the ANTLR parse tree, and the {@link CompilerSettings}. */ class Metadata { - /** * StatementMetadata is used to store metadata mostly about * control flow for ANTLR nodes related to if/else, do, while, for, etc. @@ -386,15 +382,6 @@ class Metadata { } } - /** - * A utility method to output consistent error messages. - * @param ctx The ANTLR node the error occurred in. - * @return The error message with tacked on line number and character position. - */ - static String error(final ParserRuleContext ctx) { - return "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: "; - } - /** * Acts as both the Painless API and white-list for what types and methods are allowed. */ @@ -490,49 +477,13 @@ class Metadata { final StatementMetadata sourcesmd = statementMetadata.get(source); if (sourcesmd == null) { - throw new IllegalStateException(error(source) + "Statement metadata does not exist at" + + throw new IllegalStateException("Statement metadata does not exist at" + " the parse node with text [" + source.getText() + "]."); } return sourcesmd; } - /** - * The ANTLR parse tree is modified in one single case; a parent node needs to check a child node to see if it's - * a precedence node, and if so, it must be removed from the tree permanently. Once the ANTLR tree is built, - * precedence nodes are no longer necessary to maintain the correct ordering of the tree, so they only - * add a level of indirection where complicated decisions about metadata passing would have to be made. This - * method removes the need for those decisions. - * @param source The child ANTLR node to check for precedence. - * @return The updated child ANTLR node. - */ - ExpressionContext updateExpressionTree(ExpressionContext source) { - // Check to see if the ANTLR node is a precedence node. - if (source instanceof PrecedenceContext) { - final ParserRuleContext parent = source.getParent(); - int index = 0; - - // Mark the index of the source node within the list of child nodes from the parent. - for (final ParseTree child : parent.children) { - if (child == source) { - break; - } - - ++index; - } - - // If there are multiple precedence nodes in a row, remove them all. - while (source instanceof PrecedenceContext) { - source = ((PrecedenceContext)source).expression(); - } - - // Update the parent node with the child of the precedence node. - parent.children.set(index, source); - } - - return source; - } - /** * Creates a new ExpressionMetadata and stores it in the expressionMetadata map. * @param source The ANTLR node for this metadata. @@ -554,7 +505,7 @@ class Metadata { final ExpressionMetadata sourceemd = expressionMetadata.get(source); if (sourceemd == null) { - throw new IllegalStateException(error(source) + "Expression metadata does not exist at" + + throw new IllegalStateException("Expression metadata does not exist at" + " the parse node with text [" + source.getText() + "]."); } @@ -582,7 +533,7 @@ class Metadata { final ExternalMetadata sourceemd = externalMetadata.get(source); if (sourceemd == null) { - throw new IllegalStateException(error(source) + "External metadata does not exist at" + + throw new IllegalStateException("External metadata does not exist at" + " the parse node with text [" + source.getText() + "]."); } @@ -610,7 +561,7 @@ class Metadata { final ExtNodeMetadata sourceemd = extNodeMetadata.get(source); if (sourceemd == null) { - throw new IllegalStateException(error(source) + "External metadata does not exist at" + + throw new IllegalStateException("External metadata does not exist at" + " the parse node with text [" + source.getText() + "]."); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java index 431e724127f..4ddb260aea0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java @@ -19,20 +19,11 @@ package org.elasticsearch.painless; -import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.Constructor; -import org.elasticsearch.painless.Definition.Field; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Definition.Transform; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.PainlessParser.AfterthoughtContext; import org.elasticsearch.painless.PainlessParser.ArgumentsContext; import org.elasticsearch.painless.PainlessParser.AssignmentContext; import org.elasticsearch.painless.PainlessParser.BinaryContext; -import org.elasticsearch.painless.PainlessParser.BlockContext; import org.elasticsearch.painless.PainlessParser.BoolContext; import org.elasticsearch.painless.PainlessParser.BreakContext; import org.elasticsearch.painless.PainlessParser.CastContext; @@ -48,7 +39,6 @@ import org.elasticsearch.painless.PainlessParser.DoContext; import org.elasticsearch.painless.PainlessParser.EmptyContext; import org.elasticsearch.painless.PainlessParser.EmptyscopeContext; import org.elasticsearch.painless.PainlessParser.ExprContext; -import org.elasticsearch.painless.PainlessParser.ExpressionContext; import org.elasticsearch.painless.PainlessParser.ExtbraceContext; import org.elasticsearch.painless.PainlessParser.ExtcallContext; import org.elasticsearch.painless.PainlessParser.ExtcastContext; @@ -75,261 +65,82 @@ import org.elasticsearch.painless.PainlessParser.PreincContext; import org.elasticsearch.painless.PainlessParser.ReturnContext; import org.elasticsearch.painless.PainlessParser.SingleContext; import org.elasticsearch.painless.PainlessParser.SourceContext; -import org.elasticsearch.painless.PainlessParser.StatementContext; import org.elasticsearch.painless.PainlessParser.ThrowContext; import org.elasticsearch.painless.PainlessParser.TrapContext; import org.elasticsearch.painless.PainlessParser.TrueContext; import org.elasticsearch.painless.PainlessParser.TryContext; import org.elasticsearch.painless.PainlessParser.UnaryContext; import org.elasticsearch.painless.PainlessParser.WhileContext; -import org.elasticsearch.script.ScoreAccessor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.GeneratorAdapter; -import java.lang.invoke.MethodType; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.elasticsearch.painless.PainlessParser.ADD; -import static org.elasticsearch.painless.PainlessParser.BWAND; -import static org.elasticsearch.painless.PainlessParser.BWOR; -import static org.elasticsearch.painless.PainlessParser.BWXOR; -import static org.elasticsearch.painless.PainlessParser.DIV; -import static org.elasticsearch.painless.PainlessParser.LSH; -import static org.elasticsearch.painless.PainlessParser.MUL; -import static org.elasticsearch.painless.PainlessParser.REM; -import static org.elasticsearch.painless.PainlessParser.RSH; -import static org.elasticsearch.painless.PainlessParser.SUB; -import static org.elasticsearch.painless.PainlessParser.USH; +import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE; +import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; +import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR; +import static org.elasticsearch.painless.WriterConstants.EXECUTE; +import static org.elasticsearch.painless.WriterConstants.MAP_GET; +import static org.elasticsearch.painless.WriterConstants.MAP_TYPE; +import static org.elasticsearch.painless.WriterConstants.SCORE_ACCESSOR_FLOAT; +import static org.elasticsearch.painless.WriterConstants.SCORE_ACCESSOR_TYPE; +import static org.elasticsearch.painless.WriterConstants.SIGNATURE; class Writer extends PainlessParserBaseVisitor { - private static class Branch { - final ParserRuleContext source; - - Label begin = null; - Label end = null; - Label tru = null; - Label fals = null; - - private Branch(final ParserRuleContext source) { - this.source = source; - } - } - - final static String BASE_CLASS_NAME = Executable.class.getName(); - final static String CLASS_NAME = BASE_CLASS_NAME + "$CompiledPainlessExecutable"; - private final static org.objectweb.asm.Type BASE_CLASS_TYPE = org.objectweb.asm.Type.getType(Executable.class); - private final static org.objectweb.asm.Type CLASS_TYPE = org.objectweb.asm.Type.getType("L" + CLASS_NAME.replace(".", "/") + ";"); - - private final static org.objectweb.asm.commons.Method CONSTRUCTOR = - getAsmMethod(void.class, "", Definition.class, String.class, String.class); - private final static org.objectweb.asm.commons.Method EXECUTE = getAsmMethod(Object.class, "execute", Map.class); - private final static String SIGNATURE = "(Ljava/util/Map;)Ljava/lang/Object;"; - - private final static org.objectweb.asm.Type PAINLESS_ERROR_TYPE = org.objectweb.asm.Type.getType(PainlessError.class); - - private final static org.objectweb.asm.Type DEFINITION_TYPE = org.objectweb.asm.Type.getType(Definition.class); - - private final static org.objectweb.asm.Type MAP_TYPE = org.objectweb.asm.Type.getType(Map.class); - private final static org.objectweb.asm.commons.Method MAP_GET = getAsmMethod(Object.class, "get", Object.class); - - private final static org.objectweb.asm.Type SCORE_ACCESSOR_TYPE = org.objectweb.asm.Type.getType(ScoreAccessor.class); - private final static org.objectweb.asm.commons.Method SCORE_ACCESSOR_FLOAT = getAsmMethod(float.class, "floatValue"); - - private final static org.objectweb.asm.commons.Method DEF_METHOD_CALL = getAsmMethod( - Object.class, "methodCall", Object.class, String.class, Definition.class, Object[].class, boolean[].class); - private final static org.objectweb.asm.commons.Method DEF_ARRAY_STORE = getAsmMethod( - void.class, "arrayStore", Object.class, Object.class, Object.class, Definition.class, boolean.class, boolean.class); - private final static org.objectweb.asm.commons.Method DEF_ARRAY_LOAD = getAsmMethod( - Object.class, "arrayLoad", Object.class, Object.class, Definition.class, boolean.class); - private final static org.objectweb.asm.commons.Method DEF_FIELD_STORE = getAsmMethod( - void.class, "fieldStore", Object.class, Object.class, String.class, Definition.class, boolean.class); - private final static org.objectweb.asm.commons.Method DEF_FIELD_LOAD = getAsmMethod( - Object.class, "fieldLoad", Object.class, String.class, Definition.class); - - private final static org.objectweb.asm.commons.Method DEF_NOT_CALL = getAsmMethod(Object.class, "not", Object.class); - private final static org.objectweb.asm.commons.Method DEF_NEG_CALL = getAsmMethod(Object.class, "neg", Object.class); - private final static org.objectweb.asm.commons.Method DEF_MUL_CALL = getAsmMethod(Object.class, "mul", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_DIV_CALL = getAsmMethod(Object.class, "div", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_REM_CALL = getAsmMethod(Object.class, "rem", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_ADD_CALL = getAsmMethod(Object.class, "add", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_SUB_CALL = getAsmMethod(Object.class, "sub", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_LSH_CALL = getAsmMethod(Object.class, "lsh", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_RSH_CALL = getAsmMethod(Object.class, "rsh", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_USH_CALL = getAsmMethod(Object.class, "ush", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_AND_CALL = getAsmMethod(Object.class, "and", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_XOR_CALL = getAsmMethod(Object.class, "xor", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_OR_CALL = getAsmMethod(Object.class, "or" , Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_EQ_CALL = getAsmMethod(boolean.class, "eq" , Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_LT_CALL = getAsmMethod(boolean.class, "lt" , Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_LTE_CALL = getAsmMethod(boolean.class, "lte", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_GT_CALL = getAsmMethod(boolean.class, "gt" , Object.class, Object.class); - private final static org.objectweb.asm.commons.Method DEF_GTE_CALL = getAsmMethod(boolean.class, "gte", Object.class, Object.class); - - private final static org.objectweb.asm.Type STRINGBUILDER_TYPE = org.objectweb.asm.Type.getType(StringBuilder.class); - - private final static org.objectweb.asm.commons.Method STRINGBUILDER_CONSTRUCTOR = getAsmMethod(void.class, ""); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_BOOLEAN = - getAsmMethod(StringBuilder.class, "append", boolean.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_CHAR = - getAsmMethod(StringBuilder.class, "append", char.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_INT = - getAsmMethod(StringBuilder.class, "append", int.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_LONG = - getAsmMethod(StringBuilder.class, "append", long.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_FLOAT = - getAsmMethod(StringBuilder.class, "append", float.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_DOUBLE = - getAsmMethod(StringBuilder.class, "append", double.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_STRING = - getAsmMethod(StringBuilder.class, "append", String.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_APPEND_OBJECT = - getAsmMethod(StringBuilder.class, "append", Object.class); - private final static org.objectweb.asm.commons.Method STRINGBUILDER_TOSTRING = getAsmMethod(String.class, "toString"); - - private final static org.objectweb.asm.commons.Method TOINTEXACT_LONG = getAsmMethod(int.class, "toIntExact", long.class); - private final static org.objectweb.asm.commons.Method NEGATEEXACT_INT = getAsmMethod(int.class, "negateExact", int.class); - private final static org.objectweb.asm.commons.Method NEGATEEXACT_LONG = getAsmMethod(long.class, "negateExact", long.class); - private final static org.objectweb.asm.commons.Method MULEXACT_INT = getAsmMethod(int.class, "multiplyExact", int.class, int.class); - private final static org.objectweb.asm.commons.Method MULEXACT_LONG = getAsmMethod(long.class, "multiplyExact", long.class, long.class); - private final static org.objectweb.asm.commons.Method ADDEXACT_INT = getAsmMethod(int.class, "addExact", int.class, int.class); - private final static org.objectweb.asm.commons.Method ADDEXACT_LONG = getAsmMethod(long.class, "addExact", long.class, long.class); - private final static org.objectweb.asm.commons.Method SUBEXACT_INT = getAsmMethod(int.class, "subtractExact", int.class, int.class); - private final static org.objectweb.asm.commons.Method SUBEXACT_LONG = getAsmMethod(long.class, "subtractExact", long.class, long.class); - - private final static org.objectweb.asm.commons.Method CHECKEQUALS = - getAsmMethod(boolean.class, "checkEquals", Object.class, Object.class); - private final static org.objectweb.asm.commons.Method TOBYTEEXACT_INT = getAsmMethod(byte.class, "toByteExact", int.class); - private final static org.objectweb.asm.commons.Method TOBYTEEXACT_LONG = getAsmMethod(byte.class, "toByteExact", long.class); - private final static org.objectweb.asm.commons.Method TOBYTEWOOVERFLOW_FLOAT = - getAsmMethod(byte.class, "toByteWithoutOverflow", float.class); - private final static org.objectweb.asm.commons.Method TOBYTEWOOVERFLOW_DOUBLE = - getAsmMethod(byte.class, "toByteWithoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method TOSHORTEXACT_INT = getAsmMethod(short.class, "toShortExact", int.class); - private final static org.objectweb.asm.commons.Method TOSHORTEXACT_LONG = getAsmMethod(short.class, "toShortExact", long.class); - private final static org.objectweb.asm.commons.Method TOSHORTWOOVERFLOW_FLOAT = - getAsmMethod(short.class, "toShortWithoutOverflow", float.class); - private final static org.objectweb.asm.commons.Method TOSHORTWOOVERFLOW_DOUBLE = - getAsmMethod(short.class, "toShortWihtoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method TOCHAREXACT_INT = getAsmMethod(char.class, "toCharExact", int.class); - private final static org.objectweb.asm.commons.Method TOCHAREXACT_LONG = getAsmMethod(char.class, "toCharExact", long.class); - private final static org.objectweb.asm.commons.Method TOCHARWOOVERFLOW_FLOAT = - getAsmMethod(char.class, "toCharWithoutOverflow", float.class); - private final static org.objectweb.asm.commons.Method TOCHARWOOVERFLOW_DOUBLE = - getAsmMethod(char.class, "toCharWithoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method TOINTWOOVERFLOW_FLOAT = - getAsmMethod(int.class, "toIntWithoutOverflow", float.class); - private final static org.objectweb.asm.commons.Method TOINTWOOVERFLOW_DOUBLE = - getAsmMethod(int.class, "toIntWithoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method TOLONGWOOVERFLOW_FLOAT = - getAsmMethod(long.class, "toLongWithoutOverflow", float.class); - private final static org.objectweb.asm.commons.Method TOLONGWOOVERFLOW_DOUBLE = - getAsmMethod(long.class, "toLongWithoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method TOFLOATWOOVERFLOW_DOUBLE = - getAsmMethod(float.class , "toFloatWihtoutOverflow", double.class); - private final static org.objectweb.asm.commons.Method MULWOOVERLOW_FLOAT = - getAsmMethod(float.class, "multiplyWithoutOverflow", float.class, float.class); - private final static org.objectweb.asm.commons.Method MULWOOVERLOW_DOUBLE = - getAsmMethod(double.class, "multiplyWithoutOverflow", double.class, double.class); - private final static org.objectweb.asm.commons.Method DIVWOOVERLOW_INT = - getAsmMethod(int.class, "divideWithoutOverflow", int.class, int.class); - private final static org.objectweb.asm.commons.Method DIVWOOVERLOW_LONG = - getAsmMethod(long.class, "divideWithoutOverflow", long.class, long.class); - private final static org.objectweb.asm.commons.Method DIVWOOVERLOW_FLOAT = - getAsmMethod(float.class, "divideWithoutOverflow", float.class, float.class); - private final static org.objectweb.asm.commons.Method DIVWOOVERLOW_DOUBLE = - getAsmMethod(double.class, "divideWithoutOverflow", double.class, double.class); - private final static org.objectweb.asm.commons.Method REMWOOVERLOW_FLOAT = - getAsmMethod(float.class, "remainderWithoutOverflow", float.class, float.class); - private final static org.objectweb.asm.commons.Method REMWOOVERLOW_DOUBLE = - getAsmMethod(double.class, "remainderWithoutOverflow", double.class, double.class); - private final static org.objectweb.asm.commons.Method ADDWOOVERLOW_FLOAT = - getAsmMethod(float.class, "addWithoutOverflow", float.class, float.class); - private final static org.objectweb.asm.commons.Method ADDWOOVERLOW_DOUBLE = - getAsmMethod(double.class, "addWithoutOverflow", double.class, double.class); - private final static org.objectweb.asm.commons.Method SUBWOOVERLOW_FLOAT = - getAsmMethod(float.class, "subtractWithoutOverflow", float.class, float.class); - private final static org.objectweb.asm.commons.Method SUBWOOVERLOW_DOUBLE = - getAsmMethod(double.class, "subtractWithoutOverflow", double.class, double.class); - - private static org.objectweb.asm.commons.Method getAsmMethod(final Class rtype, final String name, final Class... ptypes) { - return new org.objectweb.asm.commons.Method(name, MethodType.methodType(rtype, ptypes).toMethodDescriptorString()); - } - static byte[] write(Metadata metadata) { - Writer writer = new Writer(metadata); + final Writer writer = new Writer(metadata); return writer.getBytes(); } private final Metadata metadata; - private final Definition definition; private final ParseTree root; private final String source; private final CompilerSettings settings; - private final Map branches = new HashMap<>(); - private final Deque jumps = new ArrayDeque<>(); - private final Set strings = new HashSet<>(); + private final ClassWriter writer; + private final GeneratorAdapter execute; - private ClassWriter writer; - private GeneratorAdapter execute; + private final WriterStatement statement; + private final WriterExpression expression; + private final WriterExternal external; private Writer(final Metadata metadata) { this.metadata = metadata; - definition = metadata.definition; root = metadata.root; source = metadata.source; settings = metadata.settings; + writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + writeBegin(); writeConstructor(); + + execute = new GeneratorAdapter(Opcodes.ACC_PUBLIC, EXECUTE, SIGNATURE, null, writer); + + final WriterUtility utility = new WriterUtility(metadata, execute); + final WriterCaster caster = new WriterCaster(execute); + + statement = new WriterStatement(metadata, execute, this, utility); + expression = new WriterExpression(metadata, execute, this, utility, caster); + external = new WriterExternal(metadata, execute, this, utility, caster); + writeExecute(); writeEnd(); } - private Branch markBranch(final ParserRuleContext source, final ParserRuleContext... nodes) { - final Branch branch = new Branch(source); - - for (final ParserRuleContext node : nodes) { - branches.put(node, branch); - } - - return branch; - } - - private void copyBranch(final Branch branch, final ParserRuleContext... nodes) { - for (final ParserRuleContext node : nodes) { - branches.put(node, branch); - } - } - - private Branch getBranch(final ParserRuleContext source) { - return branches.get(source); - } - private void writeBegin() { - final int compute = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; final int version = Opcodes.V1_7; final int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL; final String base = BASE_CLASS_TYPE.getInternalName(); final String name = CLASS_TYPE.getInternalName(); - writer = new ClassWriter(compute); writer.visit(version, access, name, null, base, null); writer.visitSource(source, null); } private void writeConstructor() { - final int access = Opcodes.ACC_PUBLIC; - final GeneratorAdapter constructor = new GeneratorAdapter(access, CONSTRUCTOR, null, null, writer); + final GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, null, null, writer); constructor.loadThis(); constructor.loadArgs(); constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR); @@ -338,9 +149,6 @@ class Writer extends PainlessParserBaseVisitor { } private void writeExecute() { - final int access = Opcodes.ACC_PUBLIC; - execute = new GeneratorAdapter(access, EXECUTE, SIGNATURE, null, writer); - final Label fals = new Label(); final Label end = new Label(); execute.visitVarInsn(Opcodes.ALOAD, metadata.inputValueSlot); @@ -364,2011 +172,6 @@ class Writer extends PainlessParserBaseVisitor { execute.endMethod(); } - @Override - public Void visitSource(final SourceContext ctx) { - final Metadata.StatementMetadata sourcesmd = metadata.getStatementMetadata(ctx); - - for (final StatementContext sctx : ctx.statement()) { - visit(sctx); - } - - if (!sourcesmd.methodEscape) { - execute.visitInsn(Opcodes.ACONST_NULL); - execute.returnValue(); - } - - return null; - } - - @Override - public Void visitIf(final IfContext ctx) { - final ExpressionContext exprctx = ctx.expression(); - final boolean els = ctx.ELSE() != null; - final Branch branch = markBranch(ctx, exprctx); - branch.end = new Label(); - branch.fals = els ? new Label() : branch.end; - - visit(exprctx); - - final BlockContext blockctx0 = ctx.block(0); - final Metadata.StatementMetadata blockmd0 = metadata.getStatementMetadata(blockctx0); - visit(blockctx0); - - if (els) { - if (!blockmd0.allLast) { - execute.goTo(branch.end); - } - - execute.mark(branch.fals); - visit(ctx.block(1)); - } - - execute.mark(branch.end); - - return null; - } - - @Override - public Void visitWhile(final WhileContext ctx) { - final ExpressionContext exprctx = ctx.expression(); - final Branch branch = markBranch(ctx, exprctx); - branch.begin = new Label(); - branch.end = new Label(); - branch.fals = branch.end; - - jumps.push(branch); - execute.mark(branch.begin); - visit(exprctx); - - final BlockContext blockctx = ctx.block(); - boolean allLast = false; - - if (blockctx != null) { - final Metadata.StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); - allLast = blocksmd.allLast; - writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1); - visit(blockctx); - } else if (ctx.empty() != null) { - writeLoopCounter(1); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - if (!allLast) { - execute.goTo(branch.begin); - } - - execute.mark(branch.end); - jumps.pop(); - - return null; - } - - @Override - public Void visitDo(final DoContext ctx) { - final ExpressionContext exprctx = ctx.expression(); - final Branch branch = markBranch(ctx, exprctx); - Label start = new Label(); - branch.begin = new Label(); - branch.end = new Label(); - branch.fals = branch.end; - - final BlockContext blockctx = ctx.block(); - final Metadata.StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); - - jumps.push(branch); - execute.mark(start); - visit(blockctx); - execute.mark(branch.begin); - visit(exprctx); - writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1); - execute.goTo(start); - execute.mark(branch.end); - jumps.pop(); - - return null; - } - - @Override - public Void visitFor(final ForContext ctx) { - final ExpressionContext exprctx = ctx.expression(); - final AfterthoughtContext atctx = ctx.afterthought(); - final Branch branch = markBranch(ctx, exprctx); - final Label start = new Label(); - branch.begin = atctx == null ? start : new Label(); - branch.end = new Label(); - branch.fals = branch.end; - - jumps.push(branch); - - if (ctx.initializer() != null) { - visit(ctx.initializer()); - } - - execute.mark(start); - - if (exprctx != null) { - visit(exprctx); - } - - final BlockContext blockctx = ctx.block(); - boolean allLast = false; - - if (blockctx != null) { - Metadata.StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); - allLast = blocksmd.allLast; - - int count = blocksmd.count > 0 ? blocksmd.count : 1; - - if (atctx != null) { - ++count; - } - - writeLoopCounter(count); - visit(blockctx); - } else if (ctx.empty() != null) { - writeLoopCounter(1); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - if (atctx != null) { - execute.mark(branch.begin); - visit(atctx); - } - - if (atctx != null || !allLast) { - execute.goTo(start); - } - - execute.mark(branch.end); - jumps.pop(); - - return null; - } - - @Override - public Void visitDecl(final DeclContext ctx) { - visit(ctx.declaration()); - - return null; - } - - @Override - public Void visitContinue(final ContinueContext ctx) { - final Branch jump = jumps.peek(); - execute.goTo(jump.begin); - - return null; - } - - @Override - public Void visitBreak(final BreakContext ctx) { - final Branch jump = jumps.peek(); - execute.goTo(jump.end); - - return null; - } - - @Override - public Void visitReturn(final ReturnContext ctx) { - visit(ctx.expression()); - execute.returnValue(); - - return null; - } - - @Override - public Void visitTry(final TryContext ctx) { - final TrapContext[] trapctxs = new TrapContext[ctx.trap().size()]; - ctx.trap().toArray(trapctxs); - final Branch branch = markBranch(ctx, trapctxs); - - Label end = new Label(); - branch.begin = new Label(); - branch.end = new Label(); - branch.tru = trapctxs.length > 1 ? end : null; - - execute.mark(branch.begin); - - final BlockContext blockctx = ctx.block(); - final Metadata.StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); - visit(blockctx); - - if (!blocksmd.allLast) { - execute.goTo(end); - } - - execute.mark(branch.end); - - for (final TrapContext trapctx : trapctxs) { - visit(trapctx); - } - - if (!blocksmd.allLast || trapctxs.length > 1) { - execute.mark(end); - } - - return null; - } - - @Override - public Void visitThrow(final ThrowContext ctx) { - visit(ctx.expression()); - execute.throwException(); - - return null; - } - - @Override - public Void visitExpr(final ExprContext ctx) { - final Metadata.StatementMetadata exprsmd = metadata.getStatementMetadata(ctx); - final ExpressionContext exprctx = ctx.expression(); - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); - visit(exprctx); - - if (exprsmd.methodEscape) { - execute.returnValue(); - } else { - writePop(expremd.to.type.getSize()); - } - - return null; - } - - @Override - public Void visitMultiple(final MultipleContext ctx) { - for (final StatementContext sctx : ctx.statement()) { - visit(sctx); - } - - return null; - } - - @Override - public Void visitSingle(final SingleContext ctx) { - visit(ctx.statement()); - - return null; - } - - @Override - public Void visitEmpty(final EmptyContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected writer state."); - } - - @Override - public Void visitInitializer(InitializerContext ctx) { - final DeclarationContext declctx = ctx.declaration(); - final ExpressionContext exprctx = ctx.expression(); - - if (declctx != null) { - visit(declctx); - } else if (exprctx != null) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); - visit(exprctx); - writePop(expremd.to.type.getSize()); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - return null; - } - - @Override - public Void visitAfterthought(AfterthoughtContext ctx) { - final ExpressionContext exprctx = ctx.expression(); - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); - visit(ctx.expression()); - writePop(expremd.to.type.getSize()); - - return null; - } - - @Override - public Void visitDeclaration(DeclarationContext ctx) { - for (final DeclvarContext declctx : ctx.declvar()) { - visit(declctx); - } - - return null; - } - - @Override - public Void visitDecltype(final DecltypeContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected writer state."); - } - - @Override - public Void visitDeclvar(final DeclvarContext ctx) { - final Metadata.ExpressionMetadata declvaremd = metadata.getExpressionMetadata(ctx); - final org.objectweb.asm.Type type = declvaremd.to.type; - final Sort sort = declvaremd.to.sort; - final int slot = (int)declvaremd.postConst; - - final ExpressionContext exprctx = ctx.expression(); - final boolean initialize = exprctx == null; - - if (!initialize) { - visit(exprctx); - } - - switch (sort) { - case VOID: throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - case BOOL: - case BYTE: - case SHORT: - case CHAR: - case INT: if (initialize) execute.push(0); break; - case LONG: if (initialize) execute.push(0L); break; - case FLOAT: if (initialize) execute.push(0.0F); break; - case DOUBLE: if (initialize) execute.push(0.0); break; - default: if (initialize) execute.visitInsn(Opcodes.ACONST_NULL); - } - - execute.visitVarInsn(type.getOpcode(Opcodes.ISTORE), slot); - - return null; - } - - @Override - public Void visitTrap(final TrapContext ctx) { - final Metadata.StatementMetadata trapsmd = metadata.getStatementMetadata(ctx); - - final Branch branch = getBranch(ctx); - final Label jump = new Label(); - - final BlockContext blockctx = ctx.block(); - final EmptyscopeContext emptyctx = ctx.emptyscope(); - - execute.mark(jump); - writeLoadStoreVariable(ctx, true, trapsmd.exception, trapsmd.slot); - - if (blockctx != null) { - visit(ctx.block()); - } else if (emptyctx == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - execute.visitTryCatchBlock(branch.begin, branch.end, jump, trapsmd.exception.type.getInternalName()); - - if (branch.tru != null && !trapsmd.allLast) { - execute.goTo(branch.tru); - } - - return null; - } - - @Override - public Void visitPrecedence(final PrecedenceContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected writer state."); - } - - @Override - public Void visitNumeric(final NumericContext ctx) { - final Metadata.ExpressionMetadata numericemd = metadata.getExpressionMetadata(ctx); - final Object postConst = numericemd.postConst; - - if (postConst == null) { - writeNumeric(ctx, numericemd.preConst); - checkWriteCast(numericemd); - } else { - writeConstant(ctx, postConst); - } - - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitChar(final CharContext ctx) { - final Metadata.ExpressionMetadata charemd = metadata.getExpressionMetadata(ctx); - final Object postConst = charemd.postConst; - - if (postConst == null) { - writeNumeric(ctx, (int)(char)charemd.preConst); - checkWriteCast(charemd); - } else { - writeConstant(ctx, postConst); - } - - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitTrue(final TrueContext ctx) { - final Metadata.ExpressionMetadata trueemd = metadata.getExpressionMetadata(ctx); - final Object postConst = trueemd.postConst; - final Branch branch = getBranch(ctx); - - if (branch == null) { - if (postConst == null) { - writeBoolean(ctx, true); - checkWriteCast(trueemd); - } else { - writeConstant(ctx, postConst); - } - } else if (branch.tru != null) { - execute.goTo(branch.tru); - } - - return null; - } - - @Override - public Void visitFalse(final FalseContext ctx) { - final Metadata.ExpressionMetadata falseemd = metadata.getExpressionMetadata(ctx); - final Object postConst = falseemd.postConst; - final Branch branch = getBranch(ctx); - - if (branch == null) { - if (postConst == null) { - writeBoolean(ctx, false); - checkWriteCast(falseemd); - } else { - writeConstant(ctx, postConst); - } - } else if (branch.fals != null) { - execute.goTo(branch.fals); - } - - return null; - } - - @Override - public Void visitNull(final NullContext ctx) { - final Metadata.ExpressionMetadata nullemd = metadata.getExpressionMetadata(ctx); - - execute.visitInsn(Opcodes.ACONST_NULL); - checkWriteCast(nullemd); - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitExternal(final ExternalContext ctx) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); - visit(ctx.extstart()); - checkWriteCast(expremd); - checkWriteBranch(ctx); - - return null; - } - - - @Override - public Void visitPostinc(final PostincContext ctx) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); - visit(ctx.extstart()); - checkWriteCast(expremd); - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitPreinc(final PreincContext ctx) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); - visit(ctx.extstart()); - checkWriteCast(expremd); - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitUnary(final UnaryContext ctx) { - final Metadata.ExpressionMetadata unaryemd = metadata.getExpressionMetadata(ctx); - final Object postConst = unaryemd.postConst; - final Object preConst = unaryemd.preConst; - final Branch branch = getBranch(ctx); - - if (postConst != null) { - if (ctx.BOOLNOT() != null) { - if (branch == null) { - writeConstant(ctx, postConst); - } else { - if ((boolean)postConst && branch.tru != null) { - execute.goTo(branch.tru); - } else if (!(boolean)postConst && branch.fals != null) { - execute.goTo(branch.fals); - } - } - } else { - writeConstant(ctx, postConst); - checkWriteBranch(ctx); - } - } else if (preConst != null) { - if (branch == null) { - writeConstant(ctx, preConst); - checkWriteCast(unaryemd); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } else { - final ExpressionContext exprctx = ctx.expression(); - - if (ctx.BOOLNOT() != null) { - final Branch local = markBranch(ctx, exprctx); - - if (branch == null) { - local.fals = new Label(); - final Label aend = new Label(); - - visit(exprctx); - - execute.push(false); - execute.goTo(aend); - execute.mark(local.fals); - execute.push(true); - execute.mark(aend); - - checkWriteCast(unaryemd); - } else { - local.tru = branch.fals; - local.fals = branch.tru; - - visit(exprctx); - } - } else { - final org.objectweb.asm.Type type = unaryemd.from.type; - final Sort sort = unaryemd.from.sort; - - visit(exprctx); - - if (ctx.BWNOT() != null) { - if (sort == Sort.DEF) { - execute.invokeStatic(definition.defobjType.type, DEF_NOT_CALL); - } else { - if (sort == Sort.INT) { - writeConstant(ctx, -1); - } else if (sort == Sort.LONG) { - writeConstant(ctx, -1L); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - execute.math(GeneratorAdapter.XOR, type); - } - } else if (ctx.SUB() != null) { - if (sort == Sort.DEF) { - execute.invokeStatic(definition.defobjType.type, DEF_NEG_CALL); - } else { - if (settings.getNumericOverflow()) { - execute.math(GeneratorAdapter.NEG, type); - } else { - if (sort == Sort.INT) { - execute.invokeStatic(definition.mathType.type, NEGATEEXACT_INT); - } else if (sort == Sort.LONG) { - execute.invokeStatic(definition.mathType.type, NEGATEEXACT_LONG); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } - } - } else if (ctx.ADD() == null) { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - checkWriteCast(unaryemd); - checkWriteBranch(ctx); - } - } - - return null; - } - - @Override - public Void visitCast(final CastContext ctx) { - final Metadata.ExpressionMetadata castemd = metadata.getExpressionMetadata(ctx); - final Object postConst = castemd.postConst; - - if (postConst == null) { - visit(ctx.expression()); - checkWriteCast(castemd); - } else { - writeConstant(ctx, postConst); - } - - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitBinary(final BinaryContext ctx) { - final Metadata.ExpressionMetadata binaryemd = metadata.getExpressionMetadata(ctx); - final Object postConst = binaryemd.postConst; - final Object preConst = binaryemd.preConst; - final Branch branch = getBranch(ctx); - - if (postConst != null) { - writeConstant(ctx, postConst); - } else if (preConst != null) { - if (branch == null) { - writeConstant(ctx, preConst); - checkWriteCast(binaryemd); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } else if (binaryemd.from.sort == Sort.STRING) { - final boolean marked = strings.contains(ctx); - - if (!marked) { - writeNewStrings(); - } - - final ExpressionContext exprctx0 = ctx.expression(0); - final Metadata.ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0); - strings.add(exprctx0); - visit(exprctx0); - - if (strings.contains(exprctx0)) { - writeAppendStrings(expremd0.from.sort); - strings.remove(exprctx0); - } - - final ExpressionContext exprctx1 = ctx.expression(1); - final Metadata.ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1); - strings.add(exprctx1); - visit(exprctx1); - - if (strings.contains(exprctx1)) { - writeAppendStrings(expremd1.from.sort); - strings.remove(exprctx1); - } - - if (marked) { - strings.remove(ctx); - } else { - writeToStrings(); - } - - checkWriteCast(binaryemd); - } else { - final ExpressionContext exprctx0 = ctx.expression(0); - final ExpressionContext exprctx1 = ctx.expression(1); - - visit(exprctx0); - visit(exprctx1); - - final Type type = binaryemd.from; - - if (ctx.MUL() != null) writeBinaryInstruction(ctx, type, MUL); - else if (ctx.DIV() != null) writeBinaryInstruction(ctx, type, DIV); - else if (ctx.REM() != null) writeBinaryInstruction(ctx, type, REM); - else if (ctx.ADD() != null) writeBinaryInstruction(ctx, type, ADD); - else if (ctx.SUB() != null) writeBinaryInstruction(ctx, type, SUB); - else if (ctx.LSH() != null) writeBinaryInstruction(ctx, type, LSH); - else if (ctx.USH() != null) writeBinaryInstruction(ctx, type, USH); - else if (ctx.RSH() != null) writeBinaryInstruction(ctx, type, RSH); - else if (ctx.BWAND() != null) writeBinaryInstruction(ctx, type, BWAND); - else if (ctx.BWXOR() != null) writeBinaryInstruction(ctx, type, BWXOR); - else if (ctx.BWOR() != null) writeBinaryInstruction(ctx, type, BWOR); - else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - checkWriteCast(binaryemd); - } - - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitComp(final CompContext ctx) { - final Metadata.ExpressionMetadata compemd = metadata.getExpressionMetadata(ctx); - final Object postConst = compemd.postConst; - final Object preConst = compemd.preConst; - final Branch branch = getBranch(ctx); - - if (postConst != null) { - if (branch == null) { - writeConstant(ctx, postConst); - } else { - if ((boolean)postConst && branch.tru != null) { - execute.mark(branch.tru); - } else if (!(boolean)postConst && branch.fals != null) { - execute.mark(branch.fals); - } - } - } else if (preConst != null) { - if (branch == null) { - writeConstant(ctx, preConst); - checkWriteCast(compemd); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } else { - final ExpressionContext exprctx0 = ctx.expression(0); - final Metadata.ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0); - - final ExpressionContext exprctx1 = ctx.expression(1); - final Metadata.ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1); - final org.objectweb.asm.Type type = expremd1.to.type; - final Sort sort1 = expremd1.to.sort; - - visit(exprctx0); - - if (!expremd1.isNull) { - visit(exprctx1); - } - - final boolean tru = branch != null && branch.tru != null; - final boolean fals = branch != null && branch.fals != null; - final Label jump = tru ? branch.tru : fals ? branch.fals : new Label(); - final Label end = new Label(); - - final boolean eq = (ctx.EQ() != null || ctx.EQR() != null) && (tru || !fals) || - (ctx.NE() != null || ctx.NER() != null) && fals; - final boolean ne = (ctx.NE() != null || ctx.NER() != null) && (tru || !fals) || - (ctx.EQ() != null || ctx.EQR() != null) && fals; - final boolean lt = ctx.LT() != null && (tru || !fals) || ctx.GTE() != null && fals; - final boolean lte = ctx.LTE() != null && (tru || !fals) || ctx.GT() != null && fals; - final boolean gt = ctx.GT() != null && (tru || !fals) || ctx.LTE() != null && fals; - final boolean gte = ctx.GTE() != null && (tru || !fals) || ctx.LT() != null && fals; - - boolean writejump = true; - - switch (sort1) { - case VOID: - case BYTE: - case SHORT: - case CHAR: - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - case BOOL: - if (eq) execute.ifZCmp(GeneratorAdapter.EQ, jump); - else if (ne) execute.ifZCmp(GeneratorAdapter.NE, jump); - else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - break; - case INT: - case LONG: - case FLOAT: - case DOUBLE: - if (eq) execute.ifCmp(type, GeneratorAdapter.EQ, jump); - else if (ne) execute.ifCmp(type, GeneratorAdapter.NE, jump); - else if (lt) execute.ifCmp(type, GeneratorAdapter.LT, jump); - else if (lte) execute.ifCmp(type, GeneratorAdapter.LE, jump); - else if (gt) execute.ifCmp(type, GeneratorAdapter.GT, jump); - else if (gte) execute.ifCmp(type, GeneratorAdapter.GE, jump); - else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - break; - case DEF: - if (eq) { - if (expremd1.isNull) { - execute.ifNull(jump); - } else if (!expremd0.isNull && ctx.EQ() != null) { - execute.invokeStatic(definition.defobjType.type, DEF_EQ_CALL); - } else { - execute.ifCmp(type, GeneratorAdapter.EQ, jump); - } - } else if (ne) { - if (expremd1.isNull) { - execute.ifNonNull(jump); - } else if (!expremd0.isNull && ctx.NE() != null) { - execute.invokeStatic(definition.defobjType.type, DEF_EQ_CALL); - execute.ifZCmp(GeneratorAdapter.EQ, jump); - } else { - execute.ifCmp(type, GeneratorAdapter.NE, jump); - } - } else if (lt) { - execute.invokeStatic(definition.defobjType.type, DEF_LT_CALL); - } else if (lte) { - execute.invokeStatic(definition.defobjType.type, DEF_LTE_CALL); - } else if (gt) { - execute.invokeStatic(definition.defobjType.type, DEF_GT_CALL); - } else if (gte) { - execute.invokeStatic(definition.defobjType.type, DEF_GTE_CALL); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - writejump = expremd1.isNull || ne || ctx.EQR() != null; - - if (branch != null && !writejump) { - execute.ifZCmp(GeneratorAdapter.NE, jump); - } - - break; - default: - if (eq) { - if (expremd1.isNull) { - execute.ifNull(jump); - } else if (ctx.EQ() != null) { - execute.invokeStatic(definition.utilityType.type, CHECKEQUALS); - - if (branch != null) { - execute.ifZCmp(GeneratorAdapter.NE, jump); - } - - writejump = false; - } else { - execute.ifCmp(type, GeneratorAdapter.EQ, jump); - } - } else if (ne) { - if (expremd1.isNull) { - execute.ifNonNull(jump); - } else if (ctx.NE() != null) { - execute.invokeStatic(definition.utilityType.type, CHECKEQUALS); - execute.ifZCmp(GeneratorAdapter.EQ, jump); - } else { - execute.ifCmp(type, GeneratorAdapter.NE, jump); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } - - if (branch == null) { - if (writejump) { - execute.push(false); - execute.goTo(end); - execute.mark(jump); - execute.push(true); - execute.mark(end); - } - - checkWriteCast(compemd); - } - } - - return null; - } - - @Override - public Void visitBool(final BoolContext ctx) { - final Metadata.ExpressionMetadata boolemd = metadata.getExpressionMetadata(ctx); - final Object postConst = boolemd.postConst; - final Object preConst = boolemd.preConst; - final Branch branch = getBranch(ctx); - - if (postConst != null) { - if (branch == null) { - writeConstant(ctx, postConst); - } else { - if ((boolean)postConst && branch.tru != null) { - execute.mark(branch.tru); - } else if (!(boolean)postConst && branch.fals != null) { - execute.mark(branch.fals); - } - } - } else if (preConst != null) { - if (branch == null) { - writeConstant(ctx, preConst); - checkWriteCast(boolemd); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } else { - final ExpressionContext exprctx0 = ctx.expression(0); - final ExpressionContext exprctx1 = ctx.expression(1); - - if (branch == null) { - if (ctx.BOOLAND() != null) { - final Branch local = markBranch(ctx, exprctx0, exprctx1); - local.fals = new Label(); - final Label end = new Label(); - - visit(exprctx0); - visit(exprctx1); - - execute.push(true); - execute.goTo(end); - execute.mark(local.fals); - execute.push(false); - execute.mark(end); - } else if (ctx.BOOLOR() != null) { - final Branch branch0 = markBranch(ctx, exprctx0); - branch0.tru = new Label(); - final Branch branch1 = markBranch(ctx, exprctx1); - branch1.fals = new Label(); - final Label aend = new Label(); - - visit(exprctx0); - visit(exprctx1); - - execute.mark(branch0.tru); - execute.push(true); - execute.goTo(aend); - execute.mark(branch1.fals); - execute.push(false); - execute.mark(aend); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - checkWriteCast(boolemd); - } else { - if (ctx.BOOLAND() != null) { - final Branch branch0 = markBranch(ctx, exprctx0); - branch0.fals = branch.fals == null ? new Label() : branch.fals; - final Branch branch1 = markBranch(ctx, exprctx1); - branch1.tru = branch.tru; - branch1.fals = branch.fals; - - visit(exprctx0); - visit(exprctx1); - - if (branch.fals == null) { - execute.mark(branch0.fals); - } - } else if (ctx.BOOLOR() != null) { - final Branch branch0 = markBranch(ctx, exprctx0); - branch0.tru = branch.tru == null ? new Label() : branch.tru; - final Branch branch1 = markBranch(ctx, exprctx1); - branch1.tru = branch.tru; - branch1.fals = branch.fals; - - visit(exprctx0); - visit(exprctx1); - - if (branch.tru == null) { - execute.mark(branch0.tru); - } - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - } - } - - return null; - } - - @Override - public Void visitConditional(final ConditionalContext ctx) { - final Metadata.ExpressionMetadata condemd = metadata.getExpressionMetadata(ctx); - final Branch branch = getBranch(ctx); - - final ExpressionContext expr0 = ctx.expression(0); - final ExpressionContext expr1 = ctx.expression(1); - final ExpressionContext expr2 = ctx.expression(2); - - final Branch local = markBranch(ctx, expr0); - local.fals = new Label(); - local.end = new Label(); - - if (branch != null) { - copyBranch(branch, expr1, expr2); - } - - visit(expr0); - visit(expr1); - execute.goTo(local.end); - execute.mark(local.fals); - visit(expr2); - execute.mark(local.end); - - if (branch == null) { - checkWriteCast(condemd); - } - - return null; - } - - @Override - public Void visitAssignment(final AssignmentContext ctx) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); - visit(ctx.extstart()); - checkWriteCast(expremd); - checkWriteBranch(ctx); - - return null; - } - - @Override - public Void visitExtstart(ExtstartContext ctx) { - final Metadata.ExternalMetadata startemd = metadata.getExternalMetadata(ctx); - - if (startemd.token == ADD) { - final Metadata.ExpressionMetadata storeemd = metadata.getExpressionMetadata(startemd.storeExpr); - - if (startemd.current.sort == Sort.STRING || storeemd.from.sort == Sort.STRING) { - writeNewStrings(); - strings.add(startemd.storeExpr); - } - } - - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - if (precctx != null) { - visit(precctx); - } else if (castctx != null) { - visit(castctx); - } else if (typectx != null) { - visit(typectx); - } else if (varctx != null) { - visit(varctx); - } else if (newctx != null) { - visit(newctx); - } else if (stringctx != null) { - visit(stringctx); - } else { - throw new IllegalStateException(); - } - - return null; - } - - @Override - public Void visitExtprec(final ExtprecContext ctx) { - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - if (precctx != null) { - visit(precctx); - } else if (castctx != null) { - visit(castctx); - } else if (typectx != null) { - visit(typectx); - } else if (varctx != null) { - visit(varctx); - } else if (newctx != null) { - visit(newctx); - } else if (stringctx != null) { - visit(stringctx); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtcast(final ExtcastContext ctx) { - Metadata.ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx); - - final ExtprecContext precctx = ctx.extprec(); - final ExtcastContext castctx = ctx.extcast(); - final ExttypeContext typectx = ctx.exttype(); - final ExtvarContext varctx = ctx.extvar(); - final ExtnewContext newctx = ctx.extnew(); - final ExtstringContext stringctx = ctx.extstring(); - - if (precctx != null) { - visit(precctx); - } else if (castctx != null) { - visit(castctx); - } else if (typectx != null) { - visit(typectx); - } else if (varctx != null) { - visit(varctx); - } else if (newctx != null) { - visit(newctx); - } else if (stringctx != null) { - visit(stringctx); - } else { - throw new IllegalStateException(Metadata.error(ctx) + "Unexpected writer state."); - } - - checkWriteCast(ctx, castenmd.castTo); - - return null; - } - - @Override - public Void visitExtbrace(final ExtbraceContext ctx) { - final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression()); - - visit(exprctx); - writeLoadStoreExternal(ctx); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtdot(final ExtdotContext ctx) { - final ExtcallContext callctx = ctx.extcall(); - final ExtfieldContext fieldctx = ctx.extfield(); - - if (callctx != null) { - visit(callctx); - } else if (fieldctx != null) { - visit(fieldctx); - } - - return null; - } - - @Override - public Void visitExttype(final ExttypeContext ctx) { - visit(ctx.extdot()); - - return null; - } - - @Override - public Void visitExtcall(final ExtcallContext ctx) { - writeCallExternal(ctx); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtvar(final ExtvarContext ctx) { - writeLoadStoreExternal(ctx); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtfield(final ExtfieldContext ctx) { - writeLoadStoreExternal(ctx); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtnew(ExtnewContext ctx) { - writeNewExternal(ctx); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitExtstring(ExtstringContext ctx) { - final Metadata.ExtNodeMetadata stringenmd = metadata.getExtNodeMetadata(ctx); - - writeConstant(ctx, stringenmd.target); - - final ExtdotContext dotctx = ctx.extdot(); - final ExtbraceContext bracectx = ctx.extbrace(); - - if (dotctx != null) { - visit(dotctx); - } else if (bracectx != null) { - visit(bracectx); - } - - return null; - } - - @Override - public Void visitArguments(final ArgumentsContext ctx) { - throw new UnsupportedOperationException(Metadata.error(ctx) + "Unexpected writer state."); - } - - @Override - public Void visitIncrement(IncrementContext ctx) { - final Metadata.ExpressionMetadata incremd = metadata.getExpressionMetadata(ctx); - final Object postConst = incremd.postConst; - - if (postConst == null) { - writeNumeric(ctx, incremd.preConst); - checkWriteCast(incremd); - } else { - writeConstant(ctx, postConst); - } - - checkWriteBranch(ctx); - - return null; - } - - private void writeLoopCounter(final int count) { - final Label end = new Label(); - - execute.iinc(metadata.loopCounterSlot, -count); - execute.visitVarInsn(Opcodes.ILOAD, metadata.loopCounterSlot); - execute.push(0); - execute.ifICmp(GeneratorAdapter.GT, end); - execute.throwException(PAINLESS_ERROR_TYPE, - "The maximum number of statements that can be executed in a loop has been reached."); - execute.mark(end); - } - - private void writeConstant(final ParserRuleContext source, final Object constant) { - if (constant instanceof Number) { - writeNumeric(source, constant); - } else if (constant instanceof Character) { - writeNumeric(source, (int)(char)constant); - } else if (constant instanceof String) { - writeString(source, constant); - } else if (constant instanceof Boolean) { - writeBoolean(source, constant); - } else if (constant != null) { - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } - - private void writeNumeric(final ParserRuleContext source, final Object numeric) { - if (numeric instanceof Double) { - execute.push((double)numeric); - } else if (numeric instanceof Float) { - execute.push((float)numeric); - } else if (numeric instanceof Long) { - execute.push((long)numeric); - } else if (numeric instanceof Number) { - execute.push(((Number)numeric).intValue()); - } else { - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } - - private void writeString(final ParserRuleContext source, final Object string) { - if (string instanceof String) { - execute.push((String)string); - } else { - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } - - private void writeBoolean(final ParserRuleContext source, final Object bool) { - if (bool instanceof Boolean) { - execute.push((boolean)bool); - } else { - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } - - private void writeNewStrings() { - execute.newInstance(STRINGBUILDER_TYPE); - execute.dup(); - execute.invokeConstructor(STRINGBUILDER_TYPE, STRINGBUILDER_CONSTRUCTOR); - } - - private void writeAppendStrings(final Sort sort) { - switch (sort) { - case BOOL: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_BOOLEAN); break; - case CHAR: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_CHAR); break; - case BYTE: - case SHORT: - case INT: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_INT); break; - case LONG: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_LONG); break; - case FLOAT: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_FLOAT); break; - case DOUBLE: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_DOUBLE); break; - case STRING: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_STRING); break; - default: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_OBJECT); - } - } - - private void writeToStrings() { - execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_TOSTRING); - } - - private void writeBinaryInstruction(final ParserRuleContext source, final Type type, final int token) { - final Sort sort = type.sort; - final boolean exact = !settings.getNumericOverflow() && - ((sort == Sort.INT || sort == Sort.LONG) && - (token == MUL || token == DIV || token == ADD || token == SUB) || - (sort == Sort.FLOAT || sort == Sort.DOUBLE) && - (token == MUL || token == DIV || token == REM || token == ADD || token == SUB)); - - // if its a 64-bit shift, fixup the lastSource argument to truncate to 32-bits - // note unlike java, this means we still do binary promotion of shifts, - // but it keeps things simple -- this check works because we promote shifts. - if (sort == Sort.LONG && (token == LSH || token == USH || token == RSH)) { - execute.cast(org.objectweb.asm.Type.LONG_TYPE, org.objectweb.asm.Type.INT_TYPE); - } - - if (exact) { - switch (sort) { - case INT: - switch (token) { - case MUL: execute.invokeStatic(definition.mathType.type, MULEXACT_INT); break; - case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_INT); break; - case ADD: execute.invokeStatic(definition.mathType.type, ADDEXACT_INT); break; - case SUB: execute.invokeStatic(definition.mathType.type, SUBEXACT_INT); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - - break; - case LONG: - switch (token) { - case MUL: execute.invokeStatic(definition.mathType.type, MULEXACT_LONG); break; - case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_LONG); break; - case ADD: execute.invokeStatic(definition.mathType.type, ADDEXACT_LONG); break; - case SUB: execute.invokeStatic(definition.mathType.type, SUBEXACT_LONG); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - - break; - case FLOAT: - switch (token) { - case MUL: execute.invokeStatic(definition.utilityType.type, MULWOOVERLOW_FLOAT); break; - case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_FLOAT); break; - case REM: execute.invokeStatic(definition.utilityType.type, REMWOOVERLOW_FLOAT); break; - case ADD: execute.invokeStatic(definition.utilityType.type, ADDWOOVERLOW_FLOAT); break; - case SUB: execute.invokeStatic(definition.utilityType.type, SUBWOOVERLOW_FLOAT); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - - break; - case DOUBLE: - switch (token) { - case MUL: execute.invokeStatic(definition.utilityType.type, MULWOOVERLOW_DOUBLE); break; - case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_DOUBLE); break; - case REM: execute.invokeStatic(definition.utilityType.type, REMWOOVERLOW_DOUBLE); break; - case ADD: execute.invokeStatic(definition.utilityType.type, ADDWOOVERLOW_DOUBLE); break; - case SUB: execute.invokeStatic(definition.utilityType.type, SUBWOOVERLOW_DOUBLE); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - - break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } else { - if ((sort == Sort.FLOAT || sort == Sort.DOUBLE) && - (token == LSH || token == USH || token == RSH || token == BWAND || token == BWXOR || token == BWOR)) { - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - - if (sort == Sort.DEF) { - switch (token) { - case MUL: execute.invokeStatic(definition.defobjType.type, DEF_MUL_CALL); break; - case DIV: execute.invokeStatic(definition.defobjType.type, DEF_DIV_CALL); break; - case REM: execute.invokeStatic(definition.defobjType.type, DEF_REM_CALL); break; - case ADD: execute.invokeStatic(definition.defobjType.type, DEF_ADD_CALL); break; - case SUB: execute.invokeStatic(definition.defobjType.type, DEF_SUB_CALL); break; - case LSH: execute.invokeStatic(definition.defobjType.type, DEF_LSH_CALL); break; - case USH: execute.invokeStatic(definition.defobjType.type, DEF_RSH_CALL); break; - case RSH: execute.invokeStatic(definition.defobjType.type, DEF_USH_CALL); break; - case BWAND: execute.invokeStatic(definition.defobjType.type, DEF_AND_CALL); break; - case BWXOR: execute.invokeStatic(definition.defobjType.type, DEF_XOR_CALL); break; - case BWOR: execute.invokeStatic(definition.defobjType.type, DEF_OR_CALL); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } else { - switch (token) { - case MUL: execute.math(GeneratorAdapter.MUL, type.type); break; - case DIV: execute.math(GeneratorAdapter.DIV, type.type); break; - case REM: execute.math(GeneratorAdapter.REM, type.type); break; - case ADD: execute.math(GeneratorAdapter.ADD, type.type); break; - case SUB: execute.math(GeneratorAdapter.SUB, type.type); break; - case LSH: execute.math(GeneratorAdapter.SHL, type.type); break; - case USH: execute.math(GeneratorAdapter.USHR, type.type); break; - case RSH: execute.math(GeneratorAdapter.SHR, type.type); break; - case BWAND: execute.math(GeneratorAdapter.AND, type.type); break; - case BWXOR: execute.math(GeneratorAdapter.XOR, type.type); break; - case BWOR: execute.math(GeneratorAdapter.OR, type.type); break; - default: - throw new IllegalStateException(Metadata.error(source) + "Unexpected writer state."); - } - } - } - } - - /** - * Called for any compound assignment (including increment/decrement instructions). - * We have to be stricter than writeBinary, and do overflow checks against the original type's size - * instead of the promoted type's size, since the result will be implicitly cast back. - * - * @return true if an instruction is written, false otherwise - */ - private boolean writeExactInstruction(final Sort osort, final Sort psort) { - if (psort == Sort.DOUBLE) { - if (osort == Sort.FLOAT) { - execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE); - } else if (osort == Sort.FLOAT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE); - execute.checkCast(definition.floatobjType.type); - } else if (osort == Sort.LONG) { - execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE); - } else if (osort == Sort.LONG_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE); - execute.checkCast(definition.longobjType.type); - } else if (osort == Sort.INT) { - execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE); - } else if (osort == Sort.INT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE); - execute.checkCast(definition.intobjType.type); - } else if (osort == Sort.CHAR) { - execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE); - } else if (osort == Sort.CHAR_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE); - execute.checkCast(definition.charobjType.type); - } else if (osort == Sort.SHORT) { - execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE); - } else if (osort == Sort.SHORT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE); - execute.checkCast(definition.shortobjType.type); - } else if (osort == Sort.BYTE) { - execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE); - } else if (osort == Sort.BYTE_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE); - execute.checkCast(definition.byteobjType.type); - } else { - return false; - } - } else if (psort == Sort.FLOAT) { - if (osort == Sort.LONG) { - execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT); - } else if (osort == Sort.LONG_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT); - execute.checkCast(definition.longobjType.type); - } else if (osort == Sort.INT) { - execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT); - } else if (osort == Sort.INT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT); - execute.checkCast(definition.intobjType.type); - } else if (osort == Sort.CHAR) { - execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT); - } else if (osort == Sort.CHAR_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT); - execute.checkCast(definition.charobjType.type); - } else if (osort == Sort.SHORT) { - execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT); - } else if (osort == Sort.SHORT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT); - execute.checkCast(definition.shortobjType.type); - } else if (osort == Sort.BYTE) { - execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT); - } else if (osort == Sort.BYTE_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT); - execute.checkCast(definition.byteobjType.type); - } else { - return false; - } - } else if (psort == Sort.LONG) { - if (osort == Sort.INT) { - execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG); - } else if (osort == Sort.INT_OBJ) { - execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG); - execute.checkCast(definition.intobjType.type); - } else if (osort == Sort.CHAR) { - execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG); - } else if (osort == Sort.CHAR_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG); - execute.checkCast(definition.charobjType.type); - } else if (osort == Sort.SHORT) { - execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG); - } else if (osort == Sort.SHORT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG); - execute.checkCast(definition.shortobjType.type); - } else if (osort == Sort.BYTE) { - execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG); - } else if (osort == Sort.BYTE_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG); - execute.checkCast(definition.byteobjType.type); - } else { - return false; - } - } else if (psort == Sort.INT) { - if (osort == Sort.CHAR) { - execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT); - } else if (osort == Sort.CHAR_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT); - execute.checkCast(definition.charobjType.type); - } else if (osort == Sort.SHORT) { - execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT); - } else if (osort == Sort.SHORT_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT); - execute.checkCast(definition.shortobjType.type); - } else if (osort == Sort.BYTE) { - execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT); - } else if (osort == Sort.BYTE_OBJ) { - execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT); - execute.checkCast(definition.byteobjType.type); - } else { - return false; - } - } else { - return false; - } - - return true; - } - - private void writeLoadStoreExternal(final ParserRuleContext source) { - final Metadata.ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent); - - final boolean length = "#length".equals(sourceenmd.target); - final boolean array = "#brace".equals(sourceenmd.target); - final boolean name = sourceenmd.target instanceof String && !length && !array; - final boolean variable = sourceenmd.target instanceof Integer; - final boolean field = sourceenmd.target instanceof Field; - final boolean shortcut = sourceenmd.target instanceof Object[]; - - if (!length && !variable && !field && !array && !name && !shortcut) { - throw new IllegalStateException(Metadata.error(source) + "Target not found for load/store."); - } - - final boolean maplist = shortcut && (boolean)((Object[])sourceenmd.target)[2]; - final Object constant = shortcut ? ((Object[])sourceenmd.target)[3] : null; - - final boolean x1 = field || name || (shortcut && !maplist); - final boolean x2 = array || (shortcut && maplist); - - if (length) { - execute.arrayLength(); - } else if (sourceenmd.last && parentemd.storeExpr != null) { - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr); - final boolean cat = strings.contains(parentemd.storeExpr); - - if (cat) { - if (field || name || shortcut) { - execute.dupX1(); - } else if (array) { - execute.dup2X1(); - } - - if (maplist) { - if (constant != null) { - writeConstant(source, constant); - } - - execute.dupX2(); - } - - writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); - writeAppendStrings(sourceenmd.type.sort); - visit(parentemd.storeExpr); - - if (strings.contains(parentemd.storeExpr)) { - writeAppendStrings(expremd.to.sort); - strings.remove(parentemd.storeExpr); - } - - writeToStrings(); - checkWriteCast(source, sourceenmd.castTo); - - if (parentemd.read) { - writeDup(sourceenmd.type.sort.size, x1, x2); - } - - writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); - } else if (parentemd.token > 0) { - final int token = parentemd.token; - - if (field || name || shortcut) { - execute.dup(); - } else if (array) { - execute.dup2(); - } - - if (maplist) { - if (constant != null) { - writeConstant(source, constant); - } - - execute.dupX1(); - } - - writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); - - if (parentemd.read && parentemd.post) { - writeDup(sourceenmd.type.sort.size, x1, x2); - } - - checkWriteCast(source, sourceenmd.castFrom); - visit(parentemd.storeExpr); - - writeBinaryInstruction(source, sourceenmd.promote, token); - - boolean exact = false; - - if (!settings.getNumericOverflow() && expremd.typesafe && sourceenmd.type.sort != Sort.DEF && - (token == MUL || token == DIV || token == REM || token == ADD || token == SUB)) { - exact = writeExactInstruction(sourceenmd.type.sort, sourceenmd.promote.sort); - } - - if (!exact) { - checkWriteCast(source, sourceenmd.castTo); - } - - if (parentemd.read && !parentemd.post) { - writeDup(sourceenmd.type.sort.size, x1, x2); - } - - writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); - } else { - if (constant != null) { - writeConstant(source, constant); - } - - visit(parentemd.storeExpr); - - if (parentemd.read) { - writeDup(sourceenmd.type.sort.size, x1, x2); - } - - writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); - } - } else { - if (constant != null) { - writeConstant(source, constant); - } - - writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); - } - } - - private void writeLoadStoreInstruction(final ParserRuleContext source, - final boolean store, final boolean variable, - final boolean field, final boolean name, - final boolean array, final boolean shortcut) { - final Metadata.ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source); - - if (variable) { - writeLoadStoreVariable(source, store, sourceemd.type, (int)sourceemd.target); - } else if (field) { - writeLoadStoreField(store, (Field)sourceemd.target); - } else if (name) { - writeLoadStoreField(source, store, (String)sourceemd.target); - } else if (array) { - writeLoadStoreArray(source, store, sourceemd.type); - } else if (shortcut) { - Object[] targets = (Object[])sourceemd.target; - writeLoadStoreShortcut(store, (Method)targets[0], (Method)targets[1]); - } else { - throw new IllegalStateException(Metadata.error(source) + "Load/Store requires a variable, field, or array."); - } - } - - private void writeLoadStoreVariable(final ParserRuleContext source, final boolean store, - final Type type, final int slot) { - if (type.sort == Sort.VOID) { - throw new IllegalStateException(Metadata.error(source) + "Cannot load/store void type."); - } - - if (store) { - execute.visitVarInsn(type.type.getOpcode(Opcodes.ISTORE), slot); - } else { - execute.visitVarInsn(type.type.getOpcode(Opcodes.ILOAD), slot); - } - } - - private void writeLoadStoreField(final boolean store, final Field field) { - if (java.lang.reflect.Modifier.isStatic(field.reflect.getModifiers())) { - if (store) { - execute.putStatic(field.owner.type, field.reflect.getName(), field.type.type); - } else { - execute.getStatic(field.owner.type, field.reflect.getName(), field.type.type); - - if (!field.generic.clazz.equals(field.type.clazz)) { - execute.checkCast(field.generic.type); - } - } - } else { - if (store) { - execute.putField(field.owner.type, field.reflect.getName(), field.type.type); - } else { - execute.getField(field.owner.type, field.reflect.getName(), field.type.type); - - if (!field.generic.clazz.equals(field.type.clazz)) { - execute.checkCast(field.generic.type); - } - } - } - } - - private void writeLoadStoreField(final ParserRuleContext source, final boolean store, final String name) { - if (store) { - final Metadata.ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source); - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(sourceemd.parent); - final Metadata.ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr); - - execute.push(name); - execute.loadThis(); - execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); - execute.push(parentemd.token == 0 && expremd.typesafe); - execute.invokeStatic(definition.defobjType.type, DEF_FIELD_STORE); - } else { - execute.push(name); - execute.loadThis(); - execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); - execute.invokeStatic(definition.defobjType.type, DEF_FIELD_LOAD); - } - } - - private void writeLoadStoreArray(final ParserRuleContext source, final boolean store, final Type type) { - if (type.sort == Sort.VOID) { - throw new IllegalStateException(Metadata.error(source) + "Cannot load/store void type."); - } - - if (type.sort == Sort.DEF) { - final ExtbraceContext bracectx = (ExtbraceContext)source; - final Metadata.ExpressionMetadata expremd0 = metadata.getExpressionMetadata(bracectx.expression()); - - if (store) { - final Metadata.ExtNodeMetadata braceenmd = metadata.getExtNodeMetadata(bracectx); - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(braceenmd.parent); - final Metadata.ExpressionMetadata expremd1 = metadata.getExpressionMetadata(parentemd.storeExpr); - - execute.loadThis(); - execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); - execute.push(expremd0.typesafe); - execute.push(parentemd.token == 0 && expremd1.typesafe); - execute.invokeStatic(definition.defobjType.type, DEF_ARRAY_STORE); - } else { - execute.loadThis(); - execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); - execute.push(expremd0.typesafe); - execute.invokeStatic(definition.defobjType.type, DEF_ARRAY_LOAD); - } - } else { - if (store) { - execute.arrayStore(type.type); - } else { - execute.arrayLoad(type.type); - } - } - } - - private void writeLoadStoreShortcut(final boolean store, final Method getter, final Method setter) { - final Method method = store ? setter : getter; - - if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) { - execute.invokeInterface(method.owner.type, method.method); - } else { - execute.invokeVirtual(method.owner.type, method.method); - } - - if (store) { - writePop(method.rtn.type.getSize()); - } else if (!method.rtn.clazz.equals(method.handle.type().returnType())) { - execute.checkCast(method.rtn.type); - } - } - - private void writeDup(final int size, final boolean x1, final boolean x2) { - if (size == 1) { - if (x2) { - execute.dupX2(); - } else if (x1) { - execute.dupX1(); - } else { - execute.dup(); - } - } else if (size == 2) { - if (x2) { - execute.dup2X2(); - } else if (x1) { - execute.dup2X1(); - } else { - execute.dup2(); - } - } - } - - private void writeNewExternal(final ExtnewContext source) { - final Metadata.ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); - final Metadata.ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent); - - final boolean makearray = "#makearray".equals(sourceenmd.target); - final boolean constructor = sourceenmd.target instanceof Constructor; - - if (!makearray && !constructor) { - throw new IllegalStateException(Metadata.error(source) + "Target not found for new call."); - } - - if (makearray) { - for (final ExpressionContext exprctx : source.expression()) { - visit(exprctx); - } - - if (sourceenmd.type.sort == Sort.ARRAY) { - execute.visitMultiANewArrayInsn(sourceenmd.type.type.getDescriptor(), sourceenmd.type.type.getDimensions()); - } else { - execute.newArray(sourceenmd.type.type); - } - } else { - execute.newInstance(sourceenmd.type.type); - - if (parentemd.read) { - execute.dup(); - } - - for (final ExpressionContext exprctx : source.arguments().expression()) { - visit(exprctx); - } - - final Constructor target = (Constructor)sourceenmd.target; - execute.invokeConstructor(target.owner.type, target.method); - } - } - - private void writeCallExternal(final ExtcallContext source) { - final Metadata.ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); - - final boolean method = sourceenmd.target instanceof Method; - final boolean def = sourceenmd.target instanceof String; - - if (!method && !def) { - throw new IllegalStateException(Metadata.error(source) + "Target not found for call."); - } - - final List arguments = source.arguments().expression(); - - if (method) { - for (final ExpressionContext exprctx : arguments) { - visit(exprctx); - } - - final Method target = (Method)sourceenmd.target; - - if (java.lang.reflect.Modifier.isStatic(target.reflect.getModifiers())) { - execute.invokeStatic(target.owner.type, target.method); - } else if (java.lang.reflect.Modifier.isInterface(target.owner.clazz.getModifiers())) { - execute.invokeInterface(target.owner.type, target.method); - } else { - execute.invokeVirtual(target.owner.type, target.method); - } - - if (!target.rtn.clazz.equals(target.handle.type().returnType())) { - execute.checkCast(target.rtn.type); - } - } else { - execute.push((String)sourceenmd.target); - execute.loadThis(); - execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); - - execute.push(arguments.size()); - execute.newArray(definition.defType.type); - - for (int argument = 0; argument < arguments.size(); ++argument) { - execute.dup(); - execute.push(argument); - visit(arguments.get(argument)); - execute.arrayStore(definition.defType.type); - } - - execute.push(arguments.size()); - execute.newArray(definition.booleanType.type); - - for (int argument = 0; argument < arguments.size(); ++argument) { - execute.dup(); - execute.push(argument); - execute.push(metadata.getExpressionMetadata(arguments.get(argument)).typesafe); - execute.arrayStore(definition.booleanType.type); - } - - execute.invokeStatic(definition.defobjType.type, DEF_METHOD_CALL); - } - } - - private void writePop(final int size) { - if (size == 1) { - execute.pop(); - } else if (size == 2) { - execute.pop2(); - } - } - - private void checkWriteCast(final Metadata.ExpressionMetadata sort) { - checkWriteCast(sort.source, sort.cast); - } - - private void checkWriteCast(final ParserRuleContext source, final Cast cast) { - if (cast instanceof Transform) { - writeTransform((Transform)cast); - } else if (cast != null) { - writeCast(cast); - } else { - throw new IllegalStateException(Metadata.error(source) + "Unexpected cast object."); - } - } - - private void writeCast(final Cast cast) { - final Type from = cast.from; - final Type to = cast.to; - - if (from.equals(to)) { - return; - } - - if (from.sort.numeric && from.sort.primitive && to.sort.numeric && to.sort.primitive) { - execute.cast(from.type, to.type); - } else { - try { - from.clazz.asSubclass(to.clazz); - } catch (ClassCastException exception) { - execute.checkCast(to.type); - } - } - } - - private void writeTransform(final Transform transform) { - if (transform.upcast != null) { - execute.checkCast(transform.upcast.type); - } - - if (java.lang.reflect.Modifier.isStatic(transform.method.reflect.getModifiers())) { - execute.invokeStatic(transform.method.owner.type, transform.method.method); - } else if (java.lang.reflect.Modifier.isInterface(transform.method.owner.clazz.getModifiers())) { - execute.invokeInterface(transform.method.owner.type, transform.method.method); - } else { - execute.invokeVirtual(transform.method.owner.type, transform.method.method); - } - - if (transform.downcast != null) { - execute.checkCast(transform.downcast.type); - } - } - - void checkWriteBranch(final ParserRuleContext source) { - final Branch branch = getBranch(source); - - if (branch != null) { - if (branch.tru != null) { - execute.visitJumpInsn(Opcodes.IFNE, branch.tru); - } else if (branch.fals != null) { - execute.visitJumpInsn(Opcodes.IFEQ, branch.fals); - } - } - } - private void writeEnd() { writer.visitEnd(); } @@ -2376,4 +179,352 @@ class Writer extends PainlessParserBaseVisitor { private byte[] getBytes() { return writer.toByteArray(); } + + @Override + public Void visitSource(final SourceContext ctx) { + statement.processSource(ctx); + + return null; + } + + @Override + public Void visitIf(final IfContext ctx) { + statement.processIf(ctx); + + return null; + } + + @Override + public Void visitWhile(final WhileContext ctx) { + statement.processWhile(ctx); + + return null; + } + + @Override + public Void visitDo(final DoContext ctx) { + statement.processDo(ctx); + + return null; + } + + @Override + public Void visitFor(final ForContext ctx) { + statement.processFor(ctx); + + return null; + } + + @Override + public Void visitDecl(final DeclContext ctx) { + statement.processDecl(ctx); + + return null; + } + + @Override + public Void visitContinue(final ContinueContext ctx) { + statement.processContinue(); + + return null; + } + + @Override + public Void visitBreak(final BreakContext ctx) { + statement.processBreak(); + + return null; + } + + @Override + public Void visitReturn(final ReturnContext ctx) { + statement.processReturn(ctx); + + return null; + } + + @Override + public Void visitTry(final TryContext ctx) { + statement.processTry(ctx); + + return null; + } + + @Override + public Void visitThrow(final ThrowContext ctx) { + statement.processThrow(ctx); + + return null; + } + + @Override + public Void visitExpr(final ExprContext ctx) { + statement.processExpr(ctx); + + return null; + } + + @Override + public Void visitMultiple(final MultipleContext ctx) { + statement.processMultiple(ctx); + + return null; + } + + @Override + public Void visitSingle(final SingleContext ctx) { + statement.processSingle(ctx); + + return null; + } + + @Override + public Void visitEmpty(final EmptyContext ctx) { + throw new UnsupportedOperationException(WriterUtility.error(ctx) + "Unexpected state."); + } + + @Override + public Void visitEmptyscope(final EmptyscopeContext ctx) { + throw new UnsupportedOperationException(WriterUtility.error(ctx) + "Unexpected state."); + } + + @Override + public Void visitInitializer(final InitializerContext ctx) { + statement.processInitializer(ctx); + + return null; + } + + @Override + public Void visitAfterthought(final AfterthoughtContext ctx) { + statement.processAfterthought(ctx); + + return null; + } + + @Override + public Void visitDeclaration(DeclarationContext ctx) { + statement.processDeclaration(ctx); + + return null; + } + + @Override + public Void visitDecltype(final DecltypeContext ctx) { + throw new UnsupportedOperationException(WriterUtility.error(ctx) + "Unexpected state."); + } + + @Override + public Void visitDeclvar(final DeclvarContext ctx) { + statement.processDeclvar(ctx); + + return null; + } + + @Override + public Void visitTrap(final TrapContext ctx) { + statement.processTrap(ctx); + + return null; + } + + @Override + public Void visitPrecedence(final PrecedenceContext ctx) { + throw new UnsupportedOperationException(WriterUtility.error(ctx) + "Unexpected state."); + } + + @Override + public Void visitNumeric(final NumericContext ctx) { + expression.processNumeric(ctx); + + return null; + } + + @Override + public Void visitChar(final CharContext ctx) { + expression.processChar(ctx); + + return null; + } + + @Override + public Void visitTrue(final TrueContext ctx) { + expression.processTrue(ctx); + + return null; + } + + @Override + public Void visitFalse(final FalseContext ctx) { + expression.processFalse(ctx); + + return null; + } + + @Override + public Void visitNull(final NullContext ctx) { + expression.processNull(ctx); + + return null; + } + + @Override + public Void visitExternal(final ExternalContext ctx) { + expression.processExternal(ctx); + + return null; + } + + + @Override + public Void visitPostinc(final PostincContext ctx) { + expression.processPostinc(ctx); + + return null; + } + + @Override + public Void visitPreinc(final PreincContext ctx) { + expression.processPreinc(ctx); + + return null; + } + + @Override + public Void visitUnary(final UnaryContext ctx) { + expression.processUnary(ctx); + + return null; + } + + @Override + public Void visitCast(final CastContext ctx) { + expression.processCast(ctx); + + return null; + } + + @Override + public Void visitBinary(final BinaryContext ctx) { + expression.processBinary(ctx); + + return null; + } + + @Override + public Void visitComp(final CompContext ctx) { + expression.processComp(ctx); + + return null; + } + + @Override + public Void visitBool(final BoolContext ctx) { + expression.processBool(ctx); + + return null; + } + + @Override + public Void visitConditional(final ConditionalContext ctx) { + expression.processConditional(ctx); + + return null; + } + + @Override + public Void visitAssignment(final AssignmentContext ctx) { + expression.processAssignment(ctx); + + return null; + } + + @Override + public Void visitExtstart(final ExtstartContext ctx) { + external.processExtstart(ctx); + + return null; + } + + @Override + public Void visitExtprec(final ExtprecContext ctx) { + external.processExtprec(ctx); + + return null; + } + + @Override + public Void visitExtcast(final ExtcastContext ctx) { + external.processExtcast(ctx); + + return null; + } + + @Override + public Void visitExtbrace(final ExtbraceContext ctx) { + external.processExtbrace(ctx); + + return null; + } + + @Override + public Void visitExtdot(final ExtdotContext ctx) { + external.processExtdot(ctx); + + return null; + } + + @Override + public Void visitExttype(final ExttypeContext ctx) { + external.processExttype(ctx); + + return null; + } + + @Override + public Void visitExtcall(final ExtcallContext ctx) { + external.processExtcall(ctx); + + return null; + } + + @Override + public Void visitExtvar(final ExtvarContext ctx) { + external.processExtvar(ctx); + + return null; + } + + @Override + public Void visitExtfield(final ExtfieldContext ctx) { + external.processExtfield(ctx); + + return null; + } + + @Override + public Void visitExtnew(final ExtnewContext ctx) { + external.processExtnew(ctx); + + return null; + } + + @Override + public Void visitExtstring(final ExtstringContext ctx) { + external.processExtstring(ctx); + + return null; + } + + @Override + public Void visitArguments(final ArgumentsContext ctx) { + throw new UnsupportedOperationException(WriterUtility.error(ctx) + "Unexpected state."); + } + + @Override + public Void visitIncrement(final IncrementContext ctx) { + expression.processIncrement(ctx); + + return null; + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterCaster.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterCaster.java new file mode 100644 index 00000000000..c55dff2f549 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterCaster.java @@ -0,0 +1,86 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.Definition.Transform; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.objectweb.asm.commons.GeneratorAdapter; + +class WriterCaster { + private final GeneratorAdapter execute; + + WriterCaster(final GeneratorAdapter execute) { + this.execute = execute; + } + + void checkWriteCast(final ExpressionMetadata sort) { + checkWriteCast(sort.source, sort.cast); + } + + void checkWriteCast(final ParserRuleContext source, final Cast cast) { + if (cast instanceof Transform) { + writeTransform((Transform)cast); + } else if (cast != null) { + writeCast(cast); + } else { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected cast object."); + } + } + + private void writeCast(final Cast cast) { + final Type from = cast.from; + final Type to = cast.to; + + if (from.equals(to)) { + return; + } + + if (from.sort.numeric && from.sort.primitive && to.sort.numeric && to.sort.primitive) { + execute.cast(from.type, to.type); + } else { + try { + from.clazz.asSubclass(to.clazz); + } catch (ClassCastException exception) { + execute.checkCast(to.type); + } + } + } + + private void writeTransform(final Transform transform) { + if (transform.upcast != null) { + execute.checkCast(transform.upcast.type); + } + + if (java.lang.reflect.Modifier.isStatic(transform.method.reflect.getModifiers())) { + execute.invokeStatic(transform.method.owner.type, transform.method.method); + } else if (java.lang.reflect.Modifier.isInterface(transform.method.owner.clazz.getModifiers())) { + execute.invokeInterface(transform.method.owner.type, transform.method.method); + } else { + execute.invokeVirtual(transform.method.owner.type, transform.method.method); + } + + if (transform.downcast != null) { + execute.checkCast(transform.downcast.type); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java new file mode 100644 index 00000000000..33fea094058 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -0,0 +1,138 @@ +/* + * 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; + +import org.elasticsearch.script.ScoreAccessor; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; + +import java.lang.invoke.MethodType; +import java.util.Map; + +class WriterConstants { + final static String BASE_CLASS_NAME = Executable.class.getName(); + final static String CLASS_NAME = BASE_CLASS_NAME + "$CompiledPainlessExecutable"; + final static Type BASE_CLASS_TYPE = Type.getType(Executable.class); + final static Type CLASS_TYPE = Type.getType("L" + CLASS_NAME.replace(".", "/") + ";"); + + final static Method CONSTRUCTOR = getAsmMethod(void.class, "", Definition.class, String.class, String.class); + final static Method EXECUTE = getAsmMethod(Object.class, "execute", Map.class); + final static String SIGNATURE = "(Ljava/util/Map;)Ljava/lang/Object;"; + + final static Type PAINLESS_ERROR_TYPE = Type.getType(PainlessError.class); + + final static Type DEFINITION_TYPE = Type.getType(Definition.class); + + final static Type MAP_TYPE = Type.getType(Map.class); + final static Method MAP_GET = getAsmMethod(Object.class, "get", Object.class); + + final static Type SCORE_ACCESSOR_TYPE = Type.getType(ScoreAccessor.class); + final static Method SCORE_ACCESSOR_FLOAT = getAsmMethod(float.class, "floatValue"); + + final static Method DEF_METHOD_CALL = getAsmMethod( + Object.class, "methodCall", Object.class, String.class, Definition.class, Object[].class, boolean[].class); + final static Method DEF_ARRAY_STORE = getAsmMethod( + void.class, "arrayStore", Object.class, Object.class, Object.class, Definition.class, boolean.class, boolean.class); + final static Method DEF_ARRAY_LOAD = getAsmMethod( + Object.class, "arrayLoad", Object.class, Object.class, Definition.class, boolean.class); + final static Method DEF_FIELD_STORE = getAsmMethod( + void.class, "fieldStore", Object.class, Object.class, String.class, Definition.class, boolean.class); + final static Method DEF_FIELD_LOAD = getAsmMethod( + Object.class, "fieldLoad", Object.class, String.class, Definition.class); + + final static Method DEF_NOT_CALL = getAsmMethod(Object.class, "not", Object.class); + final static Method DEF_NEG_CALL = getAsmMethod(Object.class, "neg", Object.class); + final static Method DEF_MUL_CALL = getAsmMethod(Object.class, "mul", Object.class, Object.class); + final static Method DEF_DIV_CALL = getAsmMethod(Object.class, "div", Object.class, Object.class); + final static Method DEF_REM_CALL = getAsmMethod(Object.class, "rem", Object.class, Object.class); + final static Method DEF_ADD_CALL = getAsmMethod(Object.class, "add", Object.class, Object.class); + final static Method DEF_SUB_CALL = getAsmMethod(Object.class, "sub", Object.class, Object.class); + final static Method DEF_LSH_CALL = getAsmMethod(Object.class, "lsh", Object.class, Object.class); + final static Method DEF_RSH_CALL = getAsmMethod(Object.class, "rsh", Object.class, Object.class); + final static Method DEF_USH_CALL = getAsmMethod(Object.class, "ush", Object.class, Object.class); + final static Method DEF_AND_CALL = getAsmMethod(Object.class, "and", Object.class, Object.class); + final static Method DEF_XOR_CALL = getAsmMethod(Object.class, "xor", Object.class, Object.class); + final static Method DEF_OR_CALL = getAsmMethod(Object.class, "or" , Object.class, Object.class); + final static Method DEF_EQ_CALL = getAsmMethod(boolean.class, "eq" , Object.class, Object.class); + final static Method DEF_LT_CALL = getAsmMethod(boolean.class, "lt" , Object.class, Object.class); + final static Method DEF_LTE_CALL = getAsmMethod(boolean.class, "lte", Object.class, Object.class); + final static Method DEF_GT_CALL = getAsmMethod(boolean.class, "gt" , Object.class, Object.class); + final static Method DEF_GTE_CALL = getAsmMethod(boolean.class, "gte", Object.class, Object.class); + + final static Type STRINGBUILDER_TYPE = Type.getType(StringBuilder.class); + + final static Method STRINGBUILDER_CONSTRUCTOR = getAsmMethod(void.class, ""); + final static Method STRINGBUILDER_APPEND_BOOLEAN = getAsmMethod(StringBuilder.class, "append", boolean.class); + final static Method STRINGBUILDER_APPEND_CHAR = getAsmMethod(StringBuilder.class, "append", char.class); + final static Method STRINGBUILDER_APPEND_INT = getAsmMethod(StringBuilder.class, "append", int.class); + final static Method STRINGBUILDER_APPEND_LONG = getAsmMethod(StringBuilder.class, "append", long.class); + final static Method STRINGBUILDER_APPEND_FLOAT = getAsmMethod(StringBuilder.class, "append", float.class); + final static Method STRINGBUILDER_APPEND_DOUBLE = getAsmMethod(StringBuilder.class, "append", double.class); + final static Method STRINGBUILDER_APPEND_STRING = getAsmMethod(StringBuilder.class, "append", String.class); + final static Method STRINGBUILDER_APPEND_OBJECT = getAsmMethod(StringBuilder.class, "append", Object.class); + final static Method STRINGBUILDER_TOSTRING = getAsmMethod(String.class, "toString"); + + final static Method TOINTEXACT_LONG = getAsmMethod(int.class, "toIntExact", long.class); + final static Method NEGATEEXACT_INT = getAsmMethod(int.class, "negateExact", int.class); + final static Method NEGATEEXACT_LONG = getAsmMethod(long.class, "negateExact", long.class); + final static Method MULEXACT_INT = getAsmMethod(int.class, "multiplyExact", int.class, int.class); + final static Method MULEXACT_LONG = getAsmMethod(long.class, "multiplyExact", long.class, long.class); + final static Method ADDEXACT_INT = getAsmMethod(int.class, "addExact", int.class, int.class); + final static Method ADDEXACT_LONG = getAsmMethod(long.class, "addExact", long.class, long.class); + final static Method SUBEXACT_INT = getAsmMethod(int.class, "subtractExact", int.class, int.class); + final static Method SUBEXACT_LONG = getAsmMethod(long.class, "subtractExact", long.class, long.class); + + final static Method CHECKEQUALS = getAsmMethod(boolean.class, "checkEquals", Object.class, Object.class); + final static Method TOBYTEEXACT_INT = getAsmMethod(byte.class, "toByteExact", int.class); + final static Method TOBYTEEXACT_LONG = getAsmMethod(byte.class, "toByteExact", long.class); + final static Method TOBYTEWOOVERFLOW_FLOAT = getAsmMethod(byte.class, "toByteWithoutOverflow", float.class); + final static Method TOBYTEWOOVERFLOW_DOUBLE = getAsmMethod(byte.class, "toByteWithoutOverflow", double.class); + final static Method TOSHORTEXACT_INT = getAsmMethod(short.class, "toShortExact", int.class); + final static Method TOSHORTEXACT_LONG = getAsmMethod(short.class, "toShortExact", long.class); + final static Method TOSHORTWOOVERFLOW_FLOAT = getAsmMethod(short.class, "toShortWithoutOverflow", float.class); + final static Method TOSHORTWOOVERFLOW_DOUBLE = getAsmMethod(short.class, "toShortWihtoutOverflow", double.class); + final static Method TOCHAREXACT_INT = getAsmMethod(char.class, "toCharExact", int.class); + final static Method TOCHAREXACT_LONG = getAsmMethod(char.class, "toCharExact", long.class); + final static Method TOCHARWOOVERFLOW_FLOAT = getAsmMethod(char.class, "toCharWithoutOverflow", float.class); + final static Method TOCHARWOOVERFLOW_DOUBLE = getAsmMethod(char.class, "toCharWithoutOverflow", double.class); + final static Method TOINTWOOVERFLOW_FLOAT = getAsmMethod(int.class, "toIntWithoutOverflow", float.class); + final static Method TOINTWOOVERFLOW_DOUBLE = getAsmMethod(int.class, "toIntWithoutOverflow", double.class); + final static Method TOLONGWOOVERFLOW_FLOAT = getAsmMethod(long.class, "toLongWithoutOverflow", float.class); + final static Method TOLONGWOOVERFLOW_DOUBLE = getAsmMethod(long.class, "toLongWithoutOverflow", double.class); + final static Method TOFLOATWOOVERFLOW_DOUBLE = getAsmMethod(float.class , "toFloatWihtoutOverflow", double.class); + final static Method MULWOOVERLOW_FLOAT = getAsmMethod(float.class, "multiplyWithoutOverflow", float.class, float.class); + final static Method MULWOOVERLOW_DOUBLE = getAsmMethod(double.class, "multiplyWithoutOverflow", double.class, double.class); + final static Method DIVWOOVERLOW_INT = getAsmMethod(int.class, "divideWithoutOverflow", int.class, int.class); + final static Method DIVWOOVERLOW_LONG = getAsmMethod(long.class, "divideWithoutOverflow", long.class, long.class); + final static Method DIVWOOVERLOW_FLOAT = getAsmMethod(float.class, "divideWithoutOverflow", float.class, float.class); + final static Method DIVWOOVERLOW_DOUBLE = getAsmMethod(double.class, "divideWithoutOverflow", double.class, double.class); + final static Method REMWOOVERLOW_FLOAT = getAsmMethod(float.class, "remainderWithoutOverflow", float.class, float.class); + final static Method REMWOOVERLOW_DOUBLE = getAsmMethod(double.class, "remainderWithoutOverflow", double.class, double.class); + final static Method ADDWOOVERLOW_FLOAT = getAsmMethod(float.class, "addWithoutOverflow", float.class, float.class); + final static Method ADDWOOVERLOW_DOUBLE = getAsmMethod(double.class, "addWithoutOverflow", double.class, double.class); + final static Method SUBWOOVERLOW_FLOAT = getAsmMethod(float.class, "subtractWithoutOverflow", float.class, float.class); + final static Method SUBWOOVERLOW_DOUBLE = getAsmMethod(double.class, "subtractWithoutOverflow", double.class, double.class); + + private static Method getAsmMethod(final Class rtype, final String name, final Class... ptypes) { + return new Method(name, MethodType.methodType(rtype, ptypes).toMethodDescriptorString()); + } + + private WriterConstants() {} +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExpression.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExpression.java new file mode 100644 index 00000000000..c850031efa0 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExpression.java @@ -0,0 +1,684 @@ +/* + * 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; + +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.PainlessParser.AssignmentContext; +import org.elasticsearch.painless.PainlessParser.BinaryContext; +import org.elasticsearch.painless.PainlessParser.BoolContext; +import org.elasticsearch.painless.PainlessParser.CastContext; +import org.elasticsearch.painless.PainlessParser.CharContext; +import org.elasticsearch.painless.PainlessParser.CompContext; +import org.elasticsearch.painless.PainlessParser.ConditionalContext; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ExternalContext; +import org.elasticsearch.painless.PainlessParser.FalseContext; +import org.elasticsearch.painless.PainlessParser.IncrementContext; +import org.elasticsearch.painless.PainlessParser.NullContext; +import org.elasticsearch.painless.PainlessParser.NumericContext; +import org.elasticsearch.painless.PainlessParser.PostincContext; +import org.elasticsearch.painless.PainlessParser.PreincContext; +import org.elasticsearch.painless.PainlessParser.TrueContext; +import org.elasticsearch.painless.PainlessParser.UnaryContext; +import org.elasticsearch.painless.WriterUtility.Branch; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.GeneratorAdapter; + +import static org.elasticsearch.painless.PainlessParser.ADD; +import static org.elasticsearch.painless.PainlessParser.BWAND; +import static org.elasticsearch.painless.PainlessParser.BWOR; +import static org.elasticsearch.painless.PainlessParser.BWXOR; +import static org.elasticsearch.painless.PainlessParser.DIV; +import static org.elasticsearch.painless.PainlessParser.LSH; +import static org.elasticsearch.painless.PainlessParser.MUL; +import static org.elasticsearch.painless.PainlessParser.REM; +import static org.elasticsearch.painless.PainlessParser.RSH; +import static org.elasticsearch.painless.PainlessParser.SUB; +import static org.elasticsearch.painless.PainlessParser.USH; +import static org.elasticsearch.painless.WriterConstants.CHECKEQUALS; +import static org.elasticsearch.painless.WriterConstants.DEF_EQ_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_GTE_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_GT_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_LTE_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_LT_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_NEG_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_NOT_CALL; +import static org.elasticsearch.painless.WriterConstants.NEGATEEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.NEGATEEXACT_LONG; + +class WriterExpression { + private final Metadata metadata; + private final Definition definition; + private final CompilerSettings settings; + + private final GeneratorAdapter execute; + + private final Writer writer; + private final WriterUtility utility; + private final WriterCaster caster; + + WriterExpression(final Metadata metadata, final GeneratorAdapter execute, final Writer writer, + final WriterUtility utility, final WriterCaster caster) { + this.metadata = metadata; + definition = metadata.definition; + settings = metadata.settings; + + this.execute = execute; + + this.writer = writer; + this.utility = utility; + this.caster = caster; + } + + void processNumeric(final NumericContext ctx) { + final ExpressionMetadata numericemd = metadata.getExpressionMetadata(ctx); + final Object postConst = numericemd.postConst; + + if (postConst == null) { + utility.writeNumeric(ctx, numericemd.preConst); + caster.checkWriteCast(numericemd); + } else { + utility.writeConstant(ctx, postConst); + } + + utility.checkWriteBranch(ctx); + } + + void processChar(final CharContext ctx) { + final ExpressionMetadata charemd = metadata.getExpressionMetadata(ctx); + final Object postConst = charemd.postConst; + + if (postConst == null) { + utility.writeNumeric(ctx, (int)(char)charemd.preConst); + caster.checkWriteCast(charemd); + } else { + utility.writeConstant(ctx, postConst); + } + + utility.checkWriteBranch(ctx); + } + + void processTrue(final TrueContext ctx) { + final ExpressionMetadata trueemd = metadata.getExpressionMetadata(ctx); + final Object postConst = trueemd.postConst; + final Branch branch = utility.getBranch(ctx); + + if (branch == null) { + if (postConst == null) { + utility.writeBoolean(ctx, true); + caster.checkWriteCast(trueemd); + } else { + utility.writeConstant(ctx, postConst); + } + } else if (branch.tru != null) { + execute.goTo(branch.tru); + } + } + + void processFalse(final FalseContext ctx) { + final ExpressionMetadata falseemd = metadata.getExpressionMetadata(ctx); + final Object postConst = falseemd.postConst; + final Branch branch = utility.getBranch(ctx); + + if (branch == null) { + if (postConst == null) { + utility.writeBoolean(ctx, false); + caster.checkWriteCast(falseemd); + } else { + utility.writeConstant(ctx, postConst); + } + } else if (branch.fals != null) { + execute.goTo(branch.fals); + } + } + + void processNull(final NullContext ctx) { + final ExpressionMetadata nullemd = metadata.getExpressionMetadata(ctx); + + execute.visitInsn(Opcodes.ACONST_NULL); + caster.checkWriteCast(nullemd); + utility.checkWriteBranch(ctx); + } + + void processExternal(final ExternalContext ctx) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); + writer.visit(ctx.extstart()); + caster.checkWriteCast(expremd); + utility.checkWriteBranch(ctx); + } + + + void processPostinc(final PostincContext ctx) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); + writer.visit(ctx.extstart()); + caster.checkWriteCast(expremd); + utility.checkWriteBranch(ctx); + } + + void processPreinc(final PreincContext ctx) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); + writer.visit(ctx.extstart()); + caster.checkWriteCast(expremd); + utility.checkWriteBranch(ctx); + } + + void processUnary(final UnaryContext ctx) { + final ExpressionMetadata unaryemd = metadata.getExpressionMetadata(ctx); + final Object postConst = unaryemd.postConst; + final Object preConst = unaryemd.preConst; + final Branch branch = utility.getBranch(ctx); + + if (postConst != null) { + if (ctx.BOOLNOT() != null) { + if (branch == null) { + utility.writeConstant(ctx, postConst); + } else { + if ((boolean)postConst && branch.tru != null) { + execute.goTo(branch.tru); + } else if (!(boolean)postConst && branch.fals != null) { + execute.goTo(branch.fals); + } + } + } else { + utility.writeConstant(ctx, postConst); + utility.checkWriteBranch(ctx); + } + } else if (preConst != null) { + if (branch == null) { + utility.writeConstant(ctx, preConst); + caster.checkWriteCast(unaryemd); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } else { + final ExpressionContext exprctx = ctx.expression(); + + if (ctx.BOOLNOT() != null) { + final Branch local = utility.markBranch(ctx, exprctx); + + if (branch == null) { + local.fals = new Label(); + final Label aend = new Label(); + + writer.visit(exprctx); + + execute.push(false); + execute.goTo(aend); + execute.mark(local.fals); + execute.push(true); + execute.mark(aend); + + caster.checkWriteCast(unaryemd); + } else { + local.tru = branch.fals; + local.fals = branch.tru; + + writer.visit(exprctx); + } + } else { + final org.objectweb.asm.Type type = unaryemd.from.type; + final Sort sort = unaryemd.from.sort; + + writer.visit(exprctx); + + if (ctx.BWNOT() != null) { + if (sort == Sort.DEF) { + execute.invokeStatic(definition.defobjType.type, DEF_NOT_CALL); + } else { + if (sort == Sort.INT) { + utility.writeConstant(ctx, -1); + } else if (sort == Sort.LONG) { + utility.writeConstant(ctx, -1L); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + execute.math(GeneratorAdapter.XOR, type); + } + } else if (ctx.SUB() != null) { + if (sort == Sort.DEF) { + execute.invokeStatic(definition.defobjType.type, DEF_NEG_CALL); + } else { + if (settings.getNumericOverflow()) { + execute.math(GeneratorAdapter.NEG, type); + } else { + if (sort == Sort.INT) { + execute.invokeStatic(definition.mathType.type, NEGATEEXACT_INT); + } else if (sort == Sort.LONG) { + execute.invokeStatic(definition.mathType.type, NEGATEEXACT_LONG); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } + } + } else if (ctx.ADD() == null) { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + caster.checkWriteCast(unaryemd); + utility.checkWriteBranch(ctx); + } + } + } + + void processCast(final CastContext ctx) { + final ExpressionMetadata castemd = metadata.getExpressionMetadata(ctx); + final Object postConst = castemd.postConst; + + if (postConst == null) { + writer.visit(ctx.expression()); + caster.checkWriteCast(castemd); + } else { + utility.writeConstant(ctx, postConst); + } + + utility.checkWriteBranch(ctx); + } + + void processBinary(final BinaryContext ctx) { + final ExpressionMetadata binaryemd = metadata.getExpressionMetadata(ctx); + final Object postConst = binaryemd.postConst; + final Object preConst = binaryemd.preConst; + final Branch branch = utility.getBranch(ctx); + + if (postConst != null) { + utility.writeConstant(ctx, postConst); + } else if (preConst != null) { + if (branch == null) { + utility.writeConstant(ctx, preConst); + caster.checkWriteCast(binaryemd); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } else if (binaryemd.from.sort == Sort.STRING) { + final boolean marked = utility.containsStrings(ctx); + + if (!marked) { + utility.writeNewStrings(); + } + + final ExpressionContext exprctx0 = ctx.expression(0); + final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0); + utility.addStrings(exprctx0); + writer.visit(exprctx0); + + if (utility.containsStrings(exprctx0)) { + utility.writeAppendStrings(expremd0.from.sort); + utility.removeStrings(exprctx0); + } + + final ExpressionContext exprctx1 = ctx.expression(1); + final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1); + utility.addStrings(exprctx1); + writer.visit(exprctx1); + + if (utility.containsStrings(exprctx1)) { + utility.writeAppendStrings(expremd1.from.sort); + utility.removeStrings(exprctx1); + } + + if (marked) { + utility.removeStrings(ctx); + } else { + utility.writeToStrings(); + } + + caster.checkWriteCast(binaryemd); + } else { + final ExpressionContext exprctx0 = ctx.expression(0); + final ExpressionContext exprctx1 = ctx.expression(1); + + writer.visit(exprctx0); + writer.visit(exprctx1); + + final Type type = binaryemd.from; + + if (ctx.MUL() != null) utility.writeBinaryInstruction(ctx, type, MUL); + else if (ctx.DIV() != null) utility.writeBinaryInstruction(ctx, type, DIV); + else if (ctx.REM() != null) utility.writeBinaryInstruction(ctx, type, REM); + else if (ctx.ADD() != null) utility.writeBinaryInstruction(ctx, type, ADD); + else if (ctx.SUB() != null) utility.writeBinaryInstruction(ctx, type, SUB); + else if (ctx.LSH() != null) utility.writeBinaryInstruction(ctx, type, LSH); + else if (ctx.USH() != null) utility.writeBinaryInstruction(ctx, type, USH); + else if (ctx.RSH() != null) utility.writeBinaryInstruction(ctx, type, RSH); + else if (ctx.BWAND() != null) utility.writeBinaryInstruction(ctx, type, BWAND); + else if (ctx.BWXOR() != null) utility.writeBinaryInstruction(ctx, type, BWXOR); + else if (ctx.BWOR() != null) utility.writeBinaryInstruction(ctx, type, BWOR); + else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + caster.checkWriteCast(binaryemd); + } + + utility.checkWriteBranch(ctx); + } + + void processComp(final CompContext ctx) { + final ExpressionMetadata compemd = metadata.getExpressionMetadata(ctx); + final Object postConst = compemd.postConst; + final Object preConst = compemd.preConst; + final Branch branch = utility.getBranch(ctx); + + if (postConst != null) { + if (branch == null) { + utility.writeConstant(ctx, postConst); + } else { + if ((boolean)postConst && branch.tru != null) { + execute.mark(branch.tru); + } else if (!(boolean)postConst && branch.fals != null) { + execute.mark(branch.fals); + } + } + } else if (preConst != null) { + if (branch == null) { + utility.writeConstant(ctx, preConst); + caster.checkWriteCast(compemd); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } else { + final ExpressionContext exprctx0 = ctx.expression(0); + final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0); + + final ExpressionContext exprctx1 = ctx.expression(1); + final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1); + final org.objectweb.asm.Type type = expremd1.to.type; + final Sort sort1 = expremd1.to.sort; + + writer.visit(exprctx0); + + if (!expremd1.isNull) { + writer.visit(exprctx1); + } + + final boolean tru = branch != null && branch.tru != null; + final boolean fals = branch != null && branch.fals != null; + final Label jump = tru ? branch.tru : fals ? branch.fals : new Label(); + final Label end = new Label(); + + final boolean eq = (ctx.EQ() != null || ctx.EQR() != null) && (tru || !fals) || + (ctx.NE() != null || ctx.NER() != null) && fals; + final boolean ne = (ctx.NE() != null || ctx.NER() != null) && (tru || !fals) || + (ctx.EQ() != null || ctx.EQR() != null) && fals; + final boolean lt = ctx.LT() != null && (tru || !fals) || ctx.GTE() != null && fals; + final boolean lte = ctx.LTE() != null && (tru || !fals) || ctx.GT() != null && fals; + final boolean gt = ctx.GT() != null && (tru || !fals) || ctx.LTE() != null && fals; + final boolean gte = ctx.GTE() != null && (tru || !fals) || ctx.LT() != null && fals; + + boolean writejump = true; + + switch (sort1) { + case VOID: + case BYTE: + case SHORT: + case CHAR: + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + case BOOL: + if (eq) execute.ifZCmp(GeneratorAdapter.EQ, jump); + else if (ne) execute.ifZCmp(GeneratorAdapter.NE, jump); + else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + break; + case INT: + case LONG: + case FLOAT: + case DOUBLE: + if (eq) execute.ifCmp(type, GeneratorAdapter.EQ, jump); + else if (ne) execute.ifCmp(type, GeneratorAdapter.NE, jump); + else if (lt) execute.ifCmp(type, GeneratorAdapter.LT, jump); + else if (lte) execute.ifCmp(type, GeneratorAdapter.LE, jump); + else if (gt) execute.ifCmp(type, GeneratorAdapter.GT, jump); + else if (gte) execute.ifCmp(type, GeneratorAdapter.GE, jump); + else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + break; + case DEF: + if (eq) { + if (expremd1.isNull) { + execute.ifNull(jump); + } else if (!expremd0.isNull && ctx.EQ() != null) { + execute.invokeStatic(definition.defobjType.type, DEF_EQ_CALL); + } else { + execute.ifCmp(type, GeneratorAdapter.EQ, jump); + } + } else if (ne) { + if (expremd1.isNull) { + execute.ifNonNull(jump); + } else if (!expremd0.isNull && ctx.NE() != null) { + execute.invokeStatic(definition.defobjType.type, DEF_EQ_CALL); + execute.ifZCmp(GeneratorAdapter.EQ, jump); + } else { + execute.ifCmp(type, GeneratorAdapter.NE, jump); + } + } else if (lt) { + execute.invokeStatic(definition.defobjType.type, DEF_LT_CALL); + } else if (lte) { + execute.invokeStatic(definition.defobjType.type, DEF_LTE_CALL); + } else if (gt) { + execute.invokeStatic(definition.defobjType.type, DEF_GT_CALL); + } else if (gte) { + execute.invokeStatic(definition.defobjType.type, DEF_GTE_CALL); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + writejump = expremd1.isNull || ne || ctx.EQR() != null; + + if (branch != null && !writejump) { + execute.ifZCmp(GeneratorAdapter.NE, jump); + } + + break; + default: + if (eq) { + if (expremd1.isNull) { + execute.ifNull(jump); + } else if (ctx.EQ() != null) { + execute.invokeStatic(definition.utilityType.type, CHECKEQUALS); + + if (branch != null) { + execute.ifZCmp(GeneratorAdapter.NE, jump); + } + + writejump = false; + } else { + execute.ifCmp(type, GeneratorAdapter.EQ, jump); + } + } else if (ne) { + if (expremd1.isNull) { + execute.ifNonNull(jump); + } else if (ctx.NE() != null) { + execute.invokeStatic(definition.utilityType.type, CHECKEQUALS); + execute.ifZCmp(GeneratorAdapter.EQ, jump); + } else { + execute.ifCmp(type, GeneratorAdapter.NE, jump); + } + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } + + if (branch == null) { + if (writejump) { + execute.push(false); + execute.goTo(end); + execute.mark(jump); + execute.push(true); + execute.mark(end); + } + + caster.checkWriteCast(compemd); + } + } + } + + void processBool(final BoolContext ctx) { + final ExpressionMetadata boolemd = metadata.getExpressionMetadata(ctx); + final Object postConst = boolemd.postConst; + final Object preConst = boolemd.preConst; + final Branch branch = utility.getBranch(ctx); + + if (postConst != null) { + if (branch == null) { + utility.writeConstant(ctx, postConst); + } else { + if ((boolean)postConst && branch.tru != null) { + execute.mark(branch.tru); + } else if (!(boolean)postConst && branch.fals != null) { + execute.mark(branch.fals); + } + } + } else if (preConst != null) { + if (branch == null) { + utility.writeConstant(ctx, preConst); + caster.checkWriteCast(boolemd); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } else { + final ExpressionContext exprctx0 = ctx.expression(0); + final ExpressionContext exprctx1 = ctx.expression(1); + + if (branch == null) { + if (ctx.BOOLAND() != null) { + final Branch local = utility.markBranch(ctx, exprctx0, exprctx1); + local.fals = new Label(); + final Label end = new Label(); + + writer.visit(exprctx0); + writer.visit(exprctx1); + + execute.push(true); + execute.goTo(end); + execute.mark(local.fals); + execute.push(false); + execute.mark(end); + } else if (ctx.BOOLOR() != null) { + final Branch branch0 = utility.markBranch(ctx, exprctx0); + branch0.tru = new Label(); + final Branch branch1 = utility.markBranch(ctx, exprctx1); + branch1.fals = new Label(); + final Label aend = new Label(); + + writer.visit(exprctx0); + writer.visit(exprctx1); + + execute.mark(branch0.tru); + execute.push(true); + execute.goTo(aend); + execute.mark(branch1.fals); + execute.push(false); + execute.mark(aend); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + caster.checkWriteCast(boolemd); + } else { + if (ctx.BOOLAND() != null) { + final Branch branch0 = utility.markBranch(ctx, exprctx0); + branch0.fals = branch.fals == null ? new Label() : branch.fals; + final Branch branch1 = utility.markBranch(ctx, exprctx1); + branch1.tru = branch.tru; + branch1.fals = branch.fals; + + writer.visit(exprctx0); + writer.visit(exprctx1); + + if (branch.fals == null) { + execute.mark(branch0.fals); + } + } else if (ctx.BOOLOR() != null) { + final Branch branch0 = utility.markBranch(ctx, exprctx0); + branch0.tru = branch.tru == null ? new Label() : branch.tru; + final Branch branch1 = utility.markBranch(ctx, exprctx1); + branch1.tru = branch.tru; + branch1.fals = branch.fals; + + writer.visit(exprctx0); + writer.visit(exprctx1); + + if (branch.tru == null) { + execute.mark(branch0.tru); + } + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } + } + } + + void processConditional(final ConditionalContext ctx) { + final ExpressionMetadata condemd = metadata.getExpressionMetadata(ctx); + final Branch branch = utility.getBranch(ctx); + + final ExpressionContext expr0 = ctx.expression(0); + final ExpressionContext expr1 = ctx.expression(1); + final ExpressionContext expr2 = ctx.expression(2); + + final Branch local = utility.markBranch(ctx, expr0); + local.fals = new Label(); + local.end = new Label(); + + if (branch != null) { + utility.copyBranch(branch, expr1, expr2); + } + + writer.visit(expr0); + writer.visit(expr1); + execute.goTo(local.end); + execute.mark(local.fals); + writer.visit(expr2); + execute.mark(local.end); + + if (branch == null) { + caster.checkWriteCast(condemd); + } + } + + void processAssignment(final AssignmentContext ctx) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx); + writer.visit(ctx.extstart()); + caster.checkWriteCast(expremd); + utility.checkWriteBranch(ctx); + } + + void processIncrement(final IncrementContext ctx) { + final ExpressionMetadata incremd = metadata.getExpressionMetadata(ctx); + final Object postConst = incremd.postConst; + + if (postConst == null) { + utility.writeNumeric(ctx, incremd.preConst); + caster.checkWriteCast(incremd); + } else { + utility.writeConstant(ctx, postConst); + } + + utility.checkWriteBranch(ctx); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExternal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExternal.java new file mode 100644 index 00000000000..8ab729f98fa --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterExternal.java @@ -0,0 +1,769 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.elasticsearch.painless.Definition.Constructor; +import org.elasticsearch.painless.Definition.Field; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.Metadata.ExtNodeMetadata; +import org.elasticsearch.painless.Metadata.ExternalMetadata; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ExtbraceContext; +import org.elasticsearch.painless.PainlessParser.ExtcallContext; +import org.elasticsearch.painless.PainlessParser.ExtcastContext; +import org.elasticsearch.painless.PainlessParser.ExtdotContext; +import org.elasticsearch.painless.PainlessParser.ExtfieldContext; +import org.elasticsearch.painless.PainlessParser.ExtnewContext; +import org.elasticsearch.painless.PainlessParser.ExtprecContext; +import org.elasticsearch.painless.PainlessParser.ExtstartContext; +import org.elasticsearch.painless.PainlessParser.ExtstringContext; +import org.elasticsearch.painless.PainlessParser.ExttypeContext; +import org.elasticsearch.painless.PainlessParser.ExtvarContext; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.GeneratorAdapter; + +import java.util.List; + +import static org.elasticsearch.painless.PainlessParser.ADD; +import static org.elasticsearch.painless.PainlessParser.DIV; +import static org.elasticsearch.painless.PainlessParser.MUL; +import static org.elasticsearch.painless.PainlessParser.REM; +import static org.elasticsearch.painless.PainlessParser.SUB; +import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; +import static org.elasticsearch.painless.WriterConstants.DEFINITION_TYPE; +import static org.elasticsearch.painless.WriterConstants.DEF_ARRAY_LOAD; +import static org.elasticsearch.painless.WriterConstants.DEF_ARRAY_STORE; +import static org.elasticsearch.painless.WriterConstants.DEF_FIELD_LOAD; +import static org.elasticsearch.painless.WriterConstants.DEF_FIELD_STORE; +import static org.elasticsearch.painless.WriterConstants.DEF_METHOD_CALL; +import static org.elasticsearch.painless.WriterConstants.TOBYTEEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.TOBYTEEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.TOBYTEWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOBYTEWOOVERFLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.TOCHAREXACT_INT; +import static org.elasticsearch.painless.WriterConstants.TOCHAREXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.TOCHARWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOCHARWOOVERFLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.TOFLOATWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOINTEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.TOINTWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOINTWOOVERFLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.TOLONGWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOLONGWOOVERFLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.TOSHORTEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.TOSHORTEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.TOSHORTWOOVERFLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.TOSHORTWOOVERFLOW_FLOAT; + +class WriterExternal { + private final Metadata metadata; + private final Definition definition; + private final CompilerSettings settings; + + private final GeneratorAdapter execute; + + private final Writer writer; + private final WriterUtility utility; + private final WriterCaster caster; + + WriterExternal(final Metadata metadata, final GeneratorAdapter execute, final Writer writer, + final WriterUtility utility, final WriterCaster caster) { + this.metadata = metadata; + definition = metadata.definition; + settings = metadata.settings; + + this.execute = execute; + + this.writer = writer; + this.utility = utility; + this.caster = caster; + } + + void processExtstart(final ExtstartContext ctx) { + final ExternalMetadata startemd = metadata.getExternalMetadata(ctx); + + if (startemd.token == ADD) { + final ExpressionMetadata storeemd = metadata.getExpressionMetadata(startemd.storeExpr); + + if (startemd.current.sort == Sort.STRING || storeemd.from.sort == Sort.STRING) { + utility.writeNewStrings(); + utility.addStrings(startemd.storeExpr); + } + } + + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + if (precctx != null) { + writer.visit(precctx); + } else if (castctx != null) { + writer.visit(castctx); + } else if (typectx != null) { + writer.visit(typectx); + } else if (varctx != null) { + writer.visit(varctx); + } else if (newctx != null) { + writer.visit(newctx); + } else if (stringctx != null) { + writer.visit(stringctx); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } + + void processExtprec(final ExtprecContext ctx) { + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + if (precctx != null) { + writer.visit(precctx); + } else if (castctx != null) { + writer.visit(castctx); + } else if (typectx != null) { + writer.visit(typectx); + } else if (varctx != null) { + writer.visit(varctx); + } else if (newctx != null) { + writer.visit(newctx); + } else if (stringctx != null) { + writer.visit(stringctx); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtcast(final ExtcastContext ctx) { + ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx); + + final ExtprecContext precctx = ctx.extprec(); + final ExtcastContext castctx = ctx.extcast(); + final ExttypeContext typectx = ctx.exttype(); + final ExtvarContext varctx = ctx.extvar(); + final ExtnewContext newctx = ctx.extnew(); + final ExtstringContext stringctx = ctx.extstring(); + + if (precctx != null) { + writer.visit(precctx); + } else if (castctx != null) { + writer.visit(castctx); + } else if (typectx != null) { + writer.visit(typectx); + } else if (varctx != null) { + writer.visit(varctx); + } else if (newctx != null) { + writer.visit(newctx); + } else if (stringctx != null) { + writer.visit(stringctx); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + caster.checkWriteCast(ctx, castenmd.castTo); + } + + void processExtbrace(final ExtbraceContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + + writer.visit(exprctx); + writeLoadStoreExternal(ctx); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtdot(final ExtdotContext ctx) { + final ExtcallContext callctx = ctx.extcall(); + final ExtfieldContext fieldctx = ctx.extfield(); + + if (callctx != null) { + writer.visit(callctx); + } else if (fieldctx != null) { + writer.visit(fieldctx); + } + } + + void processExttype(final ExttypeContext ctx) { + writer.visit(ctx.extdot()); + } + + void processExtcall(final ExtcallContext ctx) { + writeCallExternal(ctx); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtvar(final ExtvarContext ctx) { + writeLoadStoreExternal(ctx); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtfield(final ExtfieldContext ctx) { + writeLoadStoreExternal(ctx); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtnew(final ExtnewContext ctx) { + writeNewExternal(ctx); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + void processExtstring(final ExtstringContext ctx) { + final ExtNodeMetadata stringenmd = metadata.getExtNodeMetadata(ctx); + + utility.writeConstant(ctx, stringenmd.target); + + final ExtdotContext dotctx = ctx.extdot(); + final ExtbraceContext bracectx = ctx.extbrace(); + + if (dotctx != null) { + writer.visit(dotctx); + } else if (bracectx != null) { + writer.visit(bracectx); + } + } + + private void writeLoadStoreExternal(final ParserRuleContext source) { + final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); + final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent); + + final boolean length = "#length".equals(sourceenmd.target); + final boolean array = "#brace".equals(sourceenmd.target); + final boolean name = sourceenmd.target instanceof String && !length && !array; + final boolean variable = sourceenmd.target instanceof Integer; + final boolean field = sourceenmd.target instanceof Field; + final boolean shortcut = sourceenmd.target instanceof Object[]; + + if (!length && !variable && !field && !array && !name && !shortcut) { + throw new IllegalStateException(WriterUtility.error(source) + "Target not found for load/store."); + } + + final boolean maplist = shortcut && (boolean)((Object[])sourceenmd.target)[2]; + final Object constant = shortcut ? ((Object[])sourceenmd.target)[3] : null; + + final boolean x1 = field || name || (shortcut && !maplist); + final boolean x2 = array || (shortcut && maplist); + + if (length) { + execute.arrayLength(); + } else if (sourceenmd.last && parentemd.storeExpr != null) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr); + final boolean cat = utility.containsStrings(parentemd.storeExpr); + + if (cat) { + if (field || name || shortcut) { + execute.dupX1(); + } else if (array) { + execute.dup2X1(); + } + + if (maplist) { + if (constant != null) { + utility.writeConstant(source, constant); + } + + execute.dupX2(); + } + + writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); + utility.writeAppendStrings(sourceenmd.type.sort); + writer.visit(parentemd.storeExpr); + + if (utility.containsStrings(parentemd.storeExpr)) { + utility.writeAppendStrings(expremd.to.sort); + utility.removeStrings(parentemd.storeExpr); + } + + utility.writeToStrings(); + caster.checkWriteCast(source, sourceenmd.castTo); + + if (parentemd.read) { + utility.writeDup(sourceenmd.type.sort.size, x1, x2); + } + + writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); + } else if (parentemd.token > 0) { + final int token = parentemd.token; + + if (field || name || shortcut) { + execute.dup(); + } else if (array) { + execute.dup2(); + } + + if (maplist) { + if (constant != null) { + utility.writeConstant(source, constant); + } + + execute.dupX1(); + } + + writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); + + if (parentemd.read && parentemd.post) { + utility.writeDup(sourceenmd.type.sort.size, x1, x2); + } + + caster.checkWriteCast(source, sourceenmd.castFrom); + writer.visit(parentemd.storeExpr); + + utility.writeBinaryInstruction(source, sourceenmd.promote, token); + + boolean exact = false; + + if (!settings.getNumericOverflow() && expremd.typesafe && sourceenmd.type.sort != Sort.DEF && + (token == MUL || token == DIV || token == REM || token == ADD || token == SUB)) { + exact = writeExactInstruction(sourceenmd.type.sort, sourceenmd.promote.sort); + } + + if (!exact) { + caster.checkWriteCast(source, sourceenmd.castTo); + } + + if (parentemd.read && !parentemd.post) { + utility.writeDup(sourceenmd.type.sort.size, x1, x2); + } + + writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); + } else { + if (constant != null) { + utility.writeConstant(source, constant); + } + + writer.visit(parentemd.storeExpr); + + if (parentemd.read) { + utility.writeDup(sourceenmd.type.sort.size, x1, x2); + } + + writeLoadStoreInstruction(source, true, variable, field, name, array, shortcut); + } + } else { + if (constant != null) { + utility.writeConstant(source, constant); + } + + writeLoadStoreInstruction(source, false, variable, field, name, array, shortcut); + } + } + + private void writeLoadStoreInstruction(final ParserRuleContext source, + final boolean store, final boolean variable, + final boolean field, final boolean name, + final boolean array, final boolean shortcut) { + final ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source); + + if (variable) { + writeLoadStoreVariable(source, store, sourceemd.type, (int)sourceemd.target); + } else if (field) { + writeLoadStoreField(store, (Field)sourceemd.target); + } else if (name) { + writeLoadStoreField(source, store, (String)sourceemd.target); + } else if (array) { + writeLoadStoreArray(source, store, sourceemd.type); + } else if (shortcut) { + Object[] targets = (Object[])sourceemd.target; + writeLoadStoreShortcut(store, (Method)targets[0], (Method)targets[1]); + } else { + throw new IllegalStateException(WriterUtility.error(source) + "Load/Store requires a variable, field, or array."); + } + } + + private void writeLoadStoreVariable(final ParserRuleContext source, final boolean store, + final Type type, final int slot) { + if (type.sort == Sort.VOID) { + throw new IllegalStateException(WriterUtility.error(source) + "Cannot load/store void type."); + } + + if (store) { + execute.visitVarInsn(type.type.getOpcode(Opcodes.ISTORE), slot); + } else { + execute.visitVarInsn(type.type.getOpcode(Opcodes.ILOAD), slot); + } + } + + private void writeLoadStoreField(final boolean store, final Field field) { + if (java.lang.reflect.Modifier.isStatic(field.reflect.getModifiers())) { + if (store) { + execute.putStatic(field.owner.type, field.reflect.getName(), field.type.type); + } else { + execute.getStatic(field.owner.type, field.reflect.getName(), field.type.type); + + if (!field.generic.clazz.equals(field.type.clazz)) { + execute.checkCast(field.generic.type); + } + } + } else { + if (store) { + execute.putField(field.owner.type, field.reflect.getName(), field.type.type); + } else { + execute.getField(field.owner.type, field.reflect.getName(), field.type.type); + + if (!field.generic.clazz.equals(field.type.clazz)) { + execute.checkCast(field.generic.type); + } + } + } + } + + private void writeLoadStoreField(final ParserRuleContext source, final boolean store, final String name) { + if (store) { + final ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source); + final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceemd.parent); + final ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr); + + execute.push(name); + execute.loadThis(); + execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); + execute.push(parentemd.token == 0 && expremd.typesafe); + execute.invokeStatic(definition.defobjType.type, DEF_FIELD_STORE); + } else { + execute.push(name); + execute.loadThis(); + execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); + execute.invokeStatic(definition.defobjType.type, DEF_FIELD_LOAD); + } + } + + private void writeLoadStoreArray(final ParserRuleContext source, final boolean store, final Type type) { + if (type.sort == Sort.VOID) { + throw new IllegalStateException(WriterUtility.error(source) + "Cannot load/store void type."); + } + + if (type.sort == Sort.DEF) { + final ExtbraceContext bracectx = (ExtbraceContext)source; + final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(bracectx.expression()); + + if (store) { + final ExtNodeMetadata braceenmd = metadata.getExtNodeMetadata(bracectx); + final ExternalMetadata parentemd = metadata.getExternalMetadata(braceenmd.parent); + final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(parentemd.storeExpr); + + execute.loadThis(); + execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); + execute.push(expremd0.typesafe); + execute.push(parentemd.token == 0 && expremd1.typesafe); + execute.invokeStatic(definition.defobjType.type, DEF_ARRAY_STORE); + } else { + execute.loadThis(); + execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); + execute.push(expremd0.typesafe); + execute.invokeStatic(definition.defobjType.type, DEF_ARRAY_LOAD); + } + } else { + if (store) { + execute.arrayStore(type.type); + } else { + execute.arrayLoad(type.type); + } + } + } + + private void writeLoadStoreShortcut(final boolean store, final Method getter, final Method setter) { + final Method method = store ? setter : getter; + + if (java.lang.reflect.Modifier.isInterface(getter.owner.clazz.getModifiers())) { + execute.invokeInterface(method.owner.type, method.method); + } else { + execute.invokeVirtual(method.owner.type, method.method); + } + + if (store) { + utility.writePop(method.rtn.type.getSize()); + } else if (!method.rtn.clazz.equals(method.handle.type().returnType())) { + execute.checkCast(method.rtn.type); + } + } + + /** + * Called for any compound assignment (including increment/decrement instructions). + * We have to be stricter than writeBinary, and do overflow checks against the original type's size + * instead of the promoted type's size, since the result will be implicitly cast back. + * + * @return This will be true if an instruction is written, false otherwise. + */ + private boolean writeExactInstruction(final Sort osort, final Sort psort) { + if (psort == Sort.DOUBLE) { + if (osort == Sort.FLOAT) { + execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE); + } else if (osort == Sort.FLOAT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOFLOATWOOVERFLOW_DOUBLE); + execute.checkCast(definition.floatobjType.type); + } else if (osort == Sort.LONG) { + execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE); + } else if (osort == Sort.LONG_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_DOUBLE); + execute.checkCast(definition.longobjType.type); + } else if (osort == Sort.INT) { + execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE); + } else if (osort == Sort.INT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_DOUBLE); + execute.checkCast(definition.intobjType.type); + } else if (osort == Sort.CHAR) { + execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE); + } else if (osort == Sort.CHAR_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_DOUBLE); + execute.checkCast(definition.charobjType.type); + } else if (osort == Sort.SHORT) { + execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE); + } else if (osort == Sort.SHORT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_DOUBLE); + execute.checkCast(definition.shortobjType.type); + } else if (osort == Sort.BYTE) { + execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE); + } else if (osort == Sort.BYTE_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_DOUBLE); + execute.checkCast(definition.byteobjType.type); + } else { + return false; + } + } else if (psort == Sort.FLOAT) { + if (osort == Sort.LONG) { + execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT); + } else if (osort == Sort.LONG_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOLONGWOOVERFLOW_FLOAT); + execute.checkCast(definition.longobjType.type); + } else if (osort == Sort.INT) { + execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT); + } else if (osort == Sort.INT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOINTWOOVERFLOW_FLOAT); + execute.checkCast(definition.intobjType.type); + } else if (osort == Sort.CHAR) { + execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT); + } else if (osort == Sort.CHAR_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOCHARWOOVERFLOW_FLOAT); + execute.checkCast(definition.charobjType.type); + } else if (osort == Sort.SHORT) { + execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT); + } else if (osort == Sort.SHORT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOSHORTWOOVERFLOW_FLOAT); + execute.checkCast(definition.shortobjType.type); + } else if (osort == Sort.BYTE) { + execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT); + } else if (osort == Sort.BYTE_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOBYTEWOOVERFLOW_FLOAT); + execute.checkCast(definition.byteobjType.type); + } else { + return false; + } + } else if (psort == Sort.LONG) { + if (osort == Sort.INT) { + execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG); + } else if (osort == Sort.INT_OBJ) { + execute.invokeStatic(definition.mathType.type, TOINTEXACT_LONG); + execute.checkCast(definition.intobjType.type); + } else if (osort == Sort.CHAR) { + execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG); + } else if (osort == Sort.CHAR_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_LONG); + execute.checkCast(definition.charobjType.type); + } else if (osort == Sort.SHORT) { + execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG); + } else if (osort == Sort.SHORT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_LONG); + execute.checkCast(definition.shortobjType.type); + } else if (osort == Sort.BYTE) { + execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG); + } else if (osort == Sort.BYTE_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_LONG); + execute.checkCast(definition.byteobjType.type); + } else { + return false; + } + } else if (psort == Sort.INT) { + if (osort == Sort.CHAR) { + execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT); + } else if (osort == Sort.CHAR_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOCHAREXACT_INT); + execute.checkCast(definition.charobjType.type); + } else if (osort == Sort.SHORT) { + execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT); + } else if (osort == Sort.SHORT_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOSHORTEXACT_INT); + execute.checkCast(definition.shortobjType.type); + } else if (osort == Sort.BYTE) { + execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT); + } else if (osort == Sort.BYTE_OBJ) { + execute.invokeStatic(definition.utilityType.type, TOBYTEEXACT_INT); + execute.checkCast(definition.byteobjType.type); + } else { + return false; + } + } else { + return false; + } + + return true; + } + + private void writeNewExternal(final ExtnewContext source) { + final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); + final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent); + + final boolean makearray = "#makearray".equals(sourceenmd.target); + final boolean constructor = sourceenmd.target instanceof Constructor; + + if (!makearray && !constructor) { + throw new IllegalStateException(WriterUtility.error(source) + "Target not found for new call."); + } + + if (makearray) { + for (final ExpressionContext exprctx : source.expression()) { + writer.visit(exprctx); + } + + if (sourceenmd.type.sort == Sort.ARRAY) { + execute.visitMultiANewArrayInsn(sourceenmd.type.type.getDescriptor(), sourceenmd.type.type.getDimensions()); + } else { + execute.newArray(sourceenmd.type.type); + } + } else { + execute.newInstance(sourceenmd.type.type); + + if (parentemd.read) { + execute.dup(); + } + + for (final ExpressionContext exprctx : source.arguments().expression()) { + writer.visit(exprctx); + } + + final Constructor target = (Constructor)sourceenmd.target; + execute.invokeConstructor(target.owner.type, target.method); + } + } + + private void writeCallExternal(final ExtcallContext source) { + final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source); + + final boolean method = sourceenmd.target instanceof Method; + final boolean def = sourceenmd.target instanceof String; + + if (!method && !def) { + throw new IllegalStateException(WriterUtility.error(source) + "Target not found for call."); + } + + final List arguments = source.arguments().expression(); + + if (method) { + for (final ExpressionContext exprctx : arguments) { + writer.visit(exprctx); + } + + final Method target = (Method)sourceenmd.target; + + if (java.lang.reflect.Modifier.isStatic(target.reflect.getModifiers())) { + execute.invokeStatic(target.owner.type, target.method); + } else if (java.lang.reflect.Modifier.isInterface(target.owner.clazz.getModifiers())) { + execute.invokeInterface(target.owner.type, target.method); + } else { + execute.invokeVirtual(target.owner.type, target.method); + } + + if (!target.rtn.clazz.equals(target.handle.type().returnType())) { + execute.checkCast(target.rtn.type); + } + } else { + execute.push((String)sourceenmd.target); + execute.loadThis(); + execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); + + execute.push(arguments.size()); + execute.newArray(definition.defType.type); + + for (int argument = 0; argument < arguments.size(); ++argument) { + execute.dup(); + execute.push(argument); + writer.visit(arguments.get(argument)); + execute.arrayStore(definition.defType.type); + } + + execute.push(arguments.size()); + execute.newArray(definition.booleanType.type); + + for (int argument = 0; argument < arguments.size(); ++argument) { + execute.dup(); + execute.push(argument); + execute.push(metadata.getExpressionMetadata(arguments.get(argument)).typesafe); + execute.arrayStore(definition.booleanType.type); + } + + execute.invokeStatic(definition.defobjType.type, DEF_METHOD_CALL); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterStatement.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterStatement.java new file mode 100644 index 00000000000..a0e70f319b5 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterStatement.java @@ -0,0 +1,391 @@ +/* + * 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; + +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Metadata.ExpressionMetadata; +import org.elasticsearch.painless.Metadata.StatementMetadata; +import org.elasticsearch.painless.PainlessParser.AfterthoughtContext; +import org.elasticsearch.painless.PainlessParser.BlockContext; +import org.elasticsearch.painless.PainlessParser.DeclContext; +import org.elasticsearch.painless.PainlessParser.DeclarationContext; +import org.elasticsearch.painless.PainlessParser.DecltypeContext; +import org.elasticsearch.painless.PainlessParser.DeclvarContext; +import org.elasticsearch.painless.PainlessParser.DoContext; +import org.elasticsearch.painless.PainlessParser.EmptyscopeContext; +import org.elasticsearch.painless.PainlessParser.ExprContext; +import org.elasticsearch.painless.PainlessParser.ExpressionContext; +import org.elasticsearch.painless.PainlessParser.ForContext; +import org.elasticsearch.painless.PainlessParser.IfContext; +import org.elasticsearch.painless.PainlessParser.InitializerContext; +import org.elasticsearch.painless.PainlessParser.MultipleContext; +import org.elasticsearch.painless.PainlessParser.ReturnContext; +import org.elasticsearch.painless.PainlessParser.SingleContext; +import org.elasticsearch.painless.PainlessParser.SourceContext; +import org.elasticsearch.painless.PainlessParser.StatementContext; +import org.elasticsearch.painless.PainlessParser.ThrowContext; +import org.elasticsearch.painless.PainlessParser.TrapContext; +import org.elasticsearch.painless.PainlessParser.TryContext; +import org.elasticsearch.painless.PainlessParser.WhileContext; +import org.elasticsearch.painless.WriterUtility.Branch; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.GeneratorAdapter; + +import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; + +class WriterStatement { + private final Metadata metadata; + + private final GeneratorAdapter execute; + + private final Writer writer; + private final WriterUtility utility; + + WriterStatement(final Metadata metadata, final GeneratorAdapter execute, + final Writer writer, final WriterUtility utility) { + this.metadata = metadata; + + this.execute = execute; + + this.writer = writer; + this.utility = utility; + } + + void processSource(final SourceContext ctx) { + final StatementMetadata sourcesmd = metadata.getStatementMetadata(ctx); + + for (final StatementContext sctx : ctx.statement()) { + writer.visit(sctx); + } + + if (!sourcesmd.methodEscape) { + execute.visitInsn(Opcodes.ACONST_NULL); + execute.returnValue(); + } + } + + void processIf(final IfContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + final boolean els = ctx.ELSE() != null; + final Branch branch = utility.markBranch(ctx, exprctx); + branch.end = new Label(); + branch.fals = els ? new Label() : branch.end; + + writer.visit(exprctx); + + final BlockContext blockctx0 = ctx.block(0); + final StatementMetadata blockmd0 = metadata.getStatementMetadata(blockctx0); + writer.visit(blockctx0); + + if (els) { + if (!blockmd0.allLast) { + execute.goTo(branch.end); + } + + execute.mark(branch.fals); + writer.visit(ctx.block(1)); + } + + execute.mark(branch.end); + } + + void processWhile(final WhileContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + final Branch branch = utility.markBranch(ctx, exprctx); + branch.begin = new Label(); + branch.end = new Label(); + branch.fals = branch.end; + + utility.pushJump(branch); + execute.mark(branch.begin); + writer.visit(exprctx); + + final BlockContext blockctx = ctx.block(); + boolean allLast = false; + + if (blockctx != null) { + final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); + allLast = blocksmd.allLast; + writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1); + writer.visit(blockctx); + } else if (ctx.empty() != null) { + writeLoopCounter(1); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + if (!allLast) { + execute.goTo(branch.begin); + } + + execute.mark(branch.end); + utility.popJump(); + } + + void processDo(final DoContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + final Branch branch = utility.markBranch(ctx, exprctx); + Label start = new Label(); + branch.begin = new Label(); + branch.end = new Label(); + branch.fals = branch.end; + + final BlockContext blockctx = ctx.block(); + final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); + + utility.pushJump(branch); + execute.mark(start); + writer.visit(blockctx); + execute.mark(branch.begin); + writer.visit(exprctx); + writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1); + execute.goTo(start); + execute.mark(branch.end); + utility.popJump(); + } + + void processFor(final ForContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + final AfterthoughtContext atctx = ctx.afterthought(); + final Branch branch = utility.markBranch(ctx, exprctx); + final Label start = new Label(); + branch.begin = atctx == null ? start : new Label(); + branch.end = new Label(); + branch.fals = branch.end; + + utility.pushJump(branch); + + if (ctx.initializer() != null) { + writer.visit(ctx.initializer()); + } + + execute.mark(start); + + if (exprctx != null) { + writer.visit(exprctx); + } + + final BlockContext blockctx = ctx.block(); + boolean allLast = false; + + if (blockctx != null) { + StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); + allLast = blocksmd.allLast; + + int count = blocksmd.count > 0 ? blocksmd.count : 1; + + if (atctx != null) { + ++count; + } + + writeLoopCounter(count); + writer.visit(blockctx); + } else if (ctx.empty() != null) { + writeLoopCounter(1); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + if (atctx != null) { + execute.mark(branch.begin); + writer.visit(atctx); + } + + if (atctx != null || !allLast) { + execute.goTo(start); + } + + execute.mark(branch.end); + utility.popJump(); + } + + void processDecl(final DeclContext ctx) { + writer.visit(ctx.declaration()); + } + + void processContinue() { + final Branch jump = utility.peekJump(); + execute.goTo(jump.begin); + } + + void processBreak() { + final Branch jump = utility.peekJump(); + execute.goTo(jump.end); + } + + void processReturn(final ReturnContext ctx) { + writer.visit(ctx.expression()); + execute.returnValue(); + } + + void processTry(final TryContext ctx) { + final TrapContext[] trapctxs = new TrapContext[ctx.trap().size()]; + ctx.trap().toArray(trapctxs); + final Branch branch = utility.markBranch(ctx, trapctxs); + + Label end = new Label(); + branch.begin = new Label(); + branch.end = new Label(); + branch.tru = trapctxs.length > 1 ? end : null; + + execute.mark(branch.begin); + + final BlockContext blockctx = ctx.block(); + final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx); + writer.visit(blockctx); + + if (!blocksmd.allLast) { + execute.goTo(end); + } + + execute.mark(branch.end); + + for (final TrapContext trapctx : trapctxs) { + writer.visit(trapctx); + } + + if (!blocksmd.allLast || trapctxs.length > 1) { + execute.mark(end); + } + } + + void processThrow(final ThrowContext ctx) { + writer.visit(ctx.expression()); + execute.throwException(); + } + + void processExpr(final ExprContext ctx) { + final StatementMetadata exprsmd = metadata.getStatementMetadata(ctx); + final ExpressionContext exprctx = ctx.expression(); + final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); + writer.visit(exprctx); + + if (exprsmd.methodEscape) { + execute.returnValue(); + } else { + utility.writePop(expremd.to.type.getSize()); + } + } + + void processMultiple(final MultipleContext ctx) { + for (final StatementContext sctx : ctx.statement()) { + writer.visit(sctx); + } + } + + void processSingle(final SingleContext ctx) { + writer.visit(ctx.statement()); + } + + void processInitializer(InitializerContext ctx) { + final DeclarationContext declctx = ctx.declaration(); + final ExpressionContext exprctx = ctx.expression(); + + if (declctx != null) { + writer.visit(declctx); + } else if (exprctx != null) { + final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); + writer.visit(exprctx); + utility.writePop(expremd.to.type.getSize()); + } else { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + } + + void processAfterthought(AfterthoughtContext ctx) { + final ExpressionContext exprctx = ctx.expression(); + final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx); + writer.visit(ctx.expression()); + utility.writePop(expremd.to.type.getSize()); + } + + void processDeclaration(DeclarationContext ctx) { + for (final DeclvarContext declctx : ctx.declvar()) { + writer.visit(declctx); + } + } + + void processDeclvar(final DeclvarContext ctx) { + final ExpressionMetadata declvaremd = metadata.getExpressionMetadata(ctx); + final org.objectweb.asm.Type type = declvaremd.to.type; + final Sort sort = declvaremd.to.sort; + final int slot = (int)declvaremd.postConst; + + final ExpressionContext exprctx = ctx.expression(); + final boolean initialize = exprctx == null; + + if (!initialize) { + writer.visit(exprctx); + } + + switch (sort) { + case VOID: throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + case BOOL: + case BYTE: + case SHORT: + case CHAR: + case INT: if (initialize) execute.push(0); break; + case LONG: if (initialize) execute.push(0L); break; + case FLOAT: if (initialize) execute.push(0.0F); break; + case DOUBLE: if (initialize) execute.push(0.0); break; + default: if (initialize) execute.visitInsn(Opcodes.ACONST_NULL); + } + + execute.visitVarInsn(type.getOpcode(Opcodes.ISTORE), slot); + } + + void processTrap(final TrapContext ctx) { + final StatementMetadata trapsmd = metadata.getStatementMetadata(ctx); + + final Branch branch = utility.getBranch(ctx); + final Label jump = new Label(); + + final BlockContext blockctx = ctx.block(); + final EmptyscopeContext emptyctx = ctx.emptyscope(); + + execute.mark(jump); + execute.visitVarInsn(trapsmd.exception.type.getOpcode(Opcodes.ISTORE), trapsmd.slot); + + if (blockctx != null) { + writer.visit(ctx.block()); + } else if (emptyctx == null) { + throw new IllegalStateException(WriterUtility.error(ctx) + "Unexpected state."); + } + + execute.visitTryCatchBlock(branch.begin, branch.end, jump, trapsmd.exception.type.getInternalName()); + + if (branch.tru != null && !trapsmd.allLast) { + execute.goTo(branch.tru); + } + } + + private void writeLoopCounter(final int count) { + final Label end = new Label(); + + execute.iinc(metadata.loopCounterSlot, -count); + execute.visitVarInsn(Opcodes.ILOAD, metadata.loopCounterSlot); + execute.push(0); + execute.ifICmp(GeneratorAdapter.GT, end); + execute.throwException(PAINLESS_ERROR_TYPE, + "The maximum number of statements that can be executed in a loop has been reached."); + execute.mark(end); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterUtility.java new file mode 100644 index 00000000000..d7e4f4ce42c --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterUtility.java @@ -0,0 +1,387 @@ +/* + * 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; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.GeneratorAdapter; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.elasticsearch.painless.PainlessParser.ADD; +import static org.elasticsearch.painless.PainlessParser.BWAND; +import static org.elasticsearch.painless.PainlessParser.BWOR; +import static org.elasticsearch.painless.PainlessParser.BWXOR; +import static org.elasticsearch.painless.PainlessParser.DIV; +import static org.elasticsearch.painless.PainlessParser.LSH; +import static org.elasticsearch.painless.PainlessParser.MUL; +import static org.elasticsearch.painless.PainlessParser.REM; +import static org.elasticsearch.painless.PainlessParser.RSH; +import static org.elasticsearch.painless.PainlessParser.SUB; +import static org.elasticsearch.painless.PainlessParser.USH; +import static org.elasticsearch.painless.WriterConstants.ADDEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.ADDEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.ADDWOOVERLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.ADDWOOVERLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.DEF_ADD_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_AND_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_DIV_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_LSH_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_MUL_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_OR_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_REM_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_RSH_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_SUB_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_USH_CALL; +import static org.elasticsearch.painless.WriterConstants.DEF_XOR_CALL; +import static org.elasticsearch.painless.WriterConstants.DIVWOOVERLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.DIVWOOVERLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.DIVWOOVERLOW_INT; +import static org.elasticsearch.painless.WriterConstants.DIVWOOVERLOW_LONG; +import static org.elasticsearch.painless.WriterConstants.MULEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.MULEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.MULWOOVERLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.MULWOOVERLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.REMWOOVERLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.REMWOOVERLOW_FLOAT; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_BOOLEAN; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_CHAR; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_FLOAT; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_INT; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_LONG; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_OBJECT; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_STRING; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_CONSTRUCTOR; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_TOSTRING; +import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_TYPE; +import static org.elasticsearch.painless.WriterConstants.SUBEXACT_INT; +import static org.elasticsearch.painless.WriterConstants.SUBEXACT_LONG; +import static org.elasticsearch.painless.WriterConstants.SUBWOOVERLOW_DOUBLE; +import static org.elasticsearch.painless.WriterConstants.SUBWOOVERLOW_FLOAT; + +class WriterUtility { + static class Branch { + final ParserRuleContext source; + + Label begin = null; + Label end = null; + Label tru = null; + Label fals = null; + + private Branch(final ParserRuleContext source) { + this.source = source; + } + } + + /** + * A utility method to output consistent error messages. + * @param ctx The ANTLR node the error occurred in. + * @return The error message with tacked on line number and character position. + */ + static String error(final ParserRuleContext ctx) { + return "Writer Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: "; + } + + private final Definition definition; + private final CompilerSettings settings; + + private final GeneratorAdapter execute; + + private final Map branches = new HashMap<>(); + private final Deque jumps = new ArrayDeque<>(); + private final Set strings = new HashSet<>(); + + WriterUtility(final Metadata metadata, final GeneratorAdapter execute) { + definition = metadata.definition; + settings = metadata.settings; + + this.execute = execute; + } + + Branch markBranch(final ParserRuleContext source, final ParserRuleContext... nodes) { + final Branch branch = new Branch(source); + + for (final ParserRuleContext node : nodes) { + branches.put(node, branch); + } + + return branch; + } + + void copyBranch(final Branch branch, final ParserRuleContext... nodes) { + for (final ParserRuleContext node : nodes) { + branches.put(node, branch); + } + } + + Branch getBranch(final ParserRuleContext source) { + return branches.get(source); + } + + void checkWriteBranch(final ParserRuleContext source) { + final Branch branch = getBranch(source); + + if (branch != null) { + if (branch.tru != null) { + execute.visitJumpInsn(Opcodes.IFNE, branch.tru); + } else if (branch.fals != null) { + execute.visitJumpInsn(Opcodes.IFEQ, branch.fals); + } + } + } + + void pushJump(final Branch branch) { + jumps.push(branch); + } + + Branch peekJump() { + return jumps.peek(); + } + + void popJump() { + jumps.pop(); + } + + void addStrings(final ParserRuleContext source) { + strings.add(source); + } + + boolean containsStrings(final ParserRuleContext source) { + return strings.contains(source); + } + + void removeStrings(final ParserRuleContext source) { + strings.remove(source); + } + + void writeDup(final int size, final boolean x1, final boolean x2) { + if (size == 1) { + if (x2) { + execute.dupX2(); + } else if (x1) { + execute.dupX1(); + } else { + execute.dup(); + } + } else if (size == 2) { + if (x2) { + execute.dup2X2(); + } else if (x1) { + execute.dup2X1(); + } else { + execute.dup2(); + } + } + } + + void writePop(final int size) { + if (size == 1) { + execute.pop(); + } else if (size == 2) { + execute.pop2(); + } + } + + void writeConstant(final ParserRuleContext source, final Object constant) { + if (constant instanceof Number) { + writeNumeric(source, constant); + } else if (constant instanceof Character) { + writeNumeric(source, (int)(char)constant); + } else if (constant instanceof String) { + writeString(source, constant); + } else if (constant instanceof Boolean) { + writeBoolean(source, constant); + } else if (constant != null) { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } + + void writeNumeric(final ParserRuleContext source, final Object numeric) { + if (numeric instanceof Double) { + execute.push((double)numeric); + } else if (numeric instanceof Float) { + execute.push((float)numeric); + } else if (numeric instanceof Long) { + execute.push((long)numeric); + } else if (numeric instanceof Number) { + execute.push(((Number)numeric).intValue()); + } else { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } + + void writeString(final ParserRuleContext source, final Object string) { + if (string instanceof String) { + execute.push((String)string); + } else { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } + + void writeBoolean(final ParserRuleContext source, final Object bool) { + if (bool instanceof Boolean) { + execute.push((boolean)bool); + } else { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } + + void writeNewStrings() { + execute.newInstance(STRINGBUILDER_TYPE); + execute.dup(); + execute.invokeConstructor(STRINGBUILDER_TYPE, STRINGBUILDER_CONSTRUCTOR); + } + + void writeAppendStrings(final Sort sort) { + switch (sort) { + case BOOL: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_BOOLEAN); break; + case CHAR: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_CHAR); break; + case BYTE: + case SHORT: + case INT: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_INT); break; + case LONG: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_LONG); break; + case FLOAT: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_FLOAT); break; + case DOUBLE: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_DOUBLE); break; + case STRING: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_STRING); break; + default: execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_APPEND_OBJECT); + } + } + + void writeToStrings() { + execute.invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER_TOSTRING); + } + + void writeBinaryInstruction(final ParserRuleContext source, final Type type, final int token) { + final Sort sort = type.sort; + final boolean exact = !settings.getNumericOverflow() && + ((sort == Sort.INT || sort == Sort.LONG) && + (token == MUL || token == DIV || token == ADD || token == SUB) || + (sort == Sort.FLOAT || sort == Sort.DOUBLE) && + (token == MUL || token == DIV || token == REM || token == ADD || token == SUB)); + + // If it's a 64-bit shift, fix-up the last argument to truncate to 32-bits. + // Note that unlike java, this means we still do binary promotion of shifts, + // but it keeps things simple, and this check works because we promote shifts. + if (sort == Sort.LONG && (token == LSH || token == USH || token == RSH)) { + execute.cast(org.objectweb.asm.Type.LONG_TYPE, org.objectweb.asm.Type.INT_TYPE); + } + + if (exact) { + switch (sort) { + case INT: + switch (token) { + case MUL: execute.invokeStatic(definition.mathType.type, MULEXACT_INT); break; + case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_INT); break; + case ADD: execute.invokeStatic(definition.mathType.type, ADDEXACT_INT); break; + case SUB: execute.invokeStatic(definition.mathType.type, SUBEXACT_INT); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + + break; + case LONG: + switch (token) { + case MUL: execute.invokeStatic(definition.mathType.type, MULEXACT_LONG); break; + case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_LONG); break; + case ADD: execute.invokeStatic(definition.mathType.type, ADDEXACT_LONG); break; + case SUB: execute.invokeStatic(definition.mathType.type, SUBEXACT_LONG); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + + break; + case FLOAT: + switch (token) { + case MUL: execute.invokeStatic(definition.utilityType.type, MULWOOVERLOW_FLOAT); break; + case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_FLOAT); break; + case REM: execute.invokeStatic(definition.utilityType.type, REMWOOVERLOW_FLOAT); break; + case ADD: execute.invokeStatic(definition.utilityType.type, ADDWOOVERLOW_FLOAT); break; + case SUB: execute.invokeStatic(definition.utilityType.type, SUBWOOVERLOW_FLOAT); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + + break; + case DOUBLE: + switch (token) { + case MUL: execute.invokeStatic(definition.utilityType.type, MULWOOVERLOW_DOUBLE); break; + case DIV: execute.invokeStatic(definition.utilityType.type, DIVWOOVERLOW_DOUBLE); break; + case REM: execute.invokeStatic(definition.utilityType.type, REMWOOVERLOW_DOUBLE); break; + case ADD: execute.invokeStatic(definition.utilityType.type, ADDWOOVERLOW_DOUBLE); break; + case SUB: execute.invokeStatic(definition.utilityType.type, SUBWOOVERLOW_DOUBLE); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + + break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } else { + if ((sort == Sort.FLOAT || sort == Sort.DOUBLE) && + (token == LSH || token == USH || token == RSH || token == BWAND || token == BWXOR || token == BWOR)) { + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + + if (sort == Sort.DEF) { + switch (token) { + case MUL: execute.invokeStatic(definition.defobjType.type, DEF_MUL_CALL); break; + case DIV: execute.invokeStatic(definition.defobjType.type, DEF_DIV_CALL); break; + case REM: execute.invokeStatic(definition.defobjType.type, DEF_REM_CALL); break; + case ADD: execute.invokeStatic(definition.defobjType.type, DEF_ADD_CALL); break; + case SUB: execute.invokeStatic(definition.defobjType.type, DEF_SUB_CALL); break; + case LSH: execute.invokeStatic(definition.defobjType.type, DEF_LSH_CALL); break; + case USH: execute.invokeStatic(definition.defobjType.type, DEF_RSH_CALL); break; + case RSH: execute.invokeStatic(definition.defobjType.type, DEF_USH_CALL); break; + case BWAND: execute.invokeStatic(definition.defobjType.type, DEF_AND_CALL); break; + case BWXOR: execute.invokeStatic(definition.defobjType.type, DEF_XOR_CALL); break; + case BWOR: execute.invokeStatic(definition.defobjType.type, DEF_OR_CALL); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } else { + switch (token) { + case MUL: execute.math(GeneratorAdapter.MUL, type.type); break; + case DIV: execute.math(GeneratorAdapter.DIV, type.type); break; + case REM: execute.math(GeneratorAdapter.REM, type.type); break; + case ADD: execute.math(GeneratorAdapter.ADD, type.type); break; + case SUB: execute.math(GeneratorAdapter.SUB, type.type); break; + case LSH: execute.math(GeneratorAdapter.SHL, type.type); break; + case USH: execute.math(GeneratorAdapter.USHR, type.type); break; + case RSH: execute.math(GeneratorAdapter.SHR, type.type); break; + case BWAND: execute.math(GeneratorAdapter.AND, type.type); break; + case BWXOR: execute.math(GeneratorAdapter.XOR, type.type); break; + case BWOR: execute.math(GeneratorAdapter.OR, type.type); break; + default: + throw new IllegalStateException(WriterUtility.error(source) + "Unexpected state."); + } + } + } + } +}