Plan A Update...
New Features: Exceptions (throw, try/catch) Infinite Loop Checking Iterators added to the API Fixes: Improved loop path analysis Fixed set/add issue with shortcuts in lists Fixed minor promotion issue with incorrect type Fixed score issue related to score extending Number Documentation: Added JavaDocs for some files
This commit is contained in:
parent
da699ceae2
commit
2249a640bb
|
@ -34,18 +34,23 @@ statement
|
||||||
| CONTINUE SEMICOLON? # continue
|
| CONTINUE SEMICOLON? # continue
|
||||||
| BREAK SEMICOLON? # break
|
| BREAK SEMICOLON? # break
|
||||||
| RETURN expression SEMICOLON? # return
|
| RETURN expression SEMICOLON? # return
|
||||||
| TRY block ( CATCH LP ( TYPE ID ) RP block )+ # try
|
| TRY block trap+ # try
|
||||||
| THROW expression SEMICOLON? # throw
|
| THROW expression SEMICOLON? # throw
|
||||||
| expression SEMICOLON? # expr
|
| expression SEMICOLON? # expr
|
||||||
;
|
;
|
||||||
|
|
||||||
block
|
block
|
||||||
: LBRACK statement* RBRACK # multiple
|
: LBRACK statement+ RBRACK # multiple
|
||||||
| statement # single
|
| statement # single
|
||||||
;
|
;
|
||||||
|
|
||||||
empty
|
empty
|
||||||
: SEMICOLON
|
: emptyscope
|
||||||
|
| SEMICOLON
|
||||||
|
;
|
||||||
|
|
||||||
|
emptyscope
|
||||||
|
: LBRACK RBRACK
|
||||||
;
|
;
|
||||||
|
|
||||||
initializer
|
initializer
|
||||||
|
@ -69,6 +74,10 @@ declvar
|
||||||
: ID ( ASSIGN expression )?
|
: ID ( ASSIGN expression )?
|
||||||
;
|
;
|
||||||
|
|
||||||
|
trap
|
||||||
|
: CATCH LP ( TYPE ID ) RP ( block | emptyscope )
|
||||||
|
;
|
||||||
|
|
||||||
expression
|
expression
|
||||||
: LP expression RP # precedence
|
: LP expression RP # precedence
|
||||||
| ( OCTAL | HEX | INTEGER | DECIMAL ) # numeric
|
| ( OCTAL | HEX | INTEGER | DECIMAL ) # numeric
|
||||||
|
|
|
@ -1,278 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.plan.a;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.ParserRuleContext;
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.elasticsearch.plan.a.Definition.Cast;
|
|
||||||
import static org.elasticsearch.plan.a.Definition.Type;
|
|
||||||
import static org.elasticsearch.plan.a.PlanAParser.ExpressionContext;
|
|
||||||
import static org.elasticsearch.plan.a.PlanAParser.PrecedenceContext;
|
|
||||||
|
|
||||||
class Adapter {
|
|
||||||
static class StatementMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean last;
|
|
||||||
|
|
||||||
boolean allExit;
|
|
||||||
boolean allReturn;
|
|
||||||
boolean anyReturn;
|
|
||||||
boolean allBreak;
|
|
||||||
boolean anyBreak;
|
|
||||||
boolean allContinue;
|
|
||||||
boolean anyContinue;
|
|
||||||
|
|
||||||
private StatementMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
last = false;
|
|
||||||
|
|
||||||
allExit = false;
|
|
||||||
allReturn = false;
|
|
||||||
anyReturn = false;
|
|
||||||
allBreak = false;
|
|
||||||
anyBreak = false;
|
|
||||||
allContinue = false;
|
|
||||||
anyContinue = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExpressionMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean read;
|
|
||||||
boolean statement;
|
|
||||||
|
|
||||||
Object preConst;
|
|
||||||
Object postConst;
|
|
||||||
boolean isNull;
|
|
||||||
|
|
||||||
Type to;
|
|
||||||
Type from;
|
|
||||||
boolean explicit;
|
|
||||||
boolean typesafe;
|
|
||||||
|
|
||||||
Cast cast;
|
|
||||||
|
|
||||||
private ExpressionMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
read = true;
|
|
||||||
statement = false;
|
|
||||||
|
|
||||||
preConst = null;
|
|
||||||
postConst = null;
|
|
||||||
isNull = false;
|
|
||||||
|
|
||||||
to = null;
|
|
||||||
from = null;
|
|
||||||
explicit = false;
|
|
||||||
typesafe = true;
|
|
||||||
|
|
||||||
cast = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExternalMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean read;
|
|
||||||
ParserRuleContext storeExpr;
|
|
||||||
int token;
|
|
||||||
boolean pre;
|
|
||||||
boolean post;
|
|
||||||
|
|
||||||
int scope;
|
|
||||||
Type current;
|
|
||||||
boolean statik;
|
|
||||||
boolean statement;
|
|
||||||
Object constant;
|
|
||||||
|
|
||||||
private ExternalMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
read = false;
|
|
||||||
storeExpr = null;
|
|
||||||
token = 0;
|
|
||||||
pre = false;
|
|
||||||
post = false;
|
|
||||||
|
|
||||||
scope = 0;
|
|
||||||
current = null;
|
|
||||||
statik = false;
|
|
||||||
statement = false;
|
|
||||||
constant = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExtNodeMetadata {
|
|
||||||
final ParserRuleContext parent;
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
Object target;
|
|
||||||
boolean last;
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
Type promote;
|
|
||||||
|
|
||||||
Cast castFrom;
|
|
||||||
Cast castTo;
|
|
||||||
|
|
||||||
private ExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
target = null;
|
|
||||||
last = false;
|
|
||||||
|
|
||||||
type = null;
|
|
||||||
promote = null;
|
|
||||||
|
|
||||||
castFrom = null;
|
|
||||||
castTo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String error(final ParserRuleContext ctx) {
|
|
||||||
return "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: ";
|
|
||||||
}
|
|
||||||
|
|
||||||
final Definition definition;
|
|
||||||
final String source;
|
|
||||||
final ParserRuleContext root;
|
|
||||||
final CompilerSettings settings;
|
|
||||||
|
|
||||||
private final Map<ParserRuleContext, StatementMetadata> statementMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExpressionMetadata> expressionMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExternalMetadata> externalMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExtNodeMetadata> extNodeMetadata;
|
|
||||||
|
|
||||||
Adapter(final Definition definition, final String source, final ParserRuleContext root, final CompilerSettings settings) {
|
|
||||||
this.definition = definition;
|
|
||||||
this.source = source;
|
|
||||||
this.root = root;
|
|
||||||
this.settings = settings;
|
|
||||||
|
|
||||||
statementMetadata = new HashMap<>();
|
|
||||||
expressionMetadata = new HashMap<>();
|
|
||||||
externalMetadata = new HashMap<>();
|
|
||||||
extNodeMetadata = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
StatementMetadata createStatementMetadata(final ParserRuleContext source) {
|
|
||||||
final StatementMetadata sourcesmd = new StatementMetadata(source);
|
|
||||||
statementMetadata.put(source, sourcesmd);
|
|
||||||
|
|
||||||
return sourcesmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
StatementMetadata getStatementMetadata(final ParserRuleContext source) {
|
|
||||||
final StatementMetadata sourcesmd = statementMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourcesmd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "Statement metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourcesmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionContext updateExpressionTree(ExpressionContext source) {
|
|
||||||
if (source instanceof PrecedenceContext) {
|
|
||||||
final ParserRuleContext parent = source.getParent();
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
for (final ParseTree child : parent.children) {
|
|
||||||
if (child == source) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (source instanceof PrecedenceContext) {
|
|
||||||
source = ((PrecedenceContext)source).expression();
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.children.set(index, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionMetadata createExpressionMetadata(ParserRuleContext source) {
|
|
||||||
final ExpressionMetadata sourceemd = new ExpressionMetadata(source);
|
|
||||||
expressionMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionMetadata getExpressionMetadata(final ParserRuleContext source) {
|
|
||||||
final ExpressionMetadata sourceemd = expressionMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "Expression metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalMetadata createExternalMetadata(final ParserRuleContext source) {
|
|
||||||
final ExternalMetadata sourceemd = new ExternalMetadata(source);
|
|
||||||
externalMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalMetadata getExternalMetadata(final ParserRuleContext source) {
|
|
||||||
final ExternalMetadata sourceemd = externalMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtNodeMetadata createExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
|
||||||
final ExtNodeMetadata sourceemd = new ExtNodeMetadata(parent, source);
|
|
||||||
extNodeMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtNodeMetadata getExtNodeMetadata(final ParserRuleContext source) {
|
|
||||||
final ExtNodeMetadata sourceemd = extNodeMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,88 +30,87 @@ import java.security.CodeSource;
|
||||||
import java.security.SecureClassLoader;
|
import java.security.SecureClassLoader;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Compiler is the entry point for generating a Plan A script. The compiler will generate an ANTLR
|
||||||
|
* parse tree based on the source code that is passed in. Two passes will then be run over the parse tree,
|
||||||
|
* one for analysis using the {@link Analyzer} and another to generate the actual byte code using ASM in
|
||||||
|
* the {@link Writer}.
|
||||||
|
*/
|
||||||
final class Compiler {
|
final class Compiler {
|
||||||
|
/**
|
||||||
|
* The default language API to be used with Plan A. The second construction is used
|
||||||
|
* to finalize all the variables, so there is no mistake of modification afterwards.
|
||||||
|
*/
|
||||||
private static Definition DEFAULT_DEFINITION = new Definition(new Definition());
|
private static Definition DEFAULT_DEFINITION = new Definition(new Definition());
|
||||||
|
|
||||||
/** we define the class with lowest privileges */
|
/**
|
||||||
|
* Define the class with lowest privileges.
|
||||||
|
*/
|
||||||
private static final CodeSource CODESOURCE;
|
private static final CodeSource CODESOURCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the code privileges.
|
||||||
|
*/
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
|
// Setup the code privileges.
|
||||||
CODESOURCE = new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[]) null);
|
CODESOURCE = new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[]) null);
|
||||||
} catch (MalformedURLException impossible) {
|
} catch (MalformedURLException impossible) {
|
||||||
throw new RuntimeException(impossible);
|
throw new RuntimeException(impossible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A secure class loader used to define Plan A scripts.
|
||||||
|
*/
|
||||||
static class Loader extends SecureClassLoader {
|
static class Loader extends SecureClassLoader {
|
||||||
Loader(ClassLoader parent) {
|
/**
|
||||||
|
* @param parent The parent ClassLoader.
|
||||||
|
*/
|
||||||
|
Loader(final ClassLoader parent) {
|
||||||
super(parent);
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends Executable> define(String name, byte[] bytes) {
|
/**
|
||||||
|
* Generates a Class object from the generated byte code.
|
||||||
|
* @param name The name of the class.
|
||||||
|
* @param bytes The generated byte code.
|
||||||
|
* @return A Class object extending {@link Executable}.
|
||||||
|
*/
|
||||||
|
Class<? extends Executable> define(final String name, final byte[] bytes) {
|
||||||
return defineClass(name, bytes, 0, bytes.length, CODESOURCE).asSubclass(Executable.class);
|
return defineClass(name, bytes, 0, bytes.length, CODESOURCE).asSubclass(Executable.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Executable compile(Loader loader, final String name, final String source, final Definition custom, CompilerSettings settings) {
|
/**
|
||||||
long start = System.currentTimeMillis();
|
* Runs the two-pass compiler to generate a Plan A script.
|
||||||
|
* @param loader The ClassLoader used to define the script.
|
||||||
final Definition definition = custom == null ? DEFAULT_DEFINITION : new Definition(custom);
|
* @param name The name of the script.
|
||||||
|
* @param source The source code for the script.
|
||||||
//long end = System.currentTimeMillis() - start;
|
* @param settings The CompilerSettings to be used during the compilation.
|
||||||
//System.out.println("types: " + end);
|
* @return An {@link Executable} Plan A script.
|
||||||
//start = System.currentTimeMillis();
|
*/
|
||||||
|
static Executable compile(final Loader loader, final String name, final String source,
|
||||||
//final ParserRuleContext root = createParseTree(source, types);
|
final Definition custom, final CompilerSettings settings) {
|
||||||
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
final Definition definition = custom != null ? new Definition(custom) : DEFAULT_DEFINITION;
|
||||||
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
final ParserRuleContext root = createParseTree(source, definition);
|
||||||
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
final Metadata metadata = new Metadata(definition, source, root, settings);
|
||||||
final ParserErrorStrategy strategy = new ParserErrorStrategy();
|
Analyzer.analyze(metadata);
|
||||||
|
final byte[] bytes = Writer.write(metadata);
|
||||||
lexer.removeErrorListeners();
|
|
||||||
lexer.setTypes(definition.structs.keySet());
|
|
||||||
|
|
||||||
//List<? extends Token> tokens = lexer.getAllTokens();
|
|
||||||
|
|
||||||
//for (final Token token : tokens) {
|
|
||||||
// System.out.println(token.getType() + " " + token.getText());
|
|
||||||
//}
|
|
||||||
|
|
||||||
parser.removeErrorListeners();
|
|
||||||
parser.setErrorHandler(strategy);
|
|
||||||
|
|
||||||
ParserRuleContext root = parser.source();
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("tree: " + end);
|
|
||||||
|
|
||||||
final Adapter adapter = new Adapter(definition, source, root, settings);
|
|
||||||
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
Analyzer.analyze(adapter);
|
|
||||||
//System.out.println(root.toStringTree(parser));
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("analyze: " + end);
|
|
||||||
//start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final byte[] bytes = Writer.write(adapter);
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("write: " + end);
|
|
||||||
//start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final Executable executable = createExecutable(loader, definition, name, source, bytes);
|
final Executable executable = createExecutable(loader, definition, name, source, bytes);
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("create: " + end);
|
|
||||||
|
|
||||||
return executable;
|
return executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParserRuleContext createParseTree(String source, Definition definition) {
|
/**
|
||||||
|
* Generates the ANTLR tree from the given source code. Several methods below, are used
|
||||||
|
* to ensure that the first error generated by ANTLR will cause the compilation to fail rather than
|
||||||
|
* use ANTLR's recovery strategies that may be potentially dangerous.
|
||||||
|
* @param source The source code for the script.
|
||||||
|
* @param definition The Plan A API.
|
||||||
|
* @return The root node for the ANTLR parse tree.
|
||||||
|
*/
|
||||||
|
private static ParserRuleContext createParseTree(final String source, final Definition definition) {
|
||||||
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
||||||
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
||||||
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
||||||
|
@ -119,36 +118,50 @@ final class Compiler {
|
||||||
|
|
||||||
lexer.removeErrorListeners();
|
lexer.removeErrorListeners();
|
||||||
lexer.setTypes(definition.structs.keySet());
|
lexer.setTypes(definition.structs.keySet());
|
||||||
|
|
||||||
parser.removeErrorListeners();
|
parser.removeErrorListeners();
|
||||||
parser.setErrorHandler(strategy);
|
parser.setErrorHandler(strategy);
|
||||||
|
|
||||||
ParserRuleContext root = parser.source();
|
ParserRuleContext root = parser.source();
|
||||||
// System.out.println(root.toStringTree(parser));
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Executable createExecutable(Loader loader, Definition definition, String name, String source, byte[] bytes) {
|
/**
|
||||||
|
* Generates an {@link Executable} that can run a Plan A script.
|
||||||
|
* @param loader The {@link Loader} to define the script's class file.
|
||||||
|
* @param definition The Plan A API.
|
||||||
|
* @param name The name of the script.
|
||||||
|
* @param source The source text of the script.
|
||||||
|
* @param bytes The ASM generated byte code to define the class with.
|
||||||
|
* @return A Plan A {@link Executable} script.
|
||||||
|
*/
|
||||||
|
private static Executable createExecutable(final Loader loader, final Definition definition,
|
||||||
|
final String name, final String source, final byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
// for debugging:
|
// Used for debugging. Uncomment this code and add -Dtests.security.manager=false when running to save
|
||||||
//try {
|
// the generated Java class files. The javap tool can then be used to inspect the generated byte code.
|
||||||
// FileOutputStream f = new FileOutputStream(new File("/Users/jdconrad/lang/generated/out.class"), false);
|
|
||||||
// f.write(bytes);
|
// try {
|
||||||
// f.close();
|
// FileOutputStream f = new FileOutputStream(new File("<path>"), false);
|
||||||
//} catch (Exception e) {
|
// f.write(bytes);
|
||||||
// throw new RuntimeException(e);
|
// f.close();
|
||||||
//}
|
// } catch (Exception e) {
|
||||||
|
// throw new RuntimeException(e);
|
||||||
|
// }
|
||||||
|
|
||||||
final Class<? extends Executable> clazz = loader.define(Writer.CLASS_NAME, bytes);
|
final Class<? extends Executable> clazz = loader.define(Writer.CLASS_NAME, bytes);
|
||||||
final java.lang.reflect.Constructor<? extends Executable> constructor =
|
final java.lang.reflect.Constructor<? extends Executable> constructor =
|
||||||
clazz.getConstructor(Definition.class, String.class, String.class);
|
clazz.getConstructor(Definition.class, String.class, String.class);
|
||||||
|
|
||||||
return constructor.newInstance(definition, name, source);
|
return constructor.newInstance(definition, name, source);
|
||||||
} catch (Exception exception) {
|
} catch (final Exception exception) { // Catch everything to let the user know this is something caused internally.
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"An internal error occurred attempting to define the script [" + name + "].", exception);
|
"An internal error occurred attempting to define the script [" + name + "].", exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All methods in the compiler should be static.
|
||||||
|
*/
|
||||||
private Compiler() {}
|
private Compiler() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,30 @@
|
||||||
|
|
||||||
package org.elasticsearch.plan.a;
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings to use when compiling a script
|
* Settings to use when compiling a script.
|
||||||
*/
|
*/
|
||||||
final class CompilerSettings {
|
final class CompilerSettings {
|
||||||
|
/**
|
||||||
|
* Constant to be used when specifying numeric overflow when compiling a script.
|
||||||
|
*/
|
||||||
|
public static final String NUMERIC_OVERFLOW = "numeric_overflow";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant to be used when specifying the maximum loop counter when compiling a script.
|
||||||
|
*/
|
||||||
|
public static final String MAX_LOOP_COUNTER = "max_loop_counter";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to allow numeric values to overflow without exception.
|
||||||
|
*/
|
||||||
private boolean numericOverflow = true;
|
private boolean numericOverflow = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of statements allowed to be run in a loop.
|
||||||
|
*/
|
||||||
|
private int maxLoopCounter = 10000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if numeric operations should overflow, {@code false}
|
* Returns {@code true} if numeric operations should overflow, {@code false}
|
||||||
* if they should signal an exception.
|
* if they should signal an exception.
|
||||||
|
@ -46,4 +63,20 @@ final class CompilerSettings {
|
||||||
public void setNumericOverflow(boolean allow) {
|
public void setNumericOverflow(boolean allow) {
|
||||||
this.numericOverflow = allow;
|
this.numericOverflow = allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value for the cumulative total number of statements that can be made in all loops
|
||||||
|
* in a script before an exception is thrown. This attempts to prevent infinite loops.
|
||||||
|
*/
|
||||||
|
public int getMaxLoopCounter() {
|
||||||
|
return maxLoopCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the cumulative total number of statements that can be made in all loops.
|
||||||
|
* @see #getMaxLoopCounter
|
||||||
|
*/
|
||||||
|
public void setMaxLoopCounter(int max) {
|
||||||
|
this.maxLoopCounter = max;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class Def {
|
||||||
} else if (owner instanceof List) {
|
} else if (owner instanceof List) {
|
||||||
try {
|
try {
|
||||||
final int index = Integer.parseInt(name);
|
final int index = Integer.parseInt(name);
|
||||||
((List)owner).add(index, value);
|
((List)owner).set(index, value);
|
||||||
} catch (NumberFormatException exception) {
|
} catch (NumberFormatException exception) {
|
||||||
throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "].");
|
throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "].");
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ public class Def {
|
||||||
"in array class [" + array.getClass().getCanonicalName() + "].", throwable);
|
"in array class [" + array.getClass().getCanonicalName() + "].", throwable);
|
||||||
}
|
}
|
||||||
} else if (array instanceof List) {
|
} else if (array instanceof List) {
|
||||||
((List)array).add((int)index, value);
|
((List)array).set((int)index, value);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Attempting to address a non-array type " +
|
throw new IllegalArgumentException("Attempting to address a non-array type " +
|
||||||
"[" + array.getClass().getCanonicalName() + "] as an array.");
|
"[" + array.getClass().getCanonicalName() + "] as an array.");
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,3 @@
|
||||||
package org.elasticsearch.plan.a;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
* license agreements. See the NOTICE file distributed with
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
@ -19,6 +17,8 @@ package org.elasticsearch.plan.a;
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.LexerNoViableAltException;
|
import org.antlr.v4.runtime.LexerNoViableAltException;
|
||||||
import org.antlr.v4.runtime.misc.Interval;
|
import org.antlr.v4.runtime.misc.Interval;
|
||||||
|
|
|
@ -0,0 +1,619 @@
|
||||||
|
/*
|
||||||
|
* 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.plan.a;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.elasticsearch.plan.a.Definition.Cast;
|
||||||
|
import org.elasticsearch.plan.a.Definition.Type;
|
||||||
|
import org.elasticsearch.plan.a.PlanAParser.ExpressionContext;
|
||||||
|
import org.elasticsearch.plan.a.PlanAParser.PrecedenceContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata is a wrapper for all the data that is collected by the {@link Analyzer}. Each node in the ANTLR parse tree
|
||||||
|
* will have one of the types of metadata to store information used either in a different node by the analyzer
|
||||||
|
* or by the {@link Writer} during byte code generation. Metadata also contains several objects passed into the
|
||||||
|
* {@link Analyzer} and {@link Writer} used during compilation including the {@link Definition}, the source code,
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
static class StatementMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lastSource variable will be set to true when the final statement from the root ANTLR node is about
|
||||||
|
* to be visited. This is used to determine whether or not the auto-return feature is allowed to be used,
|
||||||
|
* and if a null return value needs to be generated automatically since a return value is always required.
|
||||||
|
*/
|
||||||
|
boolean lastSource = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The beginLoop variable will be set to true whenever a loop node is initially visited including inner
|
||||||
|
* loops. This will not be propagated down the parse tree afterwards, though. This is used to determine
|
||||||
|
* whether or not inLoop should be set further down the tree. Note that inLoop alone is not enough
|
||||||
|
* information to determine whether we are in the last statement of a loop because we may inside of
|
||||||
|
* multiple loops, so this variable is necessary.
|
||||||
|
*/
|
||||||
|
boolean beginLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The inLoop variable is set to true when inside a loop. This will be propagated down the parse tree. This
|
||||||
|
* is used to determine whether or not continue and break statements are legal.
|
||||||
|
*/
|
||||||
|
boolean inLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lastLoop variable is set to true when the final statement of a loop is reached. This will be
|
||||||
|
* propagated down the parse tree until another loop is reached and then will not be propagated further for
|
||||||
|
* the current loop. This is used to determine whether or not a continue statement is superfluous.
|
||||||
|
*/
|
||||||
|
boolean lastLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The methodEscape variable is set to true when a statement would cause the method to potentially exit. This
|
||||||
|
* includes return, throw, and continuous loop statements. Note that a catch statement may possibly
|
||||||
|
* reset this to false after a throw statement. This will be propagated up the tree as far as necessary.
|
||||||
|
* This is used by the {@link Writer} to ensure that superfluous statements aren't unnecessarily written
|
||||||
|
* into generated bytecode.
|
||||||
|
*/
|
||||||
|
boolean methodEscape = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The loopEscape variable is set to true when a loop is going to be exited. This may be caused by a number of
|
||||||
|
* different statements including continue, break, return, etc. This will only be propagated as far as the
|
||||||
|
* loop node. This is used to ensure that in certain case an infinite loop will be caught at
|
||||||
|
* compile-time rather than run-time.
|
||||||
|
*/
|
||||||
|
boolean loopEscape = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The allLast variable is set whenever a final statement in a block is reached. This includes the end of loop,
|
||||||
|
* if, else, etc. This will be only propagated to the top of the block statement ANTLR node.
|
||||||
|
* This is used to ensure that there are no unreachable statements within the script.
|
||||||
|
*/
|
||||||
|
boolean allLast = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The anyContinue will be set to true when a continue statement is visited. This will be propagated to the
|
||||||
|
* loop node it's within. This is used to ensure that in certain case an infinite loop will be caught at
|
||||||
|
* compile-time rather than run-time.
|
||||||
|
*/
|
||||||
|
boolean anyContinue = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The anyBreak will be set to true when a break statement is visited. This will be propagated to the
|
||||||
|
* loop node it's within. This is used to in conjunction with methodEscape to ensure there are no unreachable
|
||||||
|
* statements within the script.
|
||||||
|
*/
|
||||||
|
boolean anyBreak = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The count variable is used as a rudimentary count of statements within a loop. This will be used in
|
||||||
|
* the {@link Writer} to keep a count of statements that have been executed at run-time to ensure that a loop
|
||||||
|
* will exit if it runs too long.
|
||||||
|
*/
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exception variable is used to store the exception type when a throw node is visited. This is used by
|
||||||
|
* the {@link Writer} to write the correct type of exception in the generated byte code.
|
||||||
|
*/
|
||||||
|
Type exception = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The slot variable is used to store the place on the stack of where a thrown exception will be stored to.
|
||||||
|
* This is used by the {@link Writer}.
|
||||||
|
*/
|
||||||
|
int slot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private StatementMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpressionMetadata is used to store metadata mostly about constants and casting
|
||||||
|
* for ANTLR nodes related to mathematical operations.
|
||||||
|
*/
|
||||||
|
static class ExpressionMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The read variable is used to determine whether or not the value of an expression will be read from.
|
||||||
|
* This is set to false when the expression is the left-hand side of an assignment that is not chained or
|
||||||
|
* when a method call is made alone. This will propagate down the tree as far as necessary.
|
||||||
|
* The {@link Writer} uses this to determine when a value may need to be popped from the stack
|
||||||
|
* such as when a method call returns a value that is never read.
|
||||||
|
*/
|
||||||
|
boolean read = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statement variable is set true when an expression is a complete meaning that there is some sort
|
||||||
|
* of effect on a variable or a method call is made. This will propagate up the tree as far as necessary.
|
||||||
|
* This prevents statements that have no effect on the output of a script from being executed.
|
||||||
|
*/
|
||||||
|
boolean statement = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preConst variable is set to a non-null value when a constant statement is made in a script. This is
|
||||||
|
* used to track the constant value prior to any casts being made on an ANTLR node.
|
||||||
|
*/
|
||||||
|
Object preConst = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The postConst variable is set to a non-null value when a cast is made on a node where a preConst variable
|
||||||
|
* has already been set when the cast would leave the constant as a non-object value except in the case of a
|
||||||
|
* String. This will be propagated up the tree and used to simplify constants when possible such as making
|
||||||
|
* the value of 2*2 be 4 in the * node, so that the {@link Writer} only has to push a 4 onto the stack.
|
||||||
|
*/
|
||||||
|
Object postConst = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The isNull variable is set to true when a null constant statement is made in the script. This allows the
|
||||||
|
* {@link Writer} to potentially shortcut certain comparison operations.
|
||||||
|
*/
|
||||||
|
boolean isNull = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The to variable is used to track what an ANTLR node's value should be cast to. This is set on every ANTLR
|
||||||
|
* node in the tree, and used by the {@link Writer} to make a run-time cast if necessary in the byte code.
|
||||||
|
* This is also used by the {@link Analyzer} to determine if a cast is legal.
|
||||||
|
*/
|
||||||
|
Type to = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The from variable is used to track what an ANTLR node's value should be cast from. This is set on every
|
||||||
|
* ANTLR node in the tree independent of other nodes. This is used by the {@link Analyzer} to determine if a
|
||||||
|
* cast is legal.
|
||||||
|
*/
|
||||||
|
Type from = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The explicit variable is set to true when a cast is explicitly made in the script. This tracks whether
|
||||||
|
* or not a cast is a legal up cast.
|
||||||
|
*/
|
||||||
|
boolean explicit = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The typesafe variable is set to true when a dynamic type is used as part of an expression. This propagates
|
||||||
|
* up the tree to the top of the expression. This allows for implicit up casts throughout the expression and
|
||||||
|
* is used by the {@link Analyzer}.
|
||||||
|
*/
|
||||||
|
boolean typesafe = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set to the combination of the to and from variables at the end of each node visit in the
|
||||||
|
* {@link Analyzer}. This is set on every ANTLR node in the tree independent of other nodes, and is
|
||||||
|
* used by {@link Writer} to make a run-time cast if necessary in the byte code.
|
||||||
|
*/
|
||||||
|
Cast cast = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExpressionMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExternalMetadata is used to store metadata about the overall state of a variable/method chain such as
|
||||||
|
* '(int)x.get(3)' where each piece of that chain is broken into it's indiviual pieces and stored in
|
||||||
|
* {@link ExtNodeMetadata}.
|
||||||
|
*/
|
||||||
|
static class ExternalMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The read variable is set to true when the value of a variable/method chain is going to be read from.
|
||||||
|
* This is used by the {@link Analyzer} to determine if this variable/method chain will be in a standalone
|
||||||
|
* statement.
|
||||||
|
*/
|
||||||
|
boolean read = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The storeExpr variable is set to the right-hand side of an assignment in the variable/method chain if
|
||||||
|
* necessary. This is used by the {@link Analyzer} to set the proper metadata for a read versus a write,
|
||||||
|
* and is used by the {@link Writer} to determine if a bytecode operation should be a load or a store.
|
||||||
|
*/
|
||||||
|
ParserRuleContext storeExpr = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token variable is set to a constant value of the operator type (+, -, etc.) when a compound assignment
|
||||||
|
* is being visited. This is also used by the increment and decrement operators. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle the compound assignment.
|
||||||
|
*/
|
||||||
|
int token = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre variable is set to true when pre-increment or pre-decrement is visited. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle any reads of the variable/method chain that are
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
boolean pre = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The post variable is set to true when post-increment or post-decrement is visited. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle any reads of the variable/method chain that are
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
boolean post = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scope variable is incremented and decremented when a precedence node is visited as part of a
|
||||||
|
* variable/method chain. This is used by the {@link Analyzer} to determine when the final piece of the
|
||||||
|
* variable/method chain has been reached.
|
||||||
|
*/
|
||||||
|
int scope = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current variable is set to whatever the current type is within the visited node of the variable/method
|
||||||
|
* chain. This changes as the nodes for the variable/method are walked through. This is used by the
|
||||||
|
* {@link Analyzer} to make decisions about whether or not a cast is legal, and what methods are available
|
||||||
|
* for that specific type.
|
||||||
|
*/
|
||||||
|
Type current = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statik variable is set to true when a variable/method chain begins with static type. This is used by
|
||||||
|
* the {@link Analyzer} to determine what methods/members are available for that specific type.
|
||||||
|
*/
|
||||||
|
boolean statik = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statement variable is set to true when a variable/method chain can be standalone statement. This is
|
||||||
|
* used by the {@link Analyzer} to error out if there a variable/method chain that is not a statement.
|
||||||
|
*/
|
||||||
|
boolean statement = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant variable is set when a String constant is part of the variable/method chain. String is a
|
||||||
|
* special case because methods/members need to be able to be called on a String constant, so this can't be
|
||||||
|
* only as part of {@link ExpressionMetadata}. This is used by the {@link Writer} to write out the String
|
||||||
|
* constant in the byte code.
|
||||||
|
*/
|
||||||
|
Object constant = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExternalMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExtNodeMetadata {
|
||||||
|
/**
|
||||||
|
* The parent variable is top-level ANTLR node of the variable/method chain. This is used to retrieve the
|
||||||
|
* ExternalMetadata for the variable/method chain this ExtNodeMetadata is a piece of.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target variable is set to a value based on the type of ANTLR node that is visited. This is used by
|
||||||
|
* {@link Writer} to determine whether a cast, store, load, or method call should be written in byte code
|
||||||
|
* depending on what the target variable is.
|
||||||
|
*/
|
||||||
|
Object target = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last variable is set to true when the last ANTLR node of the variable/method chain is visted. This is
|
||||||
|
* used by the {@link Writer} in conjuction with the storeExpr variable to determine whether or not a store
|
||||||
|
* needs to be written as opposed to a load.
|
||||||
|
*/
|
||||||
|
boolean last = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type variable is set to the type that a visited node ends with. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to make decisions about compound assignments, String constants, and
|
||||||
|
* shortcuts.
|
||||||
|
*/
|
||||||
|
Type type = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The promote variable is set to the type of a promotion within a compound assignment. Compound assignments
|
||||||
|
* may require promotion between the left-hand side variable and right-hand side value. This is used by the
|
||||||
|
* {@link Writer} to make the correct decision about the byte code operation.
|
||||||
|
*/
|
||||||
|
Type promote = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The castFrom variable is set during a compound assignment. This is used by the {@link Writer} to
|
||||||
|
* cast the values to the promoted type during a compound assignment.
|
||||||
|
*/
|
||||||
|
Cast castFrom = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The castTo variable is set during an explicit cast in a variable/method chain or during a compound
|
||||||
|
* assignment. This is used by the {@link Writer} to either do an explicit cast, or cast the values
|
||||||
|
* from the promoted type back to the original type during a compound assignment.
|
||||||
|
*/
|
||||||
|
Cast castTo = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param parent The top-level ANTLR node for the variable/method chain.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
||||||
|
this.parent = parent;
|
||||||
|
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 "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acts as both the Plan A API and white-list for what types and methods are allowed.
|
||||||
|
*/
|
||||||
|
final Definition definition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original text of the input script. This is used to write out the source code into
|
||||||
|
* the byte code file for debugging purposes.
|
||||||
|
*/
|
||||||
|
final String source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toot node of the ANTLR tree for the Plan A script.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine certain compile-time constraints such as whether or not numeric overflow is allowed
|
||||||
|
* and how many statements are allowed before a loop will throw an exception.
|
||||||
|
*/
|
||||||
|
final CompilerSettings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the input variable is stored in. This is used in the {@link Writer} whenever
|
||||||
|
* the input variable is accessed.
|
||||||
|
*/
|
||||||
|
int inputValueSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the score variable is stored in. This is used in the {@link Writer} whenever
|
||||||
|
* the score variable is accessed.
|
||||||
|
*/
|
||||||
|
int scoreValueSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the loopCounter variable is stored in. This is used n the {@link Writer} whenever
|
||||||
|
* the loop variable is accessed.
|
||||||
|
*/
|
||||||
|
int loopCounterSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, StatementMetadata> statementMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExpressionMetadata> expressionMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExternalMetadata> externalMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExtNodeMetadata> extNodeMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param definition The Plan A definition.
|
||||||
|
* @param source The source text for the script.
|
||||||
|
* @param root The root ANTLR node.
|
||||||
|
* @param settings The compile-time settings.
|
||||||
|
*/
|
||||||
|
Metadata(final Definition definition, final String source, final ParserRuleContext root, final CompilerSettings settings) {
|
||||||
|
this.definition = definition;
|
||||||
|
this.source = source;
|
||||||
|
this.root = root;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StatementMetadata and stores it in the statementMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new StatementMetadata.
|
||||||
|
*/
|
||||||
|
StatementMetadata createStatementMetadata(final ParserRuleContext source) {
|
||||||
|
final StatementMetadata sourcesmd = new StatementMetadata(source);
|
||||||
|
statementMetadata.put(source, sourcesmd);
|
||||||
|
|
||||||
|
return sourcesmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves StatementMetadata from the statementMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved StatementMetadata.
|
||||||
|
*/
|
||||||
|
StatementMetadata getStatementMetadata(final ParserRuleContext source) {
|
||||||
|
final StatementMetadata sourcesmd = statementMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourcesmd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "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.
|
||||||
|
* @return The new ExpressionMetadata.
|
||||||
|
*/
|
||||||
|
ExpressionMetadata createExpressionMetadata(ParserRuleContext source) {
|
||||||
|
final ExpressionMetadata sourceemd = new ExpressionMetadata(source);
|
||||||
|
expressionMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExpressionMetadata from the expressionMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExpressionMetadata.
|
||||||
|
*/
|
||||||
|
ExpressionMetadata getExpressionMetadata(final ParserRuleContext source) {
|
||||||
|
final ExpressionMetadata sourceemd = expressionMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "Expression metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ExternalMetadata and stores it in the externalMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new ExternalMetadata.
|
||||||
|
*/
|
||||||
|
ExternalMetadata createExternalMetadata(final ParserRuleContext source) {
|
||||||
|
final ExternalMetadata sourceemd = new ExternalMetadata(source);
|
||||||
|
externalMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExternalMetadata from the externalMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExternalMetadata.
|
||||||
|
*/
|
||||||
|
ExternalMetadata getExternalMetadata(final ParserRuleContext source) {
|
||||||
|
final ExternalMetadata sourceemd = externalMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ExtNodeMetadata and stores it in the extNodeMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new ExtNodeMetadata.
|
||||||
|
*/
|
||||||
|
ExtNodeMetadata createExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
||||||
|
final ExtNodeMetadata sourceemd = new ExtNodeMetadata(parent, source);
|
||||||
|
extNodeMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExtNodeMetadata from the extNodeMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExtNodeMetadata.
|
||||||
|
*/
|
||||||
|
ExtNodeMetadata getExtNodeMetadata(final ParserRuleContext source) {
|
||||||
|
final ExtNodeMetadata sourceemd = extNodeMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.plan.a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PlanAError class is used to throw internal errors caused by Plan A scripts that cannot be
|
||||||
|
* caught using a standard {@link Exception}. This prevents the user from catching this specific error
|
||||||
|
* (as Exceptions are available in the Plan A API, but Errors are not,) and possibly continuing to do
|
||||||
|
* something hazardous. The alternative was extending {@link Throwable}, but that seemed worse than using
|
||||||
|
* an {@link Error} in this case.
|
||||||
|
*/
|
||||||
|
public class PlanAError extends Error {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param message The error message.
|
||||||
|
*/
|
||||||
|
public PlanAError(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -116,6 +116,13 @@ class PlanAParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements P
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitEmpty(PlanAParser.EmptyContext ctx) { return visitChildren(ctx); }
|
@Override public T visitEmpty(PlanAParser.EmptyContext ctx) { return visitChildren(ctx); }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns the result of calling
|
||||||
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
|
*/
|
||||||
|
@Override public T visitEmptyscope(PlanAParser.EmptyscopeContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -151,6 +158,13 @@ class PlanAParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements P
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitDeclvar(PlanAParser.DeclvarContext ctx) { return visitChildren(ctx); }
|
@Override public T visitDeclvar(PlanAParser.DeclvarContext ctx) { return visitChildren(ctx); }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns the result of calling
|
||||||
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
|
*/
|
||||||
|
@Override public T visitTrap(PlanAParser.TrapContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -113,6 +113,12 @@ interface PlanAParserVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitEmpty(PlanAParser.EmptyContext ctx);
|
T visitEmpty(PlanAParser.EmptyContext ctx);
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by {@link PlanAParser#emptyscope}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
T visitEmptyscope(PlanAParser.EmptyscopeContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by {@link PlanAParser#initializer}.
|
* Visit a parse tree produced by {@link PlanAParser#initializer}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
@ -143,6 +149,12 @@ interface PlanAParserVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitDeclvar(PlanAParser.DeclvarContext ctx);
|
T visitDeclvar(PlanAParser.DeclvarContext ctx);
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by {@link PlanAParser#trap}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
T visitTrap(PlanAParser.TrapContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by the {@code comp}
|
* Visit a parse tree produced by the {@code comp}
|
||||||
* labeled alternative in {@link PlanAParser#expression}.
|
* labeled alternative in {@link PlanAParser#expression}.
|
||||||
|
|
|
@ -34,7 +34,7 @@ public final class PlanAPlugin extends Plugin {
|
||||||
return "Plan A scripting language for Elasticsearch";
|
return "Plan A scripting language for Elasticsearch";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onModule(ScriptModule module) {
|
public void onModule(final ScriptModule module) {
|
||||||
module.addScriptEngine(PlanAScriptEngineService.class);
|
module.addScriptEngine(PlanAScriptEngineService.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,117 +40,199 @@ import java.security.ProtectionDomain;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of a ScriptEngine for the Plan A language.
|
||||||
|
*/
|
||||||
public class PlanAScriptEngineService extends AbstractComponent implements ScriptEngineService {
|
public class PlanAScriptEngineService extends AbstractComponent implements ScriptEngineService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard name of the Plan A language.
|
||||||
|
*/
|
||||||
public static final String NAME = "plan-a";
|
public static final String NAME = "plan-a";
|
||||||
// default settings, used unless otherwise specified
|
|
||||||
|
/**
|
||||||
|
* Default compiler settings to be used.
|
||||||
|
*/
|
||||||
private static final CompilerSettings DEFAULT_COMPILER_SETTINGS = new CompilerSettings();
|
private static final CompilerSettings DEFAULT_COMPILER_SETTINGS = new CompilerSettings();
|
||||||
|
|
||||||
public static final String NUMERIC_OVERFLOW = "numeric_overflow";
|
/**
|
||||||
|
* Permissions context used during compilation.
|
||||||
|
*/
|
||||||
|
private static final AccessControlContext COMPILATION_CONTEXT;
|
||||||
|
|
||||||
// TODO: how should custom definitions be specified?
|
/**
|
||||||
|
* Setup the allowed permissions.
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
final Permissions none = new Permissions();
|
||||||
|
none.setReadOnly();
|
||||||
|
COMPILATION_CONTEXT = new AccessControlContext(new ProtectionDomain[] {
|
||||||
|
new ProtectionDomain(null, none)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only for testing.
|
||||||
|
*/
|
||||||
private Definition definition = null;
|
private Definition definition = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only for testing.
|
||||||
|
*/
|
||||||
|
void setDefinition(final Definition definition) {
|
||||||
|
this.definition = definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param settings The settings to initialize the engine with.
|
||||||
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
public PlanAScriptEngineService(Settings settings) {
|
public PlanAScriptEngineService(final Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefinition(final Definition definition) {
|
/**
|
||||||
this.definition = new Definition(definition);
|
* Get the type name(s) for the language.
|
||||||
}
|
* @return Always contains only the single name of the language.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] types() {
|
public String[] types() {
|
||||||
return new String[] { NAME };
|
return new String[] { NAME };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the extension(s) for the language.
|
||||||
|
* @return Always contains only the single extension of the language.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] extensions() {
|
public String[] extensions() {
|
||||||
return new String[] { NAME };
|
return new String[] { NAME };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the engine is secure.
|
||||||
|
* @return Always true as the engine should be secure at runtime.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean sandboxed() {
|
public boolean sandboxed() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// context used during compilation
|
/**
|
||||||
private static final AccessControlContext COMPILATION_CONTEXT;
|
* Compiles a Plan A script with the specified parameters.
|
||||||
static {
|
* @param script The code to be compiled.
|
||||||
Permissions none = new Permissions();
|
* @param params The params used to modify the compiler settings on a per script basis.
|
||||||
none.setReadOnly();
|
* @return Compiled script object represented by an {@link Executable}.
|
||||||
COMPILATION_CONTEXT = new AccessControlContext(new ProtectionDomain[] {
|
*/
|
||||||
new ProtectionDomain(null, none)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object compile(String script, Map<String, String> params) {
|
public Object compile(final String script, final Map<String, String> params) {
|
||||||
final CompilerSettings compilerSettings;
|
final CompilerSettings compilerSettings;
|
||||||
|
|
||||||
if (params.isEmpty()) {
|
if (params.isEmpty()) {
|
||||||
|
// Use the default settings.
|
||||||
compilerSettings = DEFAULT_COMPILER_SETTINGS;
|
compilerSettings = DEFAULT_COMPILER_SETTINGS;
|
||||||
} else {
|
} else {
|
||||||
// custom settings
|
// Use custom settings specified by params.
|
||||||
compilerSettings = new CompilerSettings();
|
compilerSettings = new CompilerSettings();
|
||||||
Map<String,String> clone = new HashMap<>(params);
|
Map<String, String> copy = new HashMap<>(params);
|
||||||
String value = clone.remove(NUMERIC_OVERFLOW);
|
String value = copy.remove(CompilerSettings.NUMERIC_OVERFLOW);
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
// TODO: can we get a real boolean parser in here?
|
|
||||||
compilerSettings.setNumericOverflow(Boolean.parseBoolean(value));
|
compilerSettings.setNumericOverflow(Boolean.parseBoolean(value));
|
||||||
}
|
}
|
||||||
if (!clone.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Unrecognized compile-time parameter(s): " + clone);
|
value = copy.remove(CompilerSettings.MAX_LOOP_COUNTER);
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
compilerSettings.setMaxLoopCounter(Integer.parseInt(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copy.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Unrecognized compile-time parameter(s): " + copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check we ourselves are not being called by unprivileged code
|
|
||||||
SecurityManager sm = System.getSecurityManager();
|
// Check we ourselves are not being called by unprivileged code.
|
||||||
|
final SecurityManager sm = System.getSecurityManager();
|
||||||
|
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
sm.checkPermission(new SpecialPermission());
|
sm.checkPermission(new SpecialPermission());
|
||||||
}
|
}
|
||||||
// create our loader (which loads compiled code with no permissions)
|
|
||||||
Compiler.Loader loader = AccessController.doPrivileged(new PrivilegedAction<Compiler.Loader>() {
|
// Create our loader (which loads compiled code with no permissions).
|
||||||
|
final Compiler.Loader loader = AccessController.doPrivileged(new PrivilegedAction<Compiler.Loader>() {
|
||||||
@Override
|
@Override
|
||||||
public Compiler.Loader run() {
|
public Compiler.Loader run() {
|
||||||
return new Compiler.Loader(getClass().getClassLoader());
|
return new Compiler.Loader(getClass().getClassLoader());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// drop all permissions to actually compile the code itself
|
|
||||||
|
// Drop all permissions to actually compile the code itself.
|
||||||
return AccessController.doPrivileged(new PrivilegedAction<Executable>() {
|
return AccessController.doPrivileged(new PrivilegedAction<Executable>() {
|
||||||
@Override
|
@Override
|
||||||
public Executable run() {
|
public Executable run() {
|
||||||
return Compiler.compile(loader, "something", script, definition, compilerSettings);
|
return Compiler.compile(loader, "unknown", script, definition, compilerSettings);
|
||||||
}
|
}
|
||||||
}, COMPILATION_CONTEXT);
|
}, COMPILATION_CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an {@link ExecutableScript} for later use.
|
||||||
|
* @param compiledScript A previously compiled script.
|
||||||
|
* @param vars The variables to be used in the script.
|
||||||
|
* @return An {@link ExecutableScript} with the currently specified variables.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ExecutableScript executable(CompiledScript compiledScript, Map<String,Object> vars) {
|
public ExecutableScript executable(final CompiledScript compiledScript, final Map<String, Object> vars) {
|
||||||
return new ScriptImpl((Executable) compiledScript.compiled(), vars, null);
|
return new ScriptImpl((Executable)compiledScript.compiled(), vars, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a {@link SearchScript} for later use.
|
||||||
|
* @param compiledScript A previously compiled script.
|
||||||
|
* @param lookup The object that ultimately allows access to search fields.
|
||||||
|
* @param vars The variables to be used in the script.
|
||||||
|
* @return An {@link SearchScript} with the currently specified variables.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, Map<String,Object> vars) {
|
public SearchScript search(final CompiledScript compiledScript, final SearchLookup lookup, final Map<String, Object> vars) {
|
||||||
return new SearchScript() {
|
return new SearchScript() {
|
||||||
|
/**
|
||||||
|
* Get the search script that will have access to search field values.
|
||||||
|
* @param context The LeafReaderContext to be used.
|
||||||
|
* @return A script that will have the search fields from the current context available for use.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException {
|
public LeafSearchScript getLeafSearchScript(final LeafReaderContext context) throws IOException {
|
||||||
return new ScriptImpl((Executable) compiledScript.compiled(), vars, lookup.getLeafSearchLookup(context));
|
return new ScriptImpl((Executable)compiledScript.compiled(), vars, lookup.getLeafSearchLookup(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the score is needed.
|
||||||
|
* @return Always true as it's assumed score is needed.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean needsScores() {
|
public boolean needsScores() {
|
||||||
return true; // TODO: maybe even do these different and more like expressions.
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action taken when a script is removed from the cache.
|
||||||
|
* @param script The removed script.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void scriptRemoved(CompiledScript script) {
|
public void scriptRemoved(final CompiledScript script) {
|
||||||
// nothing to do
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action taken when the engine is closed.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
// nothing to do
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,67 +28,128 @@ import org.elasticsearch.search.lookup.LeafSearchLookup;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScriptImpl can be used as either an {@link ExecutableScript} or a {@link LeafSearchScript}
|
||||||
|
* to run a previously compiled Plan A script.
|
||||||
|
*/
|
||||||
final class ScriptImpl implements ExecutableScript, LeafSearchScript {
|
final class ScriptImpl implements ExecutableScript, LeafSearchScript {
|
||||||
final Executable executable;
|
/**
|
||||||
final Map<String,Object> variables;
|
* The Plan A Executable script that can be run.
|
||||||
final LeafSearchLookup lookup;
|
*/
|
||||||
|
private final Executable executable;
|
||||||
ScriptImpl(Executable executable, Map<String,Object> vars, LeafSearchLookup lookup) {
|
|
||||||
|
/**
|
||||||
|
* A map that can be used to access input parameters at run-time.
|
||||||
|
*/
|
||||||
|
private final Map<String, Object> variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lookup is used to access search field values at run-time.
|
||||||
|
*/
|
||||||
|
private final LeafSearchLookup lookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ScriptImpl for the a previously compiled Plan A script.
|
||||||
|
* @param executable The previously compiled Plan A script.
|
||||||
|
* @param vars The initial variables to run the script with.
|
||||||
|
* @param lookup The lookup to allow search fields to be available if this is run as a search script.
|
||||||
|
*/
|
||||||
|
ScriptImpl(final Executable executable, final Map<String, Object> vars, final LeafSearchLookup lookup) {
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
this.variables = new HashMap<>();
|
this.variables = new HashMap<>();
|
||||||
|
|
||||||
if (vars != null) {
|
if (vars != null) {
|
||||||
variables.putAll(vars);
|
variables.putAll(vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lookup != null) {
|
if (lookup != null) {
|
||||||
variables.putAll(lookup.asMap());
|
variables.putAll(lookup.asMap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a variable for the script to be run against.
|
||||||
|
* @param name The variable name.
|
||||||
|
* @param value The variable value.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setNextVar(String name, Object value) {
|
public void setNextVar(final String name, final Object value) {
|
||||||
variables.put(name, value);
|
variables.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the script.
|
||||||
|
* @return The script result.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object run() {
|
public Object run() {
|
||||||
return executable.execute(variables);
|
return executable.execute(variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float runAsFloat() {
|
|
||||||
return ((Number) run()).floatValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long runAsLong() {
|
|
||||||
return ((Number) run()).longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the script.
|
||||||
|
* @return The script result as a double.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public double runAsDouble() {
|
public double runAsDouble() {
|
||||||
return ((Number) run()).doubleValue();
|
return ((Number)run()).doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the script.
|
||||||
|
* @return The script result as a float.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object unwrap(Object value) {
|
public float runAsFloat() {
|
||||||
|
return ((Number)run()).floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the script.
|
||||||
|
* @return The script result as a long.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long runAsLong() {
|
||||||
|
return ((Number)run()).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has no effect in Plan A.
|
||||||
|
* @param value The value to unwrap.
|
||||||
|
* @return The value passed in.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object unwrap(final Object value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scorer to be accessible within a script.
|
||||||
|
* @param scorer The scorer used for a search.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setScorer(Scorer scorer) {
|
public void setScorer(final Scorer scorer) {
|
||||||
variables.put("_score", new ScoreAccessor(scorer));
|
variables.put("#score", new ScoreAccessor(scorer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current document.
|
||||||
|
* @param doc The current document.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setDocument(int doc) {
|
public void setDocument(final int doc) {
|
||||||
if (lookup != null) {
|
if (lookup != null) {
|
||||||
lookup.setDocument(doc);
|
lookup.setDocument(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current source.
|
||||||
|
* @param source The current source.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setSource(Map<String,Object> source) {
|
public void setSource(final Map<String, Object> source) {
|
||||||
if (lookup != null) {
|
if (lookup != null) {
|
||||||
lookup.source().setSource(source);
|
lookup.source().setSource(source);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
package org.elasticsearch.plan.a;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
* license agreements. See the NOTICE file distributed with
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
@ -19,6 +17,8 @@ package org.elasticsearch.plan.a;
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
public class Utility {
|
public class Utility {
|
||||||
public static boolean NumberToboolean(final Number value) {
|
public static boolean NumberToboolean(final Number value) {
|
||||||
return value.longValue() != 0;
|
return value.longValue() != 0;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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.plan.a;
|
||||||
|
|
||||||
|
public class BasicAPITests extends ScriptTestCase {
|
||||||
|
|
||||||
|
public void testListIterator() {
|
||||||
|
assertEquals(3, exec("List x = new ArrayList(); x.add(2); x.add(3); x.add(-2); Iterator y = x.iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += y.next(); return total;"));
|
||||||
|
assertEquals(3, exec("List<Object> x = new ArrayList(); x.add(2); x.add(3); x.add(-2); Iterator<Object> y = x.iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += (int)y.next(); return total;"));
|
||||||
|
assertEquals("abc", exec("List<String> x = new ArrayList(); x.add(\"a\"); x.add(\"b\"); x.add(\"c\"); " +
|
||||||
|
"Iterator<String> y = x.iterator(); String total = \"\"; while (y.hasNext()) total += y.next(); return total;"));
|
||||||
|
assertEquals(3, exec("def x = new ArrayList(); x.add(2); x.add(3); x.add(-2); def y = x.iterator(); " +
|
||||||
|
"def total = 0; while (y.hasNext()) total += y.next(); return total;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetIterator() {
|
||||||
|
assertEquals(3, exec("Set x = new HashSet(); x.add(2); x.add(3); x.add(-2); Iterator y = x.iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += y.next(); return total;"));
|
||||||
|
assertEquals(3, exec("Set<Object> x = new HashSet(); x.add(2); x.add(3); x.add(-2); Iterator<Object> y = x.iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += (int)y.next(); return total;"));
|
||||||
|
assertEquals("abc", exec("Set<String> x = new HashSet(); x.add(\"a\"); x.add(\"b\"); x.add(\"c\"); " +
|
||||||
|
"Iterator<String> y = x.iterator(); String total = \"\"; while (y.hasNext()) total += y.next(); return total;"));
|
||||||
|
assertEquals(3, exec("def x = new HashSet(); x.add(2); x.add(3); x.add(-2); def y = x.iterator(); " +
|
||||||
|
"def total = 0; while (y.hasNext()) total += (int)y.next(); return total;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMapIterator() {
|
||||||
|
assertEquals(3, exec("Map x = new HashMap(); x.put(2, 2); x.put(3, 3); x.put(-2, -2); Iterator y = x.keySet().iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += (int)y.next(); return total;"));
|
||||||
|
assertEquals(3, exec("Map x = new HashMap(); x.put(2, 2); x.put(3, 3); x.put(-2, -2); Iterator y = x.values().iterator(); " +
|
||||||
|
"int total = 0; while (y.hasNext()) total += (int)y.next(); return total;"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,14 +24,14 @@ import java.util.Map;
|
||||||
|
|
||||||
/** Tests floating point overflow with numeric overflow disabled */
|
/** Tests floating point overflow with numeric overflow disabled */
|
||||||
public class FloatOverflowDisabledTests extends ScriptTestCase {
|
public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
|
|
||||||
/** wire overflow to false for all tests */
|
/** wire overflow to false for all tests */
|
||||||
@Override
|
@Override
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, "false"));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, "false"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentAdditionOverflow() {
|
public void testAssignmentAdditionOverflow() {
|
||||||
// float
|
// float
|
||||||
try {
|
try {
|
||||||
exec("float x = 3.4028234663852886E38f; x += 3.4028234663852886E38f; return x;");
|
exec("float x = 3.4028234663852886E38f; x += 3.4028234663852886E38f; return x;");
|
||||||
|
@ -41,7 +41,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = -3.4028234663852886E38f; x += -3.4028234663852886E38f; return x;");
|
exec("float x = -3.4028234663852886E38f; x += -3.4028234663852886E38f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double
|
// double
|
||||||
try {
|
try {
|
||||||
exec("double x = 1.7976931348623157E308; x += 1.7976931348623157E308; return x;");
|
exec("double x = 1.7976931348623157E308; x += 1.7976931348623157E308; return x;");
|
||||||
|
@ -52,8 +52,8 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentSubtractionOverflow() {
|
public void testAssignmentSubtractionOverflow() {
|
||||||
// float
|
// float
|
||||||
try {
|
try {
|
||||||
exec("float x = 3.4028234663852886E38f; x -= -3.4028234663852886E38f; return x;");
|
exec("float x = 3.4028234663852886E38f; x -= -3.4028234663852886E38f; return x;");
|
||||||
|
@ -63,7 +63,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = -3.4028234663852886E38f; x -= 3.4028234663852886E38f; return x;");
|
exec("float x = -3.4028234663852886E38f; x -= 3.4028234663852886E38f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double
|
// double
|
||||||
try {
|
try {
|
||||||
exec("double x = 1.7976931348623157E308; x -= -1.7976931348623157E308; return x;");
|
exec("double x = 1.7976931348623157E308; x -= -1.7976931348623157E308; return x;");
|
||||||
|
@ -74,7 +74,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentMultiplicationOverflow() {
|
public void testAssignmentMultiplicationOverflow() {
|
||||||
// float
|
// float
|
||||||
try {
|
try {
|
||||||
|
@ -85,7 +85,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = 3.4028234663852886E38f; x *= -3.4028234663852886E38f; return x;");
|
exec("float x = 3.4028234663852886E38f; x *= -3.4028234663852886E38f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double
|
// double
|
||||||
try {
|
try {
|
||||||
exec("double x = 1.7976931348623157E308; x *= 1.7976931348623157E308; return x;");
|
exec("double x = 1.7976931348623157E308; x *= 1.7976931348623157E308; return x;");
|
||||||
|
@ -96,7 +96,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentDivisionOverflow() {
|
public void testAssignmentDivisionOverflow() {
|
||||||
// float
|
// float
|
||||||
try {
|
try {
|
||||||
|
@ -111,7 +111,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = 1.0f; x /= 0.0f; return x;");
|
exec("float x = 1.0f; x /= 0.0f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double
|
// double
|
||||||
try {
|
try {
|
||||||
exec("double x = 1.7976931348623157E308; x /= 4.9E-324; return x;");
|
exec("double x = 1.7976931348623157E308; x /= 4.9E-324; return x;");
|
||||||
|
@ -137,7 +137,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAdditionConst() throws Exception {
|
public void testAdditionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return 3.4028234663852886E38f + 3.4028234663852886E38f;");
|
exec("return 3.4028234663852886E38f + 3.4028234663852886E38f;");
|
||||||
|
@ -148,7 +148,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtraction() throws Exception {
|
public void testSubtraction() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("float x = -3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x - y;");
|
exec("float x = -3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x - y;");
|
||||||
|
@ -159,7 +159,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtractionConst() throws Exception {
|
public void testSubtractionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return -3.4028234663852886E38f - 3.4028234663852886E38f;");
|
exec("return -3.4028234663852886E38f - 3.4028234663852886E38f;");
|
||||||
|
@ -170,7 +170,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplication() throws Exception {
|
public void testMultiplication() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x * y;");
|
exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x * y;");
|
||||||
|
@ -181,7 +181,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplicationConst() throws Exception {
|
public void testMultiplicationConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return 3.4028234663852886E38f * 3.4028234663852886E38f;");
|
exec("return 3.4028234663852886E38f * 3.4028234663852886E38f;");
|
||||||
|
@ -211,7 +211,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionConst() throws Exception {
|
public void testDivisionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return 3.4028234663852886E38f / 1.401298464324817E-45f;");
|
exec("return 3.4028234663852886E38f / 1.401298464324817E-45f;");
|
||||||
|
@ -230,7 +230,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionNaN() throws Exception {
|
public void testDivisionNaN() throws Exception {
|
||||||
// float division, constant division, and assignment
|
// float division, constant division, and assignment
|
||||||
try {
|
try {
|
||||||
|
@ -245,7 +245,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = 0f; x /= 0f; return x;");
|
exec("float x = 0f; x /= 0f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double division, constant division, and assignment
|
// double division, constant division, and assignment
|
||||||
try {
|
try {
|
||||||
exec("double x = 0.0; double y = 0.0; return x / y;");
|
exec("double x = 0.0; double y = 0.0; return x / y;");
|
||||||
|
@ -260,7 +260,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRemainderNaN() throws Exception {
|
public void testRemainderNaN() throws Exception {
|
||||||
// float division, constant division, and assignment
|
// float division, constant division, and assignment
|
||||||
try {
|
try {
|
||||||
|
@ -275,7 +275,7 @@ public class FloatOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("float x = 1f; x %= 0f; return x;");
|
exec("float x = 1f; x %= 0f; return x;");
|
||||||
fail("didn't hit expected exception");
|
fail("didn't hit expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// double division, constant division, and assignment
|
// double division, constant division, and assignment
|
||||||
try {
|
try {
|
||||||
exec("double x = 1.0; double y = 0.0; return x % y;");
|
exec("double x = 1.0; double y = 0.0; return x % y;");
|
||||||
|
|
|
@ -24,49 +24,49 @@ import java.util.Map;
|
||||||
|
|
||||||
/** Tests floating point overflow with numeric overflow enabled */
|
/** Tests floating point overflow with numeric overflow enabled */
|
||||||
public class FloatOverflowEnabledTests extends ScriptTestCase {
|
public class FloatOverflowEnabledTests extends ScriptTestCase {
|
||||||
|
|
||||||
/** wire overflow to true for all tests */
|
/** wire overflow to true for all tests */
|
||||||
@Override
|
@Override
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, "true"));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, "true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentAdditionOverflow() {
|
public void testAssignmentAdditionOverflow() {
|
||||||
// float
|
// float
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x += 3.4028234663852886E38f; return x;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x += 3.4028234663852886E38f; return x;"));
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; x += -3.4028234663852886E38f; return x;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; x += -3.4028234663852886E38f; return x;"));
|
||||||
|
|
||||||
// double
|
// double
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x += 1.7976931348623157E308; return x;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x += 1.7976931348623157E308; return x;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; x += -1.7976931348623157E308; return x;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; x += -1.7976931348623157E308; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentSubtractionOverflow() {
|
public void testAssignmentSubtractionOverflow() {
|
||||||
// float
|
// float
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x -= -3.4028234663852886E38f; return x;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x -= -3.4028234663852886E38f; return x;"));
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; x -= 3.4028234663852886E38f; return x;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; x -= 3.4028234663852886E38f; return x;"));
|
||||||
|
|
||||||
// double
|
// double
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x -= -1.7976931348623157E308; return x;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x -= -1.7976931348623157E308; return x;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; x -= 1.7976931348623157E308; return x;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; x -= 1.7976931348623157E308; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentMultiplicationOverflow() {
|
public void testAssignmentMultiplicationOverflow() {
|
||||||
// float
|
// float
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x *= 3.4028234663852886E38f; return x;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x *= 3.4028234663852886E38f; return x;"));
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x *= -3.4028234663852886E38f; return x;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x *= -3.4028234663852886E38f; return x;"));
|
||||||
|
|
||||||
// double
|
// double
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x *= 1.7976931348623157E308; return x;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x *= 1.7976931348623157E308; return x;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = 1.7976931348623157E308; x *= -1.7976931348623157E308; return x;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = 1.7976931348623157E308; x *= -1.7976931348623157E308; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentDivisionOverflow() {
|
public void testAssignmentDivisionOverflow() {
|
||||||
// float
|
// float
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x /= 1.401298464324817E-45f; return x;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x /= 1.401298464324817E-45f; return x;"));
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x /= -1.401298464324817E-45f; return x;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = 3.4028234663852886E38f; x /= -1.401298464324817E-45f; return x;"));
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 1.0f; x /= 0.0f; return x;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 1.0f; x /= 0.0f; return x;"));
|
||||||
|
|
||||||
// double
|
// double
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x /= 4.9E-324; return x;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; x /= 4.9E-324; return x;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = 1.7976931348623157E308; x /= -4.9E-324; return x;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = 1.7976931348623157E308; x /= -4.9E-324; return x;"));
|
||||||
|
@ -74,32 +74,32 @@ public class FloatOverflowEnabledTests extends ScriptTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddition() throws Exception {
|
public void testAddition() throws Exception {
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x + y;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x + y;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 1.7976931348623157E308; return x + y;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 1.7976931348623157E308; return x + y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAdditionConst() throws Exception {
|
public void testAdditionConst() throws Exception {
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f + 3.4028234663852886E38f;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f + 3.4028234663852886E38f;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 + 1.7976931348623157E308;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 + 1.7976931348623157E308;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtraction() throws Exception {
|
public void testSubtraction() throws Exception {
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x - y;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("float x = -3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x - y;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; double y = 1.7976931348623157E308; return x - y;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("double x = -1.7976931348623157E308; double y = 1.7976931348623157E308; return x - y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtractionConst() throws Exception {
|
public void testSubtractionConst() throws Exception {
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, exec("return -3.4028234663852886E38f - 3.4028234663852886E38f;"));
|
assertEquals(Float.NEGATIVE_INFINITY, exec("return -3.4028234663852886E38f - 3.4028234663852886E38f;"));
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, exec("return -1.7976931348623157E308 - 1.7976931348623157E308;"));
|
assertEquals(Double.NEGATIVE_INFINITY, exec("return -1.7976931348623157E308 - 1.7976931348623157E308;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplication() throws Exception {
|
public void testMultiplication() throws Exception {
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x * y;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("float x = 3.4028234663852886E38f; float y = 3.4028234663852886E38f; return x * y;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 1.7976931348623157E308; return x * y;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 1.7976931348623157E308; return x * y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplicationConst() throws Exception {
|
public void testMultiplicationConst() throws Exception {
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f * 3.4028234663852886E38f;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f * 3.4028234663852886E38f;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 * 1.7976931348623157E308;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 * 1.7976931348623157E308;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,32 +109,32 @@ public class FloatOverflowEnabledTests extends ScriptTestCase {
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 4.9E-324; return x / y;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.7976931348623157E308; double y = 4.9E-324; return x / y;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.0; double y = 0.0; return x / y;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("double x = 1.0; double y = 0.0; return x / y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionConst() throws Exception {
|
public void testDivisionConst() throws Exception {
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f / 1.401298464324817E-45f;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("return 3.4028234663852886E38f / 1.401298464324817E-45f;"));
|
||||||
assertEquals(Float.POSITIVE_INFINITY, exec("return 1.0f / 0.0f;"));
|
assertEquals(Float.POSITIVE_INFINITY, exec("return 1.0f / 0.0f;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 / 4.9E-324;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.7976931348623157E308 / 4.9E-324;"));
|
||||||
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.0 / 0.0;"));
|
assertEquals(Double.POSITIVE_INFINITY, exec("return 1.0 / 0.0;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionNaN() throws Exception {
|
public void testDivisionNaN() throws Exception {
|
||||||
// float division, constant division, and assignment
|
// float division, constant division, and assignment
|
||||||
assertTrue(Float.isNaN((Float) exec("float x = 0f; float y = 0f; return x / y;")));
|
assertTrue(Float.isNaN((Float) exec("float x = 0f; float y = 0f; return x / y;")));
|
||||||
assertTrue(Float.isNaN((Float) exec("return 0f / 0f;")));
|
assertTrue(Float.isNaN((Float) exec("return 0f / 0f;")));
|
||||||
assertTrue(Float.isNaN((Float) exec("float x = 0f; x /= 0f; return x;")));
|
assertTrue(Float.isNaN((Float) exec("float x = 0f; x /= 0f; return x;")));
|
||||||
|
|
||||||
// double division, constant division, and assignment
|
// double division, constant division, and assignment
|
||||||
assertTrue(Double.isNaN((Double) exec("double x = 0.0; double y = 0.0; return x / y;")));
|
assertTrue(Double.isNaN((Double) exec("double x = 0.0; double y = 0.0; return x / y;")));
|
||||||
assertTrue(Double.isNaN((Double) exec("return 0.0 / 0.0;")));
|
assertTrue(Double.isNaN((Double) exec("return 0.0 / 0.0;")));
|
||||||
assertTrue(Double.isNaN((Double) exec("double x = 0.0; x /= 0.0; return x;")));
|
assertTrue(Double.isNaN((Double) exec("double x = 0.0; x /= 0.0; return x;")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRemainderNaN() throws Exception {
|
public void testRemainderNaN() throws Exception {
|
||||||
// float division, constant division, and assignment
|
// float division, constant division, and assignment
|
||||||
assertTrue(Float.isNaN((Float) exec("float x = 1f; float y = 0f; return x % y;")));
|
assertTrue(Float.isNaN((Float) exec("float x = 1f; float y = 0f; return x % y;")));
|
||||||
assertTrue(Float.isNaN((Float) exec("return 1f % 0f;")));
|
assertTrue(Float.isNaN((Float) exec("return 1f % 0f;")));
|
||||||
assertTrue(Float.isNaN((Float) exec("float x = 1f; x %= 0f; return x;")));
|
assertTrue(Float.isNaN((Float) exec("float x = 1f; x %= 0f; return x;")));
|
||||||
|
|
||||||
// double division, constant division, and assignment
|
// double division, constant division, and assignment
|
||||||
assertTrue(Double.isNaN((Double) exec("double x = 1.0; double y = 0.0; return x % y;")));
|
assertTrue(Double.isNaN((Double) exec("double x = 1.0; double y = 0.0; return x % y;")));
|
||||||
assertTrue(Double.isNaN((Double) exec("return 1.0 % 0.0;")));
|
assertTrue(Double.isNaN((Double) exec("return 1.0 % 0.0;")));
|
||||||
|
|
|
@ -24,11 +24,11 @@ import java.util.Map;
|
||||||
|
|
||||||
/** Tests integer overflow with numeric overflow disabled */
|
/** Tests integer overflow with numeric overflow disabled */
|
||||||
public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
|
|
||||||
/** wire overflow to true for all tests */
|
/** wire overflow to true for all tests */
|
||||||
@Override
|
@Override
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, "false"));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, "false"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentAdditionOverflow() {
|
public void testAssignmentAdditionOverflow() {
|
||||||
|
@ -42,7 +42,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("byte x = 0; x += -129; return x;");
|
exec("byte x = 0; x += -129; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// short
|
// short
|
||||||
try {
|
try {
|
||||||
exec("short x = 0; x += 32768; return x;");
|
exec("short x = 0; x += 32768; return x;");
|
||||||
|
@ -53,7 +53,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("byte x = 0; x += -32769; return x;");
|
exec("byte x = 0; x += -32769; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// char
|
// char
|
||||||
try {
|
try {
|
||||||
exec("char x = 0; x += 65536; return x;");
|
exec("char x = 0; x += 65536; return x;");
|
||||||
|
@ -64,7 +64,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("char x = 0; x += -65536; return x;");
|
exec("char x = 0; x += -65536; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// int
|
// int
|
||||||
try {
|
try {
|
||||||
exec("int x = 1; x += 2147483647; return x;");
|
exec("int x = 1; x += 2147483647; return x;");
|
||||||
|
@ -75,7 +75,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("int x = -2; x += -2147483647; return x;");
|
exec("int x = -2; x += -2147483647; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// long
|
// long
|
||||||
try {
|
try {
|
||||||
exec("long x = 1; x += 9223372036854775807L; return x;");
|
exec("long x = 1; x += 9223372036854775807L; return x;");
|
||||||
|
@ -87,7 +87,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentSubtractionOverflow() {
|
public void testAssignmentSubtractionOverflow() {
|
||||||
// byte
|
// byte
|
||||||
try {
|
try {
|
||||||
|
@ -99,7 +99,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("byte x = 0; x -= 129; return x;");
|
exec("byte x = 0; x -= 129; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// short
|
// short
|
||||||
try {
|
try {
|
||||||
exec("short x = 0; x -= -32768; return x;");
|
exec("short x = 0; x -= -32768; return x;");
|
||||||
|
@ -110,7 +110,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("byte x = 0; x -= 32769; return x;");
|
exec("byte x = 0; x -= 32769; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// char
|
// char
|
||||||
try {
|
try {
|
||||||
exec("char x = 0; x -= -65536; return x;");
|
exec("char x = 0; x -= -65536; return x;");
|
||||||
|
@ -121,7 +121,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("char x = 0; x -= 65536; return x;");
|
exec("char x = 0; x -= 65536; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// int
|
// int
|
||||||
try {
|
try {
|
||||||
exec("int x = 1; x -= -2147483647; return x;");
|
exec("int x = 1; x -= -2147483647; return x;");
|
||||||
|
@ -132,7 +132,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("int x = -2; x -= 2147483647; return x;");
|
exec("int x = -2; x -= 2147483647; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// long
|
// long
|
||||||
try {
|
try {
|
||||||
exec("long x = 1; x -= -9223372036854775807L; return x;");
|
exec("long x = 1; x -= -9223372036854775807L; return x;");
|
||||||
|
@ -144,7 +144,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentMultiplicationOverflow() {
|
public void testAssignmentMultiplicationOverflow() {
|
||||||
// byte
|
// byte
|
||||||
try {
|
try {
|
||||||
|
@ -156,7 +156,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("byte x = 2; x *= -128; return x;");
|
exec("byte x = 2; x *= -128; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// char
|
// char
|
||||||
try {
|
try {
|
||||||
exec("char x = 2; x *= 65536; return x;");
|
exec("char x = 2; x *= 65536; return x;");
|
||||||
|
@ -167,7 +167,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("char x = 2; x *= -65536; return x;");
|
exec("char x = 2; x *= -65536; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// int
|
// int
|
||||||
try {
|
try {
|
||||||
exec("int x = 2; x *= 2147483647; return x;");
|
exec("int x = 2; x *= 2147483647; return x;");
|
||||||
|
@ -178,7 +178,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("int x = 2; x *= -2147483647; return x;");
|
exec("int x = 2; x *= -2147483647; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// long
|
// long
|
||||||
try {
|
try {
|
||||||
exec("long x = 2; x *= 9223372036854775807L; return x;");
|
exec("long x = 2; x *= 9223372036854775807L; return x;");
|
||||||
|
@ -190,7 +190,7 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentDivisionOverflow() {
|
public void testAssignmentDivisionOverflow() {
|
||||||
// byte
|
// byte
|
||||||
try {
|
try {
|
||||||
|
@ -203,108 +203,108 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("short x = (short) -32768; x /= -1; return x;");
|
exec("short x = (short) -32768; x /= -1; return x;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// cannot happen for char: unsigned
|
// cannot happen for char: unsigned
|
||||||
|
|
||||||
// int
|
// int
|
||||||
try {
|
try {
|
||||||
exec("int x = -2147483647 - 1; x /= -1; return x;");
|
exec("int x = -2147483647 - 1; x /= -1; return x;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// long
|
// long
|
||||||
try {
|
try {
|
||||||
exec("long x = -9223372036854775807L - 1L; x /=-1L; return x;");
|
exec("long x = -9223372036854775807L - 1L; x /=-1L; return x;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIncrementOverFlow() throws Exception {
|
public void testIncrementOverFlow() throws Exception {
|
||||||
// byte
|
// byte
|
||||||
try {
|
try {
|
||||||
exec("byte x = 127; ++x; return x;");
|
exec("byte x = 127; ++x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("byte x = 127; x++; return x;");
|
exec("byte x = 127; x++; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("byte x = (byte) -128; --x; return x;");
|
exec("byte x = (byte) -128; --x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("byte x = (byte) -128; x--; return x;");
|
exec("byte x = (byte) -128; x--; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// short
|
// short
|
||||||
try {
|
try {
|
||||||
exec("short x = 32767; ++x; return x;");
|
exec("short x = 32767; ++x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("short x = 32767; x++; return x;");
|
exec("short x = 32767; x++; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("short x = (short) -32768; --x; return x;");
|
exec("short x = (short) -32768; --x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("short x = (short) -32768; x--; return x;");
|
exec("short x = (short) -32768; x--; return x;");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// char
|
// char
|
||||||
try {
|
try {
|
||||||
exec("char x = 65535; ++x; return x;");
|
exec("char x = 65535; ++x; return x;");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("char x = 65535; x++; return x;");
|
exec("char x = 65535; x++; return x;");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("char x = (char) 0; --x; return x;");
|
exec("char x = (char) 0; --x; return x;");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("char x = (char) 0; x--; return x;");
|
exec("char x = (char) 0; x--; return x;");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// int
|
// int
|
||||||
try {
|
try {
|
||||||
exec("int x = 2147483647; ++x; return x;");
|
exec("int x = 2147483647; ++x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("int x = 2147483647; x++; return x;");
|
exec("int x = 2147483647; x++; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("int x = (int) -2147483648L; --x; return x;");
|
exec("int x = (int) -2147483648L; --x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("int x = (int) -2147483648L; x--; return x;");
|
exec("int x = (int) -2147483648L; x--; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
// long
|
// long
|
||||||
try {
|
try {
|
||||||
exec("long x = 9223372036854775807L; ++x; return x;");
|
exec("long x = 9223372036854775807L; ++x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = 9223372036854775807L; x++; return x;");
|
exec("long x = 9223372036854775807L; x++; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
|
@ -320,68 +320,68 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddition() throws Exception {
|
public void testAddition() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("int x = 2147483647; int y = 2147483647; return x + y;");
|
exec("int x = 2147483647; int y = 2147483647; return x + y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x + y;");
|
exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x + y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAdditionConst() throws Exception {
|
public void testAdditionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return 2147483647 + 2147483647;");
|
exec("return 2147483647 + 2147483647;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("return 9223372036854775807L + 9223372036854775807L;");
|
exec("return 9223372036854775807L + 9223372036854775807L;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testSubtraction() throws Exception {
|
public void testSubtraction() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("int x = -10; int y = 2147483647; return x - y;");
|
exec("int x = -10; int y = 2147483647; return x - y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = -10L; long y = 9223372036854775807L; return x - y;");
|
exec("long x = -10L; long y = 9223372036854775807L; return x - y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtractionConst() throws Exception {
|
public void testSubtractionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return -10 - 2147483647;");
|
exec("return -10 - 2147483647;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("return -10L - 9223372036854775807L;");
|
exec("return -10L - 9223372036854775807L;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplication() throws Exception {
|
public void testMultiplication() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("int x = 2147483647; int y = 2147483647; return x * y;");
|
exec("int x = 2147483647; int y = 2147483647; return x * y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x * y;");
|
exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x * y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplicationConst() throws Exception {
|
public void testMultiplicationConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return 2147483647 * 2147483647;");
|
exec("return 2147483647 * 2147483647;");
|
||||||
|
@ -399,13 +399,13 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
exec("int x = -2147483647 - 1; int y = -1; return x / y;");
|
exec("int x = -2147483647 - 1; int y = -1; return x / y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = -9223372036854775808L; long y = -1L; return x / y;");
|
exec("long x = -9223372036854775808L; long y = -1L; return x / y;");
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionConst() throws Exception {
|
public void testDivisionConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("return (-2147483648) / -1;");
|
exec("return (-2147483648) / -1;");
|
||||||
|
@ -417,25 +417,25 @@ public class IntegerOverflowDisabledTests extends ScriptTestCase {
|
||||||
fail("should have hit exception");
|
fail("should have hit exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNegationOverflow() throws Exception {
|
public void testNegationOverflow() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("int x = -2147483648; x = -x; return x;");
|
exec("int x = -2147483648; x = -x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = -9223372036854775808L; x = -x; return x;");
|
exec("long x = -9223372036854775808L; x = -x; return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNegationOverflowConst() throws Exception {
|
public void testNegationOverflowConst() throws Exception {
|
||||||
try {
|
try {
|
||||||
exec("int x = -(-2147483648); return x;");
|
exec("int x = -(-2147483648); return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
} catch (ArithmeticException expected) {}
|
} catch (ArithmeticException expected) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exec("long x = -(-9223372036854775808L); return x;");
|
exec("long x = -(-9223372036854775808L); return x;");
|
||||||
fail("did not get expected exception");
|
fail("did not get expected exception");
|
||||||
|
|
|
@ -24,148 +24,148 @@ import java.util.Map;
|
||||||
|
|
||||||
/** Tests integer overflow with numeric overflow enabled */
|
/** Tests integer overflow with numeric overflow enabled */
|
||||||
public class IntegerOverflowEnabledTests extends ScriptTestCase {
|
public class IntegerOverflowEnabledTests extends ScriptTestCase {
|
||||||
|
|
||||||
/** wire overflow to true for all tests */
|
/** wire overflow to true for all tests */
|
||||||
@Override
|
@Override
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, "true"));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, "true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentAdditionOverflow() {
|
public void testAssignmentAdditionOverflow() {
|
||||||
// byte
|
// byte
|
||||||
assertEquals((byte)(0 + 128), exec("byte x = 0; x += 128; return x;"));
|
assertEquals((byte)(0 + 128), exec("byte x = 0; x += 128; return x;"));
|
||||||
assertEquals((byte)(0 + -129), exec("byte x = 0; x += -129; return x;"));
|
assertEquals((byte)(0 + -129), exec("byte x = 0; x += -129; return x;"));
|
||||||
|
|
||||||
// short
|
// short
|
||||||
assertEquals((short)(0 + 32768), exec("short x = 0; x += 32768; return x;"));
|
assertEquals((short)(0 + 32768), exec("short x = 0; x += 32768; return x;"));
|
||||||
assertEquals((short)(0 + -32769), exec("short x = 0; x += -32769; return x;"));
|
assertEquals((short)(0 + -32769), exec("short x = 0; x += -32769; return x;"));
|
||||||
|
|
||||||
// char
|
// char
|
||||||
assertEquals((char)(0 + 65536), exec("char x = 0; x += 65536; return x;"));
|
assertEquals((char)(0 + 65536), exec("char x = 0; x += 65536; return x;"));
|
||||||
assertEquals((char)(0 + -65536), exec("char x = 0; x += -65536; return x;"));
|
assertEquals((char)(0 + -65536), exec("char x = 0; x += -65536; return x;"));
|
||||||
|
|
||||||
// int
|
// int
|
||||||
assertEquals(1 + 2147483647, exec("int x = 1; x += 2147483647; return x;"));
|
assertEquals(1 + 2147483647, exec("int x = 1; x += 2147483647; return x;"));
|
||||||
assertEquals(-2 + -2147483647, exec("int x = -2; x += -2147483647; return x;"));
|
assertEquals(-2 + -2147483647, exec("int x = -2; x += -2147483647; return x;"));
|
||||||
|
|
||||||
// long
|
// long
|
||||||
assertEquals(1L + 9223372036854775807L, exec("long x = 1; x += 9223372036854775807L; return x;"));
|
assertEquals(1L + 9223372036854775807L, exec("long x = 1; x += 9223372036854775807L; return x;"));
|
||||||
assertEquals(-2L + -9223372036854775807L, exec("long x = -2; x += -9223372036854775807L; return x;"));
|
assertEquals(-2L + -9223372036854775807L, exec("long x = -2; x += -9223372036854775807L; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentSubtractionOverflow() {
|
public void testAssignmentSubtractionOverflow() {
|
||||||
// byte
|
// byte
|
||||||
assertEquals((byte)(0 - -128), exec("byte x = 0; x -= -128; return x;"));
|
assertEquals((byte)(0 - -128), exec("byte x = 0; x -= -128; return x;"));
|
||||||
assertEquals((byte)(0 - 129), exec("byte x = 0; x -= 129; return x;"));
|
assertEquals((byte)(0 - 129), exec("byte x = 0; x -= 129; return x;"));
|
||||||
|
|
||||||
// short
|
// short
|
||||||
assertEquals((short)(0 - -32768), exec("short x = 0; x -= -32768; return x;"));
|
assertEquals((short)(0 - -32768), exec("short x = 0; x -= -32768; return x;"));
|
||||||
assertEquals((short)(0 - 32769), exec("short x = 0; x -= 32769; return x;"));
|
assertEquals((short)(0 - 32769), exec("short x = 0; x -= 32769; return x;"));
|
||||||
|
|
||||||
// char
|
// char
|
||||||
assertEquals((char)(0 - -65536), exec("char x = 0; x -= -65536; return x;"));
|
assertEquals((char)(0 - -65536), exec("char x = 0; x -= -65536; return x;"));
|
||||||
assertEquals((char)(0 - 65536), exec("char x = 0; x -= 65536; return x;"));
|
assertEquals((char)(0 - 65536), exec("char x = 0; x -= 65536; return x;"));
|
||||||
|
|
||||||
// int
|
// int
|
||||||
assertEquals(1 - -2147483647, exec("int x = 1; x -= -2147483647; return x;"));
|
assertEquals(1 - -2147483647, exec("int x = 1; x -= -2147483647; return x;"));
|
||||||
assertEquals(-2 - 2147483647, exec("int x = -2; x -= 2147483647; return x;"));
|
assertEquals(-2 - 2147483647, exec("int x = -2; x -= 2147483647; return x;"));
|
||||||
|
|
||||||
// long
|
// long
|
||||||
assertEquals(1L - -9223372036854775807L, exec("long x = 1; x -= -9223372036854775807L; return x;"));
|
assertEquals(1L - -9223372036854775807L, exec("long x = 1; x -= -9223372036854775807L; return x;"));
|
||||||
assertEquals(-2L - 9223372036854775807L, exec("long x = -2; x -= 9223372036854775807L; return x;"));
|
assertEquals(-2L - 9223372036854775807L, exec("long x = -2; x -= 9223372036854775807L; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentMultiplicationOverflow() {
|
public void testAssignmentMultiplicationOverflow() {
|
||||||
// byte
|
// byte
|
||||||
assertEquals((byte) (2 * 128), exec("byte x = 2; x *= 128; return x;"));
|
assertEquals((byte) (2 * 128), exec("byte x = 2; x *= 128; return x;"));
|
||||||
assertEquals((byte) (2 * -128), exec("byte x = 2; x *= -128; return x;"));
|
assertEquals((byte) (2 * -128), exec("byte x = 2; x *= -128; return x;"));
|
||||||
|
|
||||||
// char
|
// char
|
||||||
assertEquals((char) (2 * 65536), exec("char x = 2; x *= 65536; return x;"));
|
assertEquals((char) (2 * 65536), exec("char x = 2; x *= 65536; return x;"));
|
||||||
assertEquals((char) (2 * -65536), exec("char x = 2; x *= -65536; return x;"));
|
assertEquals((char) (2 * -65536), exec("char x = 2; x *= -65536; return x;"));
|
||||||
|
|
||||||
// int
|
// int
|
||||||
assertEquals(2 * 2147483647, exec("int x = 2; x *= 2147483647; return x;"));
|
assertEquals(2 * 2147483647, exec("int x = 2; x *= 2147483647; return x;"));
|
||||||
assertEquals(2 * -2147483647, exec("int x = 2; x *= -2147483647; return x;"));
|
assertEquals(2 * -2147483647, exec("int x = 2; x *= -2147483647; return x;"));
|
||||||
|
|
||||||
// long
|
// long
|
||||||
assertEquals(2L * 9223372036854775807L, exec("long x = 2; x *= 9223372036854775807L; return x;"));
|
assertEquals(2L * 9223372036854775807L, exec("long x = 2; x *= 9223372036854775807L; return x;"));
|
||||||
assertEquals(2L * -9223372036854775807L, exec("long x = 2; x *= -9223372036854775807L; return x;"));
|
assertEquals(2L * -9223372036854775807L, exec("long x = 2; x *= -9223372036854775807L; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAssignmentDivisionOverflow() {
|
public void testAssignmentDivisionOverflow() {
|
||||||
// byte
|
// byte
|
||||||
assertEquals((byte) (-128 / -1), exec("byte x = (byte) -128; x /= -1; return x;"));
|
assertEquals((byte) (-128 / -1), exec("byte x = (byte) -128; x /= -1; return x;"));
|
||||||
|
|
||||||
// short
|
// short
|
||||||
assertEquals((short) (-32768 / -1), exec("short x = (short) -32768; x /= -1; return x;"));
|
assertEquals((short) (-32768 / -1), exec("short x = (short) -32768; x /= -1; return x;"));
|
||||||
|
|
||||||
// cannot happen for char: unsigned
|
// cannot happen for char: unsigned
|
||||||
|
|
||||||
// int
|
// int
|
||||||
assertEquals((-2147483647 - 1) / -1, exec("int x = -2147483647 - 1; x /= -1; return x;"));
|
assertEquals((-2147483647 - 1) / -1, exec("int x = -2147483647 - 1; x /= -1; return x;"));
|
||||||
|
|
||||||
// long
|
// long
|
||||||
assertEquals((-9223372036854775807L - 1L) / -1L, exec("long x = -9223372036854775807L - 1L; x /=-1L; return x;"));
|
assertEquals((-9223372036854775807L - 1L) / -1L, exec("long x = -9223372036854775807L - 1L; x /=-1L; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIncrementOverFlow() throws Exception {
|
public void testIncrementOverFlow() throws Exception {
|
||||||
// byte
|
// byte
|
||||||
assertEquals((byte) 128, exec("byte x = 127; ++x; return x;"));
|
assertEquals((byte) 128, exec("byte x = 127; ++x; return x;"));
|
||||||
assertEquals((byte) 128, exec("byte x = 127; x++; return x;"));
|
assertEquals((byte) 128, exec("byte x = 127; x++; return x;"));
|
||||||
assertEquals((byte) -129, exec("byte x = (byte) -128; --x; return x;"));
|
assertEquals((byte) -129, exec("byte x = (byte) -128; --x; return x;"));
|
||||||
assertEquals((byte) -129, exec("byte x = (byte) -128; x--; return x;"));
|
assertEquals((byte) -129, exec("byte x = (byte) -128; x--; return x;"));
|
||||||
|
|
||||||
// short
|
// short
|
||||||
assertEquals((short) 32768, exec("short x = 32767; ++x; return x;"));
|
assertEquals((short) 32768, exec("short x = 32767; ++x; return x;"));
|
||||||
assertEquals((short) 32768, exec("short x = 32767; x++; return x;"));
|
assertEquals((short) 32768, exec("short x = 32767; x++; return x;"));
|
||||||
assertEquals((short) -32769, exec("short x = (short) -32768; --x; return x;"));
|
assertEquals((short) -32769, exec("short x = (short) -32768; --x; return x;"));
|
||||||
assertEquals((short) -32769, exec("short x = (short) -32768; x--; return x;"));
|
assertEquals((short) -32769, exec("short x = (short) -32768; x--; return x;"));
|
||||||
|
|
||||||
// char
|
// char
|
||||||
assertEquals((char) 65536, exec("char x = 65535; ++x; return x;"));
|
assertEquals((char) 65536, exec("char x = 65535; ++x; return x;"));
|
||||||
assertEquals((char) 65536, exec("char x = 65535; x++; return x;"));
|
assertEquals((char) 65536, exec("char x = 65535; x++; return x;"));
|
||||||
assertEquals((char) -1, exec("char x = (char) 0; --x; return x;"));
|
assertEquals((char) -1, exec("char x = (char) 0; --x; return x;"));
|
||||||
assertEquals((char) -1, exec("char x = (char) 0; x--; return x;"));
|
assertEquals((char) -1, exec("char x = (char) 0; x--; return x;"));
|
||||||
|
|
||||||
// int
|
// int
|
||||||
assertEquals(2147483647 + 1, exec("int x = 2147483647; ++x; return x;"));
|
assertEquals(2147483647 + 1, exec("int x = 2147483647; ++x; return x;"));
|
||||||
assertEquals(2147483647 + 1, exec("int x = 2147483647; x++; return x;"));
|
assertEquals(2147483647 + 1, exec("int x = 2147483647; x++; return x;"));
|
||||||
assertEquals(-2147483648 - 1, exec("int x = (int) -2147483648L; --x; return x;"));
|
assertEquals(-2147483648 - 1, exec("int x = (int) -2147483648L; --x; return x;"));
|
||||||
assertEquals(-2147483648 - 1, exec("int x = (int) -2147483648L; x--; return x;"));
|
assertEquals(-2147483648 - 1, exec("int x = (int) -2147483648L; x--; return x;"));
|
||||||
|
|
||||||
// long
|
// long
|
||||||
assertEquals(9223372036854775807L + 1L, exec("long x = 9223372036854775807L; ++x; return x;"));
|
assertEquals(9223372036854775807L + 1L, exec("long x = 9223372036854775807L; ++x; return x;"));
|
||||||
assertEquals(9223372036854775807L + 1L, exec("long x = 9223372036854775807L; x++; return x;"));
|
assertEquals(9223372036854775807L + 1L, exec("long x = 9223372036854775807L; x++; return x;"));
|
||||||
assertEquals(-9223372036854775807L - 1L - 1L, exec("long x = -9223372036854775807L - 1L; --x; return x;"));
|
assertEquals(-9223372036854775807L - 1L - 1L, exec("long x = -9223372036854775807L - 1L; --x; return x;"));
|
||||||
assertEquals(-9223372036854775807L - 1L - 1L, exec("long x = -9223372036854775807L - 1L; x--; return x;"));
|
assertEquals(-9223372036854775807L - 1L - 1L, exec("long x = -9223372036854775807L - 1L; x--; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddition() throws Exception {
|
public void testAddition() throws Exception {
|
||||||
assertEquals(2147483647 + 2147483647, exec("int x = 2147483647; int y = 2147483647; return x + y;"));
|
assertEquals(2147483647 + 2147483647, exec("int x = 2147483647; int y = 2147483647; return x + y;"));
|
||||||
assertEquals(9223372036854775807L + 9223372036854775807L, exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x + y;"));
|
assertEquals(9223372036854775807L + 9223372036854775807L, exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x + y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAdditionConst() throws Exception {
|
public void testAdditionConst() throws Exception {
|
||||||
assertEquals(2147483647 + 2147483647, exec("return 2147483647 + 2147483647;"));
|
assertEquals(2147483647 + 2147483647, exec("return 2147483647 + 2147483647;"));
|
||||||
assertEquals(9223372036854775807L + 9223372036854775807L, exec("return 9223372036854775807L + 9223372036854775807L;"));
|
assertEquals(9223372036854775807L + 9223372036854775807L, exec("return 9223372036854775807L + 9223372036854775807L;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtraction() throws Exception {
|
public void testSubtraction() throws Exception {
|
||||||
assertEquals(-10 - 2147483647, exec("int x = -10; int y = 2147483647; return x - y;"));
|
assertEquals(-10 - 2147483647, exec("int x = -10; int y = 2147483647; return x - y;"));
|
||||||
assertEquals(-10L - 9223372036854775807L, exec("long x = -10L; long y = 9223372036854775807L; return x - y;"));
|
assertEquals(-10L - 9223372036854775807L, exec("long x = -10L; long y = 9223372036854775807L; return x - y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubtractionConst() throws Exception {
|
public void testSubtractionConst() throws Exception {
|
||||||
assertEquals(-10 - 2147483647, exec("return -10 - 2147483647;"));
|
assertEquals(-10 - 2147483647, exec("return -10 - 2147483647;"));
|
||||||
assertEquals(-10L - 9223372036854775807L, exec("return -10L - 9223372036854775807L;"));
|
assertEquals(-10L - 9223372036854775807L, exec("return -10L - 9223372036854775807L;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplication() throws Exception {
|
public void testMultiplication() throws Exception {
|
||||||
assertEquals(2147483647 * 2147483647, exec("int x = 2147483647; int y = 2147483647; return x * y;"));
|
assertEquals(2147483647 * 2147483647, exec("int x = 2147483647; int y = 2147483647; return x * y;"));
|
||||||
assertEquals(9223372036854775807L * 9223372036854775807L, exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x * y;"));
|
assertEquals(9223372036854775807L * 9223372036854775807L, exec("long x = 9223372036854775807L; long y = 9223372036854775807L; return x * y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiplicationConst() throws Exception {
|
public void testMultiplicationConst() throws Exception {
|
||||||
assertEquals(2147483647 * 2147483647, exec("return 2147483647 * 2147483647;"));
|
assertEquals(2147483647 * 2147483647, exec("return 2147483647 * 2147483647;"));
|
||||||
assertEquals(9223372036854775807L * 9223372036854775807L, exec("return 9223372036854775807L * 9223372036854775807L;"));
|
assertEquals(9223372036854775807L * 9223372036854775807L, exec("return 9223372036854775807L * 9223372036854775807L;"));
|
||||||
|
@ -175,17 +175,17 @@ public class IntegerOverflowEnabledTests extends ScriptTestCase {
|
||||||
assertEquals((-2147483647 - 1) / -1, exec("int x = -2147483648; int y = -1; return x / y;"));
|
assertEquals((-2147483647 - 1) / -1, exec("int x = -2147483648; int y = -1; return x / y;"));
|
||||||
assertEquals((-9223372036854775807L - 1L) / -1L, exec("long x = -9223372036854775808L; long y = -1L; return x / y;"));
|
assertEquals((-9223372036854775807L - 1L) / -1L, exec("long x = -9223372036854775808L; long y = -1L; return x / y;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDivisionConst() throws Exception {
|
public void testDivisionConst() throws Exception {
|
||||||
assertEquals((-2147483647 - 1) / -1, exec("return (-2147483648) / -1;"));
|
assertEquals((-2147483647 - 1) / -1, exec("return (-2147483648) / -1;"));
|
||||||
assertEquals((-9223372036854775807L - 1L) / -1L, exec("return (-9223372036854775808L) / -1L;"));
|
assertEquals((-9223372036854775807L - 1L) / -1L, exec("return (-9223372036854775808L) / -1L;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNegationOverflow() throws Exception {
|
public void testNegationOverflow() throws Exception {
|
||||||
assertEquals(-(-2147483647 - 1), exec("int x = -2147483648; x = -x; return x;"));
|
assertEquals(-(-2147483647 - 1), exec("int x = -2147483648; x = -x; return x;"));
|
||||||
assertEquals(-(-9223372036854775807L - 1L), exec("long x = -9223372036854775808L; x = -x; return x;"));
|
assertEquals(-(-9223372036854775807L - 1L), exec("long x = -9223372036854775808L; x = -x; return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNegationOverflowConst() throws Exception {
|
public void testNegationOverflowConst() throws Exception {
|
||||||
assertEquals(-(-2147483647 - 1), exec("int x = -(-2147483648); return x;"));
|
assertEquals(-(-2147483647 - 1), exec("int x = -(-2147483648); return x;"));
|
||||||
assertEquals(-(-9223372036854775807L - 1L), exec("long x = -(-9223372036854775808L); return x;"));
|
assertEquals(-(-9223372036854775807L - 1L), exec("long x = -(-9223372036854775808L); return x;"));
|
||||||
|
|
|
@ -48,7 +48,7 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||||
|
|
||||||
/** Compiles and returns the result of {@code script} with access to {@code vars} */
|
/** Compiles and returns the result of {@code script} with access to {@code vars} */
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, Boolean.toString(random().nextBoolean())));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, Boolean.toString(random().nextBoolean())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
|
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.plan.a;
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public class WhenThingsGoWrongTests extends ScriptTestCase {
|
public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
|
@ -40,7 +41,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
fail("should have hit cce");
|
fail("should have hit cce");
|
||||||
} catch (ClassCastException expected) {}
|
} catch (ClassCastException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBogusParameter() {
|
public void testBogusParameter() {
|
||||||
try {
|
try {
|
||||||
exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"));
|
exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"));
|
||||||
|
@ -49,4 +50,83 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter"));
|
assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInfiniteLoops() {
|
||||||
|
try {
|
||||||
|
exec("boolean x = true; while (x) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) {int y = 5}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) { boolean x = true; while (x) {} }");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) { boolean x = false; while (x) {} }");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("boolean x = true; for (;x;) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("for (;;) {int x = 5}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("def x = true; do {int y = 5;} while (x)");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("try { int x } catch (PlanAError error) {}");
|
||||||
|
fail("should have hit ParseException");
|
||||||
|
} catch (RuntimeException expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"unexpected token ['PlanAError'] was expecting one of [TYPE]."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoopLimits() {
|
||||||
|
exec("for (int x = 0; x < 9999; ++x) {}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("for (int x = 0; x < 10000; ++x) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue