parent
6ae738a2eb
commit
800c844ebd
File diff suppressed because it is too large
Load Diff
|
@ -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 + "].");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<ExpressionContext> 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<ExpressionContext> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<StatementContext> 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<StatementContext> 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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Integer> scopes = new ArrayDeque<>();
|
||||
private final Deque<Variable> 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<Variable> 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;
|
||||
}
|
||||
}
|
|
@ -160,7 +160,7 @@ final class Compiler {
|
|||
// throw new RuntimeException(e);
|
||||
// }
|
||||
|
||||
final Class<? extends Executable> clazz = loader.define(Writer.CLASS_NAME, bytes);
|
||||
final Class<? extends Executable> clazz = loader.define(WriterConstants.CLASS_NAME, bytes);
|
||||
final java.lang.reflect.Constructor<? extends Executable> constructor =
|
||||
clazz.getConstructor(Definition.class, String.class, String.class);
|
||||
|
||||
|
|
|
@ -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() + "].");
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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, "<init>", 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/String;Ljava/lang/Object;>;)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, "<init>");
|
||||
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() {}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<ExpressionContext> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<ParserRuleContext, Branch> branches = new HashMap<>();
|
||||
private final Deque<Branch> jumps = new ArrayDeque<>();
|
||||
private final Set<ParserRuleContext> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue