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;
}
}