Java tutorial
package org.elasticsearch.plan.a; /* * 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. */ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; import java.util.List; import org.antlr.v4.runtime.ParserRuleContext; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import static org.elasticsearch.plan.a.Adapter.*; import static org.elasticsearch.plan.a.Caster.*; import static org.elasticsearch.plan.a.Default.*; import static org.elasticsearch.plan.a.Definition.*; import static org.elasticsearch.plan.a.PlanAParser.*; class External { private abstract class Segment { final ParserRuleContext source; Segment(final ParserRuleContext source) { this.source = source; } abstract void write(); } private class VariableSegment extends Segment { private final Variable variable; private final boolean store; VariableSegment(final ParserRuleContext source, final Variable variable, final boolean store) { super(source); this.variable = variable; this.store = store; } @Override void write() { final TypeMetadata metadata = variable.type.metadata; final int slot = variable.slot; switch (metadata) { case VOID: throw new IllegalStateException(); case BOOL: case BYTE: case SHORT: case CHAR: case INT: visitor.visitVarInsn(store ? Opcodes.ISTORE : Opcodes.ILOAD, slot); break; case LONG: visitor.visitVarInsn(store ? Opcodes.LSTORE : Opcodes.LLOAD, slot); break; case FLOAT: visitor.visitVarInsn(store ? Opcodes.FSTORE : Opcodes.FLOAD, slot); break; case DOUBLE: visitor.visitVarInsn(store ? Opcodes.DSTORE : Opcodes.DLOAD, slot); break; default: visitor.visitVarInsn(store ? Opcodes.ASTORE : Opcodes.ALOAD, slot); } } } private class FieldSegment extends Segment { private final Field field; private final boolean store; FieldSegment(final ParserRuleContext source, final Field field, final boolean store) { super(source); this.field = field; this.store = store; } @Override void write() { final String internal = field.owner.internal; final String name = field.field.getName(); final String descriptor = field.type.descriptor; int opcode; if (java.lang.reflect.Modifier.isStatic(field.field.getModifiers())) { opcode = store ? Opcodes.PUTSTATIC : Opcodes.GETSTATIC; } else { opcode = store ? Opcodes.PUTFIELD : Opcodes.GETFIELD; } visitor.visitFieldInsn(opcode, internal, name, descriptor); } } private class NewSegment extends Segment { private final Struct struct; NewSegment(final ParserRuleContext source, final Struct struct) { super(source); this.struct = struct; } @Override void write() { final String internal = struct.internal; visitor.visitTypeInsn(Opcodes.NEW, internal); } } private class ConstructorSegment extends Segment { private final Constructor constructor; ConstructorSegment(final ParserRuleContext source, final Constructor constructor) { super(source); this.constructor = constructor; } @Override void write() { final String internal = constructor.owner.internal; final String descriptor = constructor.descriptor; visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, internal, "<init>", descriptor, false); } } private class MethodSegment extends Segment { private final String internal; private final String name; private final String descriptor; private final boolean statik; private final boolean iface; MethodSegment(final ParserRuleContext source, final Method method) { super(source); this.internal = method.owner.internal; this.name = method.method.getName(); this.descriptor = method.descriptor; statik = java.lang.reflect.Modifier.isStatic(method.method.getModifiers()); iface = java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers()); } @Override void write() { if (statik) { visitor.visitMethodInsn(Opcodes.INVOKESTATIC, internal, name, descriptor, false); } else if (iface) { visitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, internal, name, descriptor, true); } else { visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, internal, name, descriptor, false); } } } private class NodeSegment extends Segment { NodeSegment(final ParserRuleContext source) { super(source); } @Override void write() { writer.visit(source); } } private class ArraySegment extends Segment { private final Type type; private final boolean store; ArraySegment(final ParserRuleContext source, final Type type, final boolean store) { super(source); this.type = type; this.store = store; } @Override void write() { switch (type.metadata) { case VOID: throw new IllegalStateException(error(source) + "Unexpected state during write."); case BYTE: visitor.visitInsn(store ? Opcodes.BASTORE : Opcodes.BALOAD); break; case SHORT: visitor.visitInsn(store ? Opcodes.SASTORE : Opcodes.SALOAD); break; case CHAR: visitor.visitInsn(store ? Opcodes.CASTORE : Opcodes.CALOAD); break; case BOOL: case INT: visitor.visitInsn(store ? Opcodes.IASTORE : Opcodes.IALOAD); break; case LONG: visitor.visitInsn(store ? Opcodes.LASTORE : Opcodes.LALOAD); break; case FLOAT: visitor.visitInsn(store ? Opcodes.FASTORE : Opcodes.FALOAD); break; case DOUBLE: visitor.visitInsn(store ? Opcodes.DASTORE : Opcodes.DALOAD); break; default: visitor.visitInsn(store ? Opcodes.AASTORE : Opcodes.AALOAD); break; } } } private class MakeSegment extends Segment { private final Type type; private final int dimensions; MakeSegment(final ParserRuleContext source, final Type type, final int dimensions) { super(source); this.type = type; this.dimensions = dimensions; } @Override void write() { if (dimensions == 1) { switch (type.metadata) { case VOID: throw new IllegalStateException(error(source) + "Unexpected state during write."); case BOOL: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BOOLEAN); break; case BYTE: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE); break; case SHORT: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_SHORT); break; case CHAR: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_CHAR); break; case INT: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT); break; case LONG: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_LONG); break; case FLOAT: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_FLOAT); break; case DOUBLE: visitor.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_DOUBLE); break; default: visitor.visitTypeInsn(Opcodes.ANEWARRAY, type.internal); } } else if (dimensions > 0) { final String descriptor = getTypeWithArrayDimensions(type.struct, dimensions).descriptor; visitor.visitMultiANewArrayInsn(descriptor, dimensions); } else { throw new IllegalStateException(error(source) + "Unexpected state during write."); } } } private class LengthSegment extends Segment { LengthSegment(final ParserRuleContext source) { super(source); } @Override void write() { visitor.visitInsn(Opcodes.ARRAYLENGTH); } } private class CastSegment extends Segment { private final Cast cast; CastSegment(final ParserRuleContext source, final Cast cast) { super(source); this.cast = cast; } @Override void write() { caster.checkWriteCast(visitor, source, cast); } } private class TokenSegment extends Segment { private final Type type; private final int token; TokenSegment(final ParserRuleContext source, final Type type, final int token) { super(source); this.type = type; this.token = token; } @Override void write() { writer.writeBinaryInstruction(source, type.metadata, token); } } private class NewStringsSegment extends Segment { NewStringsSegment(final ParserRuleContext source) { super(source); } @Override void write() { writer.writeNewStrings(); adapter.markStrings(source); } } private class AppendStringsSegment extends Segment { private final Type type; private final boolean force; AppendStringsSegment(final ParserRuleContext source, final Type type, final boolean force) { super(source); this.type = type; this.force = force; } @Override void write() { if (force || adapter.getStrings(source)) { writer.writeAppendStrings(source, type.metadata); adapter.unmarkStrings(source); } } } private class ToStringsSegment extends Segment { ToStringsSegment(final ParserRuleContext source) { super(source); } @Override void write() { writer.writeToStrings(); } } private class InstructionSegment extends Segment { private final int instruction; InstructionSegment(final ParserRuleContext source, final int instruction) { super(source); this.instruction = instruction; } @Override void write() { visitor.visitInsn(instruction); } } private class IncrementSegment extends Segment { private final Variable variable; private final int value; IncrementSegment(final ParserRuleContext source, final Variable variable, final int value) { super(source); this.variable = variable; this.value = value; } @Override void write() { visitor.visitIincInsn(variable.slot, value); } } private final Adapter adapter; private final Definition definition; private final Standard standard; private final Caster caster; private final Analyzer analyzer; private Writer writer; private MethodVisitor visitor; private boolean read; private ParserRuleContext write; private int token; private boolean post; private int prec; private Type current; private boolean statik; private boolean statement; private final Deque<Segment> segments; External(final Adapter adapter, final Analyzer analyzer) { this.adapter = adapter; definition = adapter.definition; standard = adapter.standard; caster = adapter.caster; this.analyzer = analyzer; writer = null; visitor = null; read = false; write = null; token = 0; post = false; prec = 0; current = null; statik = false; statement = false; segments = new ArrayDeque<>(); } void setWriter(final Writer writer, final MethodVisitor visitor) { this.writer = writer; this.visitor = visitor; } void write(final ParserRuleContext ctx) { final ExpressionMetadata writeemd = adapter.getExpressionMetadata(ctx); for (Segment segment : segments) { segment.write(); } caster.checkWriteCast(visitor, writeemd); adapter.checkWriteBranch(visitor, ctx); } void ext(ExtContext ctx) { final ExpressionMetadata extemd = adapter.getExpressionMetadata(ctx); read = extemd.promotion != null || extemd.to.metadata != TypeMetadata.VOID; start(ctx.extstart()); extemd.statement = statement; extemd.from = read ? current : standard.voidType; caster.markCast(extemd); } void assignment(AssignmentContext ctx) { final ExpressionMetadata assignemd = adapter.getExpressionMetadata(ctx); read = assignemd.promotion != null || assignemd.to.metadata != TypeMetadata.VOID; write = adapter.getExpressionContext(ctx.expression()); if (ctx.AMUL() != null) token = MUL; else if (ctx.ADIV() != null) token = DIV; else if (ctx.AREM() != null) token = REM; else if (ctx.AADD() != null) token = ADD; else if (ctx.ASUB() != null) token = SUB; else if (ctx.ALSH() != null) token = LSH; else if (ctx.AUSH() != null) token = USH; else if (ctx.ARSH() != null) token = RSH; else if (ctx.AAND() != null) token = BWAND; else if (ctx.AXOR() != null) token = BWXOR; else if (ctx.AOR() != null) token = BWOR; else if (ctx.ACAT() != null) { token = CAT; segments.add(new NewStringsSegment(write)); } start(ctx.extstart()); assignemd.from = current; assignemd.statement = true; caster.markCast(assignemd); } void postinc(PostincContext ctx) { final ExpressionMetadata postincemd = adapter.getExpressionMetadata(ctx); read = postincemd.promotion != null || postincemd.to.metadata != TypeMetadata.VOID; write = ctx.increment(); token = ADD; post = true; start(ctx.extstart()); postincemd.from = current; postincemd.statement = true; caster.markCast(postincemd); } void preinc(PreincContext ctx) { final ExpressionMetadata preincemd = adapter.getExpressionMetadata(ctx); read = preincemd.promotion != null || preincemd.to.metadata != TypeMetadata.VOID; write = ctx.increment(); token = ADD; start(ctx.extstart()); preincemd.from = current; preincemd.statement = true; caster.markCast(preincemd); } private void start(final ExtstartContext startctx) { final ExtprecContext precctx = startctx.extprec(); final ExtcastContext castctx = startctx.extcast(); final ExttypeContext typectx = startctx.exttype(); final ExtmemberContext memberctx = startctx.extmember(); if (precctx != null) { prec(precctx); } else if (castctx != null) { cast(castctx); } else if (typectx != null) { type(typectx); } else if (memberctx != null) { member(memberctx); } else { throw new IllegalStateException(); } } public void prec(final ExtprecContext ctx) { final ExtprecContext precctx = ctx.extprec(); final ExtcastContext castctx = ctx.extcast(); final ExttypeContext typectx = ctx.exttype(); final ExtmemberContext memberctx = ctx.extmember(); final ExtdotContext dotctx = ctx.extdot(); final ExtbraceContext bracectx = ctx.extbrace(); if (dotctx != null || bracectx != null) { ++prec; } if (precctx != null) { prec(precctx); } else if (castctx != null) { cast(castctx); } else if (typectx != null) { type(typectx); } else if (memberctx != null) { member(memberctx); } else { throw new IllegalStateException(error(ctx) + "Unexpected parser state."); } statement = false; if (dotctx != null) { --prec; dot(dotctx); } else if (bracectx != null) { --prec; brace(bracectx); } } public void cast(final ExtcastContext ctx) { final ExtprecContext precctx = ctx.extprec(); final ExtcastContext castctx = ctx.extcast(); final ExttypeContext typectx = ctx.exttype(); final ExtmemberContext memberctx = ctx.extmember(); if (precctx != null) { prec(precctx); } else if (castctx != null) { cast(castctx); } else if (typectx != null) { type(typectx); } else if (memberctx != null) { member(memberctx); } else { throw new IllegalStateException(error(ctx) + "Unexpected parser state."); } final DecltypeContext declctx = ctx.decltype(); final ExpressionMetadata declemd = adapter.createExpressionMetadata(declctx); analyzer.visit(declctx); final Cast cast = caster.getLegalCast(ctx, current, declemd.from, true); segments.add(new CastSegment(ctx, cast)); current = declemd.from; statement = false; } public void brace(final ExtbraceContext ctx) { final ExpressionContext exprctx = adapter.getExpressionContext(ctx.expression()); final ExtdotContext dotctx = ctx.extdot(); final ExtbraceContext bracectx = ctx.extbrace(); final boolean last = prec == 0 && dotctx == null && bracectx == null; array(ctx, exprctx, last); if (dotctx != null) { dot(dotctx); } else if (bracectx != null) { brace(bracectx); } } public void dot(final ExtdotContext ctx) { final ExtcallContext callctx = ctx.extcall(); final ExtmemberContext memberctx = ctx.extmember(); if (callctx != null) { call(callctx); } else if (memberctx != null) { member(memberctx); } } public void type(final ExttypeContext ctx) { if (current != null) { throw new IllegalArgumentException(error(ctx) + "Unexpected static type."); } final String typestr = ctx.ID().getText(); current = getTypeFromCanonicalName(definition, typestr); statik = true; dot(ctx.extdot()); } public void call(final ExtcallContext ctx) { final String name = ctx.ID().getText(); final List<ExpressionContext> arguments = ctx.arguments().expression(); final ExtdotContext dotctx = ctx.extdot(); final ExtbraceContext bracectx = ctx.extbrace(); final boolean last = prec == 0 && dotctx == null && bracectx == null; method(ctx, name, arguments, last); if (dotctx != null) { dot(dotctx); } else if (bracectx != null) { brace(bracectx); } } public void member(final ExtmemberContext ctx) { final String name = ctx.ID().getText(); final ExtdotContext dotctx = ctx.extdot(); final ExtbraceContext bracectx = ctx.extbrace(); final boolean last = prec == 0 && dotctx == null && bracectx == null; if (current == null) { variable(ctx, name, last); } else { field(ctx, name, last); } if (dotctx != null) { dot(dotctx); } else if (bracectx != null) { brace(bracectx); } } private void variable(final ParserRuleContext source, final String name, final boolean last) { final Variable variable = adapter.getVariable(name); if (variable == null) { throw new IllegalArgumentException(error(source) + "Unknown variable [" + name + "]."); } final Type type = variable.type; if (last && write != null) { final ExpressionMetadata writeemd = adapter.createExpressionMetadata(write); if (token == CAT) { writeemd.promotion = caster.concat; analyzer.visit(write); writeemd.to = writeemd.from; caster.markCast(writeemd); final Cast cast = caster.getLegalCast(source, standard.stringType, type, false); segments.add(new VariableSegment(source, variable, false)); segments.add(new AppendStringsSegment(source, type, true)); segments.add(new NodeSegment(write)); segments.add(new AppendStringsSegment(write, writeemd.to, false)); segments.add(new ToStringsSegment(source)); segments.add(new CastSegment(source, cast)); if (read) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new VariableSegment(source, variable, true)); } else if (token > 0) { final boolean increment = type.metadata == TypeMetadata.INT && (token == ADD || token == SUB); current = type; final Cast[] casts = toNumericCasts(source); writeemd.to = current; analyzer.visit(write); if (increment && writeemd.postConst != null) { if (read && post) { segments.add(new VariableSegment(source, variable, false)); } final int value = token == SUB ? -1 * (int) writeemd.postConst : (int) writeemd.postConst; segments.add(new IncrementSegment(source, variable, value)); if (read && !post) { segments.add(new VariableSegment(source, variable, false)); } } else { segments.add(new VariableSegment(source, variable, false)); if (read && post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new CastSegment(source, casts[0])); segments.add(new NodeSegment(write)); segments.add(new TokenSegment(source, current, token)); segments.add(new CastSegment(source, casts[1])); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new VariableSegment(source, variable, true)); } } else { writeemd.to = type; analyzer.visit(write); segments.add(new NodeSegment(write)); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new VariableSegment(source, variable, true)); } current = read ? type : standard.voidType; } else { segments.add(new VariableSegment(source, variable, false)); current = variable.type; } } private void field(final ParserRuleContext source, final String name, final boolean last) { if (current.metadata == TypeMetadata.ARRAY) { if ("length".equals(name)) { if (!read || last && write != null) { throw new IllegalArgumentException(error(source) + "Cannot write to read-only field [length]."); } segments.add(new LengthSegment(source)); current = standard.intType; } else { throw new IllegalArgumentException(error(source) + "Unexpected array field [" + name + "]."); } } else { final Struct struct = current.struct; final Field field = statik ? struct.statics.get(name) : struct.members.get(name); if (field == null) { throw new IllegalArgumentException( error(source) + "Unknown field [" + name + "] for type [" + struct.name + "]."); } if (last && write != null) { if (java.lang.reflect.Modifier.isFinal(field.field.getModifiers())) { throw new IllegalArgumentException(error(source) + "Cannot write to read-only" + " field [" + name + "] for type [" + struct.name + "]."); } final ExpressionMetadata writeemd = adapter.createExpressionMetadata(write); final Type type = field.type; if (token == CAT) { writeemd.promotion = caster.concat; analyzer.visit(write); writeemd.to = writeemd.from; caster.markCast(writeemd); final Cast cast = caster.getLegalCast(source, standard.stringType, type, false); segments.add(new InstructionSegment(source, Opcodes.DUP_X1)); segments.add(new FieldSegment(source, field, false)); segments.add(new AppendStringsSegment(source, type, true)); segments.add(new NodeSegment(write)); segments.add(new AppendStringsSegment(write, writeemd.to, false)); segments.add(new ToStringsSegment(source)); segments.add(new CastSegment(source, cast)); if (read) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X1)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X1)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new FieldSegment(source, field, true)); } else if (token > 0) { current = type; final Cast[] casts = toNumericCasts(source); writeemd.to = current; analyzer.visit(write); segments.add(new InstructionSegment(source, Opcodes.DUP)); segments.add(new FieldSegment(source, field, false)); if (read && post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X1)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X1)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new CastSegment(source, casts[0])); segments.add(new NodeSegment(write)); segments.add(new TokenSegment(source, current, token)); segments.add(new CastSegment(source, casts[1])); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X1)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X1)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new FieldSegment(source, field, true)); } else { writeemd.to = type; analyzer.visit(write); segments.add(new NodeSegment(write)); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X1)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X1)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new FieldSegment(source, field, true)); } current = read ? type : standard.voidType; } else { segments.add(new FieldSegment(source, field, false)); current = field.type; } } } private void method(final ParserRuleContext source, final String name, final List<ExpressionContext> arguments, final boolean last) { final Struct struct = current.struct; Type[] types; Segment segment0; Segment segment1 = null; if (current.dimensions > 0) { throw new IllegalArgumentException(error(source) + "Unexpected call [" + name + "] on an array."); } else if (last && write != null) { throw new IllegalArgumentException(error(source) + "Cannot assign a value to a call [" + name + "]."); } else if (statik && "makearray".equals(name)) { if (!read) { throw new IllegalArgumentException(error(source) + "A newly created array must be assigned."); } types = new Type[arguments.size()]; Arrays.fill(types, standard.intType); segment0 = new MakeSegment(source, current, arguments.size()); current = getTypeWithArrayDimensions(struct, arguments.size()); } else { final Constructor constructor = statik ? struct.constructors.get(name) : null; final Method method = statik ? struct.functions.get(name) : struct.methods.get(name); if (constructor != null) { types = new Type[constructor.arguments.size()]; constructor.arguments.toArray(types); segments.add(new NewSegment(source, constructor.owner)); if (read) { segments.add(new InstructionSegment(source, Opcodes.DUP)); } else { current = standard.voidType; statement = true; } segment0 = new ConstructorSegment(source, constructor); } else if (method != null) { types = new Type[method.arguments.size()]; method.arguments.toArray(types); if (!read) { final int size = method.rtn.metadata.size; if (size == 1) { segment1 = new InstructionSegment(source, Opcodes.POP); } else if (size == 2) { segment1 = new InstructionSegment(source, Opcodes.POP2); } else if (size != 0) { throw new IllegalStateException(error(source) + "Unexpected type size."); } current = standard.voidType; statement = true; } else { current = method.rtn; } segment0 = new MethodSegment(source, method); } else { throw new IllegalArgumentException( error(source) + "Unknown call [" + name + "] on type [" + struct.name + "]."); } } if (arguments.size() != types.length) { throw new IllegalArgumentException(); } for (int argument = 0; argument < arguments.size(); ++argument) { final ExpressionContext exprctx = adapter.getExpressionContext(arguments.get(argument)); final ExpressionMetadata expremd = adapter.createExpressionMetadata(exprctx); expremd.to = types[argument]; analyzer.visit(exprctx); segments.add(new NodeSegment(exprctx)); } segments.add(segment0); if (segment1 != null) { segments.add(segment1); } } private void array(final ParserRuleContext source, final ExpressionContext exprctx, final boolean last) { if (current.dimensions == 0) { throw new IllegalArgumentException( error(source) + "Attempting to address a non-array type [" + current.name + "] as an array."); } final ExpressionMetadata expremd = adapter.createExpressionMetadata(exprctx); expremd.to = standard.intType; analyzer.visit(exprctx); segments.add(new NodeSegment(exprctx)); final Type type = getTypeWithArrayDimensions(current.struct, current.dimensions - 1); if (last && write != null) { final ExpressionMetadata writeemd = adapter.createExpressionMetadata(write); if (token == CAT) { writeemd.promotion = caster.concat; analyzer.visit(write); writeemd.to = writeemd.from; caster.markCast(writeemd); final Cast cast = caster.getLegalCast(source, standard.stringType, type, false); segments.add(new InstructionSegment(source, Opcodes.DUP2_X1)); segments.add(new ArraySegment(source, type, false)); segments.add(new AppendStringsSegment(source, type, true)); segments.add(new NodeSegment(write)); segments.add(new AppendStringsSegment(write, writeemd.to, false)); segments.add(new ToStringsSegment(source)); segments.add(new CastSegment(source, cast)); if (read) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X2)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new ArraySegment(source, type, true)); } else if (token > 0) { current = type; final Cast[] casts = toNumericCasts(source); writeemd.to = current; analyzer.visit(write); segments.add(new InstructionSegment(source, Opcodes.DUP2)); segments.add(new ArraySegment(source, type, false)); if (read && post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X2)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new CastSegment(source, casts[0])); segments.add(new NodeSegment(write)); segments.add(new TokenSegment(source, current, token)); segments.add(new CastSegment(source, casts[1])); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X2)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new ArraySegment(source, type, true)); } else { writeemd.to = type; analyzer.visit(write); segments.add(new NodeSegment(write)); if (read && !post) { if (type.metadata.size == 1) { segments.add(new InstructionSegment(source, Opcodes.DUP_X2)); } else if (type.metadata.size == 2) { segments.add(new InstructionSegment(source, Opcodes.DUP2_X2)); } else { throw new IllegalStateException(error(source) + "Unexpected type size."); } } segments.add(new ArraySegment(source, type, true)); } current = read ? type : standard.voidType; } else { segments.add(new ArraySegment(source, type, false)); current = type; } } private Cast[] toNumericCasts(final ParserRuleContext source) { final boolean decimal = token == MUL || token == DIV || token == REM || token == ADD || token == SUB; final Promotion promotion = decimal ? caster.decimal : caster.numeric; final Type promote = caster.getTypePromotion(source, current, null, promotion); final Cast[] casts = new Cast[2]; casts[0] = caster.getLegalCast(source, current, promote, false); casts[1] = caster.getLegalCast(source, promote, current, true); current = promote; return casts; } }