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:
Jack Conradson 2016-01-19 08:37:22 -08:00
parent da699ceae2
commit 2249a640bb
25 changed files with 3317 additions and 1985 deletions

View File

@ -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

View File

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

View File

@ -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);
// try {
// FileOutputStream f = new FileOutputStream(new File("<path>"), false);
// f.write(bytes); // f.write(bytes);
// f.close(); // f.close();
//} catch (Exception e) { // } catch (Exception e) {
// throw new RuntimeException(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() {}
} }

View File

@ -20,12 +20,29 @@
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;
}
} }

View File

@ -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.");

View File

@ -24,10 +24,14 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
class Definition { class Definition {
enum Sort { enum Sort {
@ -351,16 +355,32 @@ class Definition {
final Type utilityType; final Type utilityType;
final Type defobjType; final Type defobjType;
final Type itrType;
final Type oitrType;
final Type sitrType;
final Type collectionType;
final Type ocollectionType;
final Type scollectionType;
final Type listType; final Type listType;
final Type arraylistType; final Type arraylistType;
final Type mapType;
final Type hashmapType;
final Type olistType; final Type olistType;
final Type oarraylistType; final Type oarraylistType;
final Type omapType; final Type slistType;
final Type ohashmapType; final Type sarraylistType;
final Type setType;
final Type hashsetType;
final Type osetType;
final Type ohashsetType;
final Type ssetType;
final Type shashsetType;
final Type mapType;
final Type hashmapType;
final Type oomapType;
final Type oohashmapType;
final Type smapType; final Type smapType;
final Type shashmapType; final Type shashmapType;
final Type somapType; final Type somapType;
@ -368,6 +388,12 @@ class Definition {
final Type execType; final Type execType;
final Type exceptionType;
final Type arithexcepType;
final Type iargexcepType;
final Type istateexceptType;
final Type nfexcepType;
public Definition() { public Definition() {
structs = new HashMap<>(); structs = new HashMap<>();
classes = new HashMap<>(); classes = new HashMap<>();
@ -406,16 +432,32 @@ class Definition {
utilityType = getType("Utility"); utilityType = getType("Utility");
defobjType = getType("Def"); defobjType = getType("Def");
itrType = getType("Iterator");
oitrType = getType("Iterator<Object>");
sitrType = getType("Iterator<String>");
collectionType = getType("Collection");
ocollectionType = getType("Collection<Object>");
scollectionType = getType("Collection<String>");
listType = getType("List"); listType = getType("List");
arraylistType = getType("ArrayList"); arraylistType = getType("ArrayList");
mapType = getType("Map");
hashmapType = getType("HashMap");
olistType = getType("List<Object>"); olistType = getType("List<Object>");
oarraylistType = getType("ArrayList<Object>"); oarraylistType = getType("ArrayList<Object>");
omapType = getType("Map<Object,Object>"); slistType = getType("List<String>");
ohashmapType = getType("HashMap<Object,Object>"); sarraylistType = getType("ArrayList<String>");
setType = getType("Set");
hashsetType = getType("HashSet");
osetType = getType("Set<Object>");
ohashsetType = getType("HashSet<Object>");
ssetType = getType("Set<String>");
shashsetType = getType("HashSet<String>");
mapType = getType("Map");
hashmapType = getType("HashMap");
oomapType = getType("Map<Object,Object>");
oohashmapType = getType("HashMap<Object,Object>");
smapType = getType("Map<String,def>"); smapType = getType("Map<String,def>");
shashmapType = getType("HashMap<String,def>"); shashmapType = getType("HashMap<String,def>");
somapType = getType("Map<String,Object>"); somapType = getType("Map<String,Object>");
@ -423,6 +465,12 @@ class Definition {
execType = getType("Executable"); execType = getType("Executable");
exceptionType = getType("Exception");
arithexcepType = getType("ArithmeticException");
iargexcepType = getType("IllegalArgumentException");
istateexceptType = getType("IllegalStateException");
nfexcepType = getType("NumberFormatException");
addDefaultElements(); addDefaultElements();
copyDefaultStructs(); copyDefaultStructs();
addDefaultTransforms(); addDefaultTransforms();
@ -478,22 +526,44 @@ class Definition {
utilityType = definition.utilityType; utilityType = definition.utilityType;
defobjType = definition.defobjType; defobjType = definition.defobjType;
itrType = definition.itrType;
oitrType = definition.oitrType;
sitrType = definition.sitrType;
collectionType = definition.collectionType;
ocollectionType = definition.ocollectionType;
scollectionType = definition.scollectionType;
listType = definition.listType; listType = definition.listType;
arraylistType = definition.arraylistType; arraylistType = definition.arraylistType;
mapType = definition.mapType;
hashmapType = definition.hashmapType;
olistType = definition.olistType; olistType = definition.olistType;
oarraylistType = definition.oarraylistType; oarraylistType = definition.oarraylistType;
omapType = definition.omapType; slistType = definition.slistType;
ohashmapType = definition.ohashmapType; sarraylistType = definition.sarraylistType;
setType = definition.setType;
hashsetType = definition.hashsetType;
osetType = definition.osetType;
ohashsetType = definition.ohashsetType;
ssetType = definition.ssetType;
shashsetType = definition.shashsetType;
mapType = definition.mapType;
hashmapType = definition.hashmapType;
oomapType = definition.oomapType;
oohashmapType = definition.oohashmapType;
smapType = definition.smapType; smapType = definition.smapType;
shashmapType = definition.shashmapType; shashmapType = definition.shashmapType;
somapType = definition.somapType; somapType = definition.somapType;
sohashmapType = definition.sohashmapType; sohashmapType = definition.sohashmapType;
execType = definition.execType; execType = definition.execType;
exceptionType = definition.exceptionType;
arithexcepType = definition.arithexcepType;
iargexcepType = definition.iargexcepType;
istateexceptType = definition.istateexceptType;
nfexcepType = definition.nfexcepType;
} }
private void addDefaultStructs() { private void addDefaultStructs() {
@ -526,22 +596,44 @@ class Definition {
addStruct( "Utility" , Utility.class ); addStruct( "Utility" , Utility.class );
addStruct( "Def" , Def.class ); addStruct( "Def" , Def.class );
addStruct( "Iterator" , Iterator.class );
addStruct( "Iterator<Object>" , Iterator.class );
addStruct( "Iterator<String>" , Iterator.class );
addStruct( "Collection" , Collection.class );
addStruct( "Collection<Object>" , Collection.class );
addStruct( "Collection<String>" , Collection.class );
addStruct( "List" , List.class ); addStruct( "List" , List.class );
addStruct( "ArrayList" , ArrayList.class ); addStruct( "ArrayList" , ArrayList.class );
addStruct( "Map" , Map.class );
addStruct( "HashMap" , HashMap.class );
addStruct( "List<Object>" , List.class ); addStruct( "List<Object>" , List.class );
addStruct( "ArrayList<Object>" , ArrayList.class ); addStruct( "ArrayList<Object>" , ArrayList.class );
addStruct( "List<String>" , List.class );
addStruct( "ArrayList<String>" , ArrayList.class );
addStruct( "Set" , Set.class );
addStruct( "HashSet" , HashSet.class );
addStruct( "Set<Object>" , Set.class );
addStruct( "HashSet<Object>" , HashSet.class );
addStruct( "Set<String>" , Set.class );
addStruct( "HashSet<String>" , HashSet.class );
addStruct( "Map" , Map.class );
addStruct( "HashMap" , HashMap.class );
addStruct( "Map<Object,Object>" , Map.class ); addStruct( "Map<Object,Object>" , Map.class );
addStruct( "HashMap<Object,Object>" , HashMap.class ); addStruct( "HashMap<Object,Object>" , HashMap.class );
addStruct( "Map<String,def>" , Map.class ); addStruct( "Map<String,def>" , Map.class );
addStruct( "HashMap<String,def>" , HashMap.class ); addStruct( "HashMap<String,def>" , HashMap.class );
addStruct( "Map<String,Object>" , Map.class ); addStruct( "Map<String,Object>" , Map.class );
addStruct( "HashMap<String,Object>" , HashMap.class ); addStruct( "HashMap<String,Object>" , HashMap.class );
addStruct( "Executable" , Executable.class ); addStruct( "Executable" , Executable.class );
addStruct( "Exception" , Exception.class);
addStruct( "ArithmeticException" , ArithmeticException.class);
addStruct( "IllegalArgumentException" , IllegalArgumentException.class);
addStruct( "IllegalStateException" , IllegalStateException.class);
addStruct( "NumberFormatException" , NumberFormatException.class);
} }
private void addDefaultClasses() { private void addDefaultClasses() {
@ -568,10 +660,16 @@ class Definition {
addClass("CharSequence"); addClass("CharSequence");
addClass("String"); addClass("String");
addClass("Iterator");
addClass("Collection");
addClass("List"); addClass("List");
addClass("ArrayList"); addClass("ArrayList");
addClass("Set");
addClass("HashSet");
addClass("Map"); addClass("Map");
addClass("HashMap"); addClass("HashMap");
addClass("Exception");
} }
private void addDefaultElements() { private void addDefaultElements() {
@ -587,45 +685,39 @@ class Definition {
addMethod("Boolean", "valueOf", null, true, booleanobjType, new Type[] {booleanType}, null, null); addMethod("Boolean", "valueOf", null, true, booleanobjType, new Type[] {booleanType}, null, null);
addMethod("Boolean", "booleanValue", null, false, booleanType, new Type[] {}, null, null); addMethod("Boolean", "booleanValue", null, false, booleanType, new Type[] {}, null, null);
addConstructor("Byte", "new", new Type[]{byteType}, null); addConstructor("Byte", "new", new Type[] {byteType}, null);
addMethod("Byte", "valueOf", null, true, byteobjType, new Type[] {byteType}, null, null); addMethod("Byte", "valueOf", null, true, byteobjType, new Type[] {byteType}, null, null);
addMethod("Byte", "byteValue", null, false, byteType, new Type[] {}, null, null);
addField("Byte", "MIN_VALUE", null, true, byteType, null); addField("Byte", "MIN_VALUE", null, true, byteType, null);
addField("Byte", "MAX_VALUE", null, true, byteType, null); addField("Byte", "MAX_VALUE", null, true, byteType, null);
addConstructor("Short", "new", new Type[]{shortType}, null); addConstructor("Short", "new", new Type[] {shortType}, null);
addMethod("Short", "valueOf", null, true, shortobjType, new Type[] {shortType}, null, null); addMethod("Short", "valueOf", null, true, shortobjType, new Type[] {shortType}, null, null);
addMethod("Short", "shortValue", null, false, shortType, new Type[] {}, null, null);
addField("Short", "MIN_VALUE", null, true, shortType, null); addField("Short", "MIN_VALUE", null, true, shortType, null);
addField("Short", "MAX_VALUE", null, true, shortType, null); addField("Short", "MAX_VALUE", null, true, shortType, null);
addConstructor("Character", "new", new Type[]{charType}, null); addConstructor("Character", "new", new Type[] {charType}, null);
addMethod("Character", "valueOf", null, true, charobjType, new Type[] {charType}, null, null); addMethod("Character", "valueOf", null, true, charobjType, new Type[] {charType}, null, null);
addMethod("Character", "charValue", null, false, charType, new Type[] {}, null, null); addMethod("Character", "charValue", null, false, charType, new Type[] {}, null, null);
addField("Character", "MIN_VALUE", null, true, charType, null); addField("Character", "MIN_VALUE", null, true, charType, null);
addField("Character", "MAX_VALUE", null, true, charType, null); addField("Character", "MAX_VALUE", null, true, charType, null);
addConstructor("Integer", "new", new Type[]{intType}, null); addConstructor("Integer", "new", new Type[] {intType}, null);
addMethod("Integer", "valueOf", null, true, intobjType, new Type[] {intType}, null, null); addMethod("Integer", "valueOf", null, true, intobjType, new Type[] {intType}, null, null);
addMethod("Integer", "intValue", null, false, intType, new Type[] {}, null, null);
addField("Integer", "MIN_VALUE", null, true, intType, null); addField("Integer", "MIN_VALUE", null, true, intType, null);
addField("Integer", "MAX_VALUE", null, true, intType, null); addField("Integer", "MAX_VALUE", null, true, intType, null);
addConstructor("Long", "new", new Type[]{longType}, null); addConstructor("Long", "new", new Type[] {longType}, null);
addMethod("Long", "valueOf", null, true, longobjType, new Type[] {longType}, null, null); addMethod("Long", "valueOf", null, true, longobjType, new Type[] {longType}, null, null);
addMethod("Long", "longValue", null, false, longType, new Type[] {}, null, null);
addField("Long", "MIN_VALUE", null, true, longType, null); addField("Long", "MIN_VALUE", null, true, longType, null);
addField("Long", "MAX_VALUE", null, true, longType, null); addField("Long", "MAX_VALUE", null, true, longType, null);
addConstructor("Float", "new", new Type[]{floatType}, null); addConstructor("Float", "new", new Type[] {floatType}, null);
addMethod("Float", "valueOf", null, true, floatobjType, new Type[] {floatType}, null, null); addMethod("Float", "valueOf", null, true, floatobjType, new Type[] {floatType}, null, null);
addMethod("Float", "floatValue", null, false, floatType, new Type[] {}, null, null);
addField("Float", "MIN_VALUE", null, true, floatType, null); addField("Float", "MIN_VALUE", null, true, floatType, null);
addField("Float", "MAX_VALUE", null, true, floatType, null); addField("Float", "MAX_VALUE", null, true, floatType, null);
addConstructor("Double", "new", new Type[]{doubleType}, null); addConstructor("Double", "new", new Type[] {doubleType}, null);
addMethod("Double", "valueOf", null, true, doubleobjType, new Type[] {doubleType}, null, null); addMethod("Double", "valueOf", null, true, doubleobjType, new Type[] {doubleType}, null, null);
addMethod("Double", "doubleValue", null, false, doubleType, new Type[] {}, null, null);
addField("Double", "MIN_VALUE", null, true, doubleType, null); addField("Double", "MIN_VALUE", null, true, doubleType, null);
addField("Double", "MAX_VALUE", null, true, doubleType, null); addField("Double", "MAX_VALUE", null, true, doubleType, null);
@ -760,7 +852,44 @@ class Definition {
addMethod("Utility", "DoubleToboolean", null, true, booleanType, new Type[] {doubleobjType}, null, null); addMethod("Utility", "DoubleToboolean", null, true, booleanType, new Type[] {doubleobjType}, null, null);
addMethod("Utility", "DoubleTochar", null, true, charType, new Type[] {doubleobjType}, null, null); addMethod("Utility", "DoubleTochar", null, true, charType, new Type[] {doubleobjType}, null, null);
addMethod("Math", "dmax", "max", true, doubleType, new Type[] {doubleType, doubleType}, null, null); addMethod("Math", "abs", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "fabs", "abs", true, floatType, new Type[] {floatType}, null, null);
addMethod("Math", "labs", "abs", true, longType, new Type[] {longType}, null, null);
addMethod("Math", "iabs", "abs", true, intType, new Type[] {intType}, null, null);
addMethod("Math", "acos", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "asin", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "atan", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "atan2", null, true, doubleType, new Type[] {doubleType, doubleType}, null, null);
addMethod("Math", "cbrt", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "ceil", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "cos", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "cosh", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "exp", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "expm1", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "floor", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "hypot", null, true, doubleType, new Type[] {doubleType, doubleType}, null, null);
addMethod("Math", "log", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "log10", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "log1p", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "max", "max", true, doubleType, new Type[] {doubleType, doubleType}, null, null);
addMethod("Math", "fmax", "max", true, floatType, new Type[] {floatType, floatType}, null, null);
addMethod("Math", "lmax", "max", true, longType, new Type[] {longType, longType}, null, null);
addMethod("Math", "imax", "max", true, intType, new Type[] {intType, intType}, null, null);
addMethod("Math", "min", "min", true, doubleType, new Type[] {doubleType, doubleType}, null, null);
addMethod("Math", "fmin", "min", true, floatType, new Type[] {floatType, floatType}, null, null);
addMethod("Math", "lmin", "min", true, longType, new Type[] {longType, longType}, null, null);
addMethod("Math", "imin", "min", true, intType, new Type[] {intType, intType}, null, null);
addMethod("Math", "pow", null, true, doubleType, new Type[] {doubleType, doubleType}, null, null);
addMethod("Math", "random", null, true, doubleType, new Type[] {}, null, null);
addMethod("Math", "rint", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "round", null, true, longType, new Type[] {doubleType}, null, null);
addMethod("Math", "sin", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "sinh", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "sqrt", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "tan", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "tanh", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "toDegrees", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Math", "toRadians", null, true, doubleType, new Type[] {doubleType}, null, null);
addMethod("Def", "DefToboolean", null, true, booleanType, new Type[] {defType}, null, null); addMethod("Def", "DefToboolean", null, true, booleanType, new Type[] {defType}, null, null);
addMethod("Def", "DefTobyte", null, true, byteType, new Type[] {defType}, null, null); addMethod("Def", "DefTobyte", null, true, byteType, new Type[] {defType}, null, null);
@ -779,55 +908,123 @@ class Definition {
addMethod("Def", "DefToFloat", null, true, floatobjType, new Type[] {defType}, null, null); addMethod("Def", "DefToFloat", null, true, floatobjType, new Type[] {defType}, null, null);
addMethod("Def", "DefToDouble", null, true, doubleobjType, new Type[] {defType}, null, null); addMethod("Def", "DefToDouble", null, true, doubleobjType, new Type[] {defType}, null, null);
addMethod("List", "addLast", "add", false, booleanType, new Type[] {objectType}, null, new Type[] {defType}); addMethod("Iterator", "hasNext", null, false, booleanType, new Type[] {}, null, null);
addMethod("List", "add", null, false, voidType, new Type[] {intType, objectType}, null, new Type[] {intType, defType}); addMethod("Iterator", "next", null, false, objectType, new Type[] {}, defType, null);
addMethod("Iterator", "remove", null, false, voidType, new Type[] {}, null, null);
addMethod("Iterator<Object>", "hasNext", null, false, booleanType, new Type[] {}, null, null);
addMethod("Iterator<Object>", "next", null, false, objectType, new Type[] {}, null, null);
addMethod("Iterator<Object>", "remove", null, false, voidType, new Type[] {}, null, null);
addMethod("Iterator<String>", "hasNext", null, false, booleanType, new Type[] {}, null, null);
addMethod("Iterator<String>", "next", null, false, objectType, new Type[] {}, stringType, null);
addMethod("Iterator<String>", "remove", null, false, voidType, new Type[] {}, null, null);
addMethod("Collection", "add", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Collection", "clear", null, false, voidType, new Type[] {}, null, null);
addMethod("Collection", "contains", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Collection", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Collection", "iterator", null, false, itrType, new Type[] {}, null, null);
addMethod("Collection", "remove", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Collection", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Collection<Object>", "add", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Collection<Object>", "clear", null, false, voidType, new Type[] {}, null, null);
addMethod("Collection<Object>", "contains", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Collection<Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Collection<Object>", "iterator", null, false, oitrType, new Type[] {}, null, null);
addMethod("Collection<Object>", "remove", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Collection<Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Collection<String>", "add", null, false, booleanType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Collection<String>", "clear", null, false, voidType, new Type[] {}, null, null);
addMethod("Collection<String>", "contains", null, false, booleanType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Collection<String>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Collection<String>", "iterator", null, false, sitrType, new Type[] {}, null, null);
addMethod("Collection<String>", "remove", null, false, booleanType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Collection<String>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("List", "set", null, false, objectType, new Type[] {intType, objectType}, defType, new Type[] {intType, defType});
addMethod("List", "get", null, false, objectType, new Type[] {intType}, defType, null); addMethod("List", "get", null, false, objectType, new Type[] {intType}, defType, null);
addMethod("List", "remove", null, false, objectType, new Type[] {intType}, defType, null); addMethod("List", "remove", null, false, objectType, new Type[] {intType}, defType, null);
addMethod("List", "size", null, false, intType, new Type[] {}, null, null);
addMethod("List", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addConstructor("ArrayList", "new", new Type[] {}, null); addConstructor("ArrayList", "new", new Type[] {}, null);
addMethod("List<Object>", "set", null, false, objectType, new Type[] {intType, objectType}, null, null);
addMethod("List<Object>", "get", null, false, objectType, new Type[] {intType}, null, null);
addMethod("List<Object>", "remove", null, false, objectType, new Type[] {intType}, null, null);
addConstructor("ArrayList<Object>", "new", new Type[] {}, null);
addMethod("List<String>", "set", null, false, objectType, new Type[] {intType, objectType}, stringType, new Type[] {intType, stringType});
addMethod("List<String>", "get", null, false, objectType, new Type[] {intType}, stringType, null);
addMethod("List<String>", "remove", null, false, objectType, new Type[] {intType}, stringType, null);
addConstructor("ArrayList<String>", "new", new Type[] {}, null);
addConstructor("HashSet", "new", new Type[] {}, null);
addConstructor("HashSet<Object>", "new", new Type[] {}, null);
addConstructor("HashSet<String>", "new", new Type[] {}, null);
addMethod("Map", "put", null, false, objectType, new Type[] {objectType, objectType}, defType, new Type[] {defType, defType}); addMethod("Map", "put", null, false, objectType, new Type[] {objectType, objectType}, defType, new Type[] {defType, defType});
addMethod("Map", "get", null, false, objectType, new Type[] {objectType}, defType, new Type[] {defType}); addMethod("Map", "get", null, false, objectType, new Type[] {objectType}, defType, new Type[] {defType});
addMethod("Map", "remove", null, false, objectType, new Type[] {objectType}, null, null); addMethod("Map", "remove", null, false, objectType, new Type[] {objectType}, null, null);
addMethod("Map", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map", "isEmpty", null, false, booleanType, new Type[] {}, null, null); addMethod("Map", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Map", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map", "containsKey", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Map", "containsValue", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Map", "keySet", null, false, osetType, new Type[] {}, setType, null);
addMethod("Map", "values", null, false, ocollectionType, new Type[] {}, collectionType, null);
addConstructor("HashMap", "new", new Type[] {}, null); addConstructor("HashMap", "new", new Type[] {}, null);
addMethod("Map<String,def>", "put", null, false, objectType, new Type[] {objectType, objectType}, defType, new Type[] {stringType, defType});
addMethod("Map<String,def>", "get", null, false, objectType, new Type[] {objectType}, defType, new Type[] {stringType});
addMethod("Map<String,def>", "remove", null, false, objectType, new Type[] {objectType}, defType, new Type[] {stringType});
addMethod("Map<String,def>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<String,def>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addConstructor("HashMap<String,def>", "new", new Type[] {}, null);
addMethod("List<Object>", "addLast", "add", false, booleanType, new Type[] {objectType}, null, null);
addMethod("List<Object>", "add", null, false, voidType, new Type[] {intType, objectType}, null, null);
addMethod("List<Object>", "get", null, false, objectType, new Type[] {intType}, null, null);
addMethod("List<Object>", "remove", null, false, objectType, new Type[] {intType}, null, null);
addMethod("List<Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("List<Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addConstructor("ArrayList<Object>", "new", new Type[] {}, null);
addMethod("Map<Object,Object>", "put", null, false, objectType, new Type[] {objectType, objectType}, null, null); addMethod("Map<Object,Object>", "put", null, false, objectType, new Type[] {objectType, objectType}, null, null);
addMethod("Map<Object,Object>", "get", null, false, objectType, new Type[] {objectType}, null, null); addMethod("Map<Object,Object>", "get", null, false, objectType, new Type[] {objectType}, null, null);
addMethod("Map<Object,Object>", "remove", null, false, objectType, new Type[] {objectType}, null, null); addMethod("Map<Object,Object>", "remove", null, false, objectType, new Type[] {objectType}, null, null);
addMethod("Map<Object,Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<Object,Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null); addMethod("Map<Object,Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Map<Object,Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<Object,Object>", "containsKey", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Map<Object,Object>", "containsValue", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Map<Object,Object>", "keySet", null, false, osetType, new Type[] {}, null, null);
addMethod("Map<Object,Object>", "values", null, false, ocollectionType, new Type[] {}, null, null);
addConstructor("HashMap<Object,Object>", "new", new Type[] {}, null); addConstructor("HashMap<Object,Object>", "new", new Type[] {}, null);
addMethod("Map<String,def>", "put", null, false, objectType, new Type[] {objectType, objectType}, defType, new Type[] {stringType, defType});
addMethod("Map<String,def>", "get", null, false, objectType, new Type[] {objectType}, defType, new Type[] {stringType});
addMethod("Map<String,def>", "remove", null, false, objectType, new Type[] {objectType}, defType, new Type[] {stringType});
addMethod("Map<String,def>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Map<String,def>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<String,def>", "containsKey", null, false, booleanType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Map<String,def>", "containsValue", null, false, booleanType, new Type[] {objectType}, null, new Type[] {defType});
addMethod("Map<String,def>", "keySet", null, false, osetType, new Type[] {}, ssetType, null);
addMethod("Map<String,def>", "values", null, false, ocollectionType, new Type[] {}, collectionType, null);
addConstructor("HashMap<String,def>", "new", new Type[] {}, null);
addMethod("Map<String,Object>", "put", null, false, objectType, new Type[] {objectType, objectType}, null, new Type[] {stringType, objectType}); addMethod("Map<String,Object>", "put", null, false, objectType, new Type[] {objectType, objectType}, null, new Type[] {stringType, objectType});
addMethod("Map<String,Object>", "get", null, false, objectType, new Type[] {objectType}, null, new Type[] {stringType}); addMethod("Map<String,Object>", "get", null, false, objectType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Map<String,Object>", "remove", null, false, objectType, new Type[] {objectType}, null, new Type[] {stringType}); addMethod("Map<String,Object>", "remove", null, false, objectType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Map<String,Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<String,Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null); addMethod("Map<String,Object>", "isEmpty", null, false, booleanType, new Type[] {}, null, null);
addMethod("Map<String,Object>", "size", null, false, intType, new Type[] {}, null, null);
addMethod("Map<String,Object>", "containsKey", null, false, booleanType, new Type[] {objectType}, null, new Type[] {stringType});
addMethod("Map<String,Object>", "containsValue", null, false, booleanType, new Type[] {objectType}, null, null);
addMethod("Map<String,Object>", "keySet", null, false, osetType, new Type[] {}, ssetType, null);
addMethod("Map<String,Object>", "values", null, false, ocollectionType, new Type[] {}, null, null);
addConstructor("HashMap<String,Object>", "new", new Type[] {}, null); addConstructor("HashMap<String,Object>", "new", new Type[] {}, null);
addMethod("Exception", "getMessage", null, false, stringType, new Type[] {}, null, null);
addConstructor("ArithmeticException", "new", new Type[] {stringType}, null);
addConstructor("IllegalArgumentException", "new", new Type[] {stringType}, null);
addConstructor("IllegalStateException", "new", new Type[] {stringType}, null);
addConstructor("NumberFormatException", "new", new Type[] {stringType}, null);
} }
private void copyDefaultStructs() { private void copyDefaultStructs() {
@ -845,21 +1042,36 @@ class Definition {
copyStruct("CharSequence", "Object"); copyStruct("CharSequence", "Object");
copyStruct("String", "CharSequence", "Object"); copyStruct("String", "CharSequence", "Object");
copyStruct("List", "Object"); copyStruct("List", "Collection", "Object");
copyStruct("ArrayList", "List", "Object"); copyStruct("ArrayList", "List", "Collection", "Object");
copyStruct("List<Object>", "Collection<Object>", "Object");
copyStruct("ArrayList<Object>", "List<Object>", "Collection<Object>", "Object");
copyStruct("List<String>", "Collection<String>", "Object");
copyStruct("ArrayList<String>", "List<String>", "Collection<String>", "Object");
copyStruct("Set", "Collection", "Object");
copyStruct("HashSet", "Set", "Collection", "Object");
copyStruct("Set<Object>", "Collection<Object>", "Object");
copyStruct("HashSet<Object>", "Set<Object>", "Collection<Object>", "Object");
copyStruct("Set<String>", "Collection<String>", "Object");
copyStruct("HashSet<String>", "Set<String>", "Collection<String>", "Object");
copyStruct("Map", "Object"); copyStruct("Map", "Object");
copyStruct("HashMap", "Map", "Object"); copyStruct("HashMap", "Map", "Object");
copyStruct("Map<String,def>", "Object");
copyStruct("HashMap<String,def>", "Map<String,def>", "Object");
copyStruct("List<Object>", "Object");
copyStruct("ArrayList<Object>", "List", "Object");
copyStruct("Map<Object,Object>", "Object"); copyStruct("Map<Object,Object>", "Object");
copyStruct("HashMap<Object,Object>", "Map<Object,Object>", "Object"); copyStruct("HashMap<Object,Object>", "Map<Object,Object>", "Object");
copyStruct("Map<String,def>", "Object");
copyStruct("HashMap<String,def>", "Map<String,def>", "Object");
copyStruct("Map<String,Object>", "Object"); copyStruct("Map<String,Object>", "Object");
copyStruct("HashMap<String,Object>", "Map<String,Object>", "Object"); copyStruct("HashMap<String,Object>", "Map<String,Object>", "Object");
copyStruct("Executable", "Object"); copyStruct("Executable", "Object");
copyStruct("Exception", "Object");
copyStruct("ArithmeticException", "Exception", "Object");
copyStruct("IllegalArgumentException", "Exception", "Object");
copyStruct("IllegalStateException", "Exception", "Object");
copyStruct("NumberFormatException", "Exception", "Object");
} }
private void addDefaultTransforms() { private void addDefaultTransforms() {
@ -1162,41 +1374,155 @@ class Definition {
addBound(stringType, charseqType, charseqType); addBound(stringType, charseqType, charseqType);
addBound(oitrType, itrType, itrType);
addBound(oitrType, sitrType, itrType);
addBound(sitrType, itrType, itrType);
addBound(ocollectionType, collectionType, collectionType);
addBound(scollectionType, collectionType, collectionType);
addBound(scollectionType, ocollectionType, ocollectionType);
addBound(listType, collectionType, collectionType);
addBound(listType, ocollectionType, collectionType);
addBound(listType, scollectionType, collectionType);
addBound(arraylistType, collectionType, collectionType);
addBound(arraylistType, ocollectionType, collectionType);
addBound(arraylistType, scollectionType, collectionType);
addBound(arraylistType, listType, listType); addBound(arraylistType, listType, listType);
addBound(olistType, collectionType, collectionType);
addBound(olistType, ocollectionType, ocollectionType);
addBound(olistType, scollectionType, ocollectionType);
addBound(olistType, listType, listType); addBound(olistType, listType, listType);
addBound(olistType, arraylistType, listType); addBound(olistType, arraylistType, listType);
addBound(oarraylistType, collectionType, collectionType);
addBound(oarraylistType, ocollectionType, ocollectionType);
addBound(oarraylistType, scollectionType, ocollectionType);
addBound(oarraylistType, listType, listType); addBound(oarraylistType, listType, listType);
addBound(oarraylistType, olistType, olistType);
addBound(oarraylistType, arraylistType, arraylistType); addBound(oarraylistType, arraylistType, arraylistType);
addBound(oarraylistType, olistType, olistType);
addBound(slistType, collectionType, collectionType);
addBound(slistType, ocollectionType, ocollectionType);
addBound(slistType, scollectionType, scollectionType);
addBound(slistType, listType, listType);
addBound(slistType, arraylistType, listType);
addBound(slistType, olistType, olistType);
addBound(slistType, oarraylistType, olistType);
addBound(sarraylistType, collectionType, collectionType);
addBound(sarraylistType, ocollectionType, ocollectionType);
addBound(sarraylistType, scollectionType, scollectionType);
addBound(sarraylistType, listType, listType);
addBound(sarraylistType, arraylistType, arraylistType);
addBound(sarraylistType, olistType, olistType);
addBound(sarraylistType, oarraylistType, oarraylistType);
addBound(sarraylistType, slistType, slistType);
addBound(setType, collectionType, collectionType);
addBound(setType, ocollectionType, collectionType);
addBound(setType, scollectionType, collectionType);
addBound(setType, listType, collectionType);
addBound(setType, arraylistType, collectionType);
addBound(setType, olistType, collectionType);
addBound(setType, oarraylistType, collectionType);
addBound(setType, slistType, collectionType);
addBound(setType, sarraylistType, collectionType);
addBound(hashsetType, collectionType, collectionType);
addBound(hashsetType, ocollectionType, collectionType);
addBound(hashsetType, scollectionType, collectionType);
addBound(hashsetType, listType, collectionType);
addBound(hashsetType, arraylistType, collectionType);
addBound(hashsetType, olistType, collectionType);
addBound(hashsetType, oarraylistType, collectionType);
addBound(hashsetType, slistType, collectionType);
addBound(hashsetType, sarraylistType, collectionType);
addBound(hashsetType, setType, setType);
addBound(osetType, collectionType, collectionType);
addBound(osetType, ocollectionType, ocollectionType);
addBound(osetType, scollectionType, ocollectionType);
addBound(osetType, listType, collectionType);
addBound(osetType, arraylistType, collectionType);
addBound(osetType, olistType, ocollectionType);
addBound(osetType, oarraylistType, ocollectionType);
addBound(osetType, slistType, ocollectionType);
addBound(osetType, sarraylistType, ocollectionType);
addBound(osetType, setType, setType);
addBound(osetType, hashsetType, setType);
addBound(ohashsetType, collectionType, collectionType);
addBound(ohashsetType, ocollectionType, ocollectionType);
addBound(ohashsetType, scollectionType, ocollectionType);
addBound(ohashsetType, listType, collectionType);
addBound(ohashsetType, arraylistType, collectionType);
addBound(ohashsetType, olistType, ocollectionType);
addBound(ohashsetType, oarraylistType, ocollectionType);
addBound(ohashsetType, slistType, ocollectionType);
addBound(ohashsetType, sarraylistType, ocollectionType);
addBound(ohashsetType, setType, setType);
addBound(ohashsetType, hashsetType, hashsetType);
addBound(ohashsetType, osetType, osetType);
addBound(ssetType, collectionType, collectionType);
addBound(ssetType, ocollectionType, ocollectionType);
addBound(ssetType, scollectionType, scollectionType);
addBound(ssetType, listType, collectionType);
addBound(ssetType, arraylistType, collectionType);
addBound(ssetType, olistType, ocollectionType);
addBound(ssetType, oarraylistType, ocollectionType);
addBound(ssetType, slistType, scollectionType);
addBound(ssetType, sarraylistType, scollectionType);
addBound(ssetType, setType, setType);
addBound(ssetType, hashsetType, setType);
addBound(ssetType, osetType, osetType);
addBound(ssetType, ohashsetType, osetType);
addBound(shashsetType, collectionType, collectionType);
addBound(shashsetType, ocollectionType, ocollectionType);
addBound(shashsetType, scollectionType, scollectionType);
addBound(shashsetType, listType, collectionType);
addBound(shashsetType, arraylistType, collectionType);
addBound(shashsetType, olistType, ocollectionType);
addBound(shashsetType, oarraylistType, ocollectionType);
addBound(shashsetType, slistType, scollectionType);
addBound(shashsetType, sarraylistType, scollectionType);
addBound(shashsetType, setType, setType);
addBound(shashsetType, hashsetType, hashsetType);
addBound(shashsetType, osetType, osetType);
addBound(shashsetType, ohashsetType, hashsetType);
addBound(shashsetType, ssetType, ssetType);
addBound(hashmapType, mapType, mapType); addBound(hashmapType, mapType, mapType);
addBound(omapType, mapType, mapType); addBound(oomapType, mapType, mapType);
addBound(omapType, hashmapType, mapType); addBound(oomapType, hashmapType, mapType);
addBound(ohashmapType, mapType, mapType); addBound(oohashmapType, mapType, mapType);
addBound(ohashmapType, hashmapType, hashmapType); addBound(oohashmapType, hashmapType, hashmapType);
addBound(ohashmapType, omapType, omapType); addBound(oohashmapType, oomapType, oomapType);
addBound(smapType, mapType, mapType); addBound(smapType, mapType, mapType);
addBound(smapType, hashmapType, mapType); addBound(smapType, hashmapType, mapType);
addBound(smapType, omapType, omapType); addBound(smapType, oomapType, oomapType);
addBound(smapType, ohashmapType, omapType); addBound(smapType, oohashmapType, oomapType);
addBound(shashmapType, mapType, mapType); addBound(shashmapType, mapType, mapType);
addBound(shashmapType, hashmapType, hashmapType); addBound(shashmapType, hashmapType, hashmapType);
addBound(shashmapType, omapType, omapType); addBound(shashmapType, oomapType, oomapType);
addBound(shashmapType, ohashmapType, ohashmapType); addBound(shashmapType, oohashmapType, oohashmapType);
addBound(shashmapType, smapType, smapType); addBound(shashmapType, smapType, smapType);
addBound(somapType, mapType, mapType); addBound(somapType, mapType, mapType);
addBound(somapType, hashmapType, mapType); addBound(somapType, hashmapType, mapType);
addBound(somapType, omapType, omapType); addBound(somapType, oomapType, oomapType);
addBound(somapType, ohashmapType, omapType); addBound(somapType, oohashmapType, oomapType);
addBound(somapType, smapType, smapType); addBound(somapType, smapType, smapType);
addBound(somapType, shashmapType, smapType); addBound(somapType, shashmapType, smapType);
addBound(sohashmapType, mapType, mapType); addBound(sohashmapType, mapType, mapType);
addBound(sohashmapType, hashmapType, hashmapType); addBound(sohashmapType, hashmapType, hashmapType);
addBound(sohashmapType, omapType, omapType); addBound(sohashmapType, oomapType, oomapType);
addBound(sohashmapType, ohashmapType, ohashmapType); addBound(sohashmapType, oohashmapType, oohashmapType);
addBound(sohashmapType, smapType, smapType); addBound(sohashmapType, smapType, smapType);
addBound(sohashmapType, shashmapType, shashmapType); addBound(sohashmapType, shashmapType, shashmapType);
addBound(sohashmapType, somapType, somapType); addBound(sohashmapType, somapType, somapType);
addBound(arithexcepType, exceptionType, exceptionType);
addBound(iargexcepType, exceptionType, exceptionType);
addBound(istateexceptType, exceptionType, exceptionType);
addBound(nfexcepType, exceptionType, exceptionType);
addBound(arithexcepType, iargexcepType, exceptionType);
addBound(arithexcepType, istateexceptType, exceptionType);
addBound(arithexcepType, nfexcepType, exceptionType);
addBound(iargexcepType, istateexceptType, exceptionType);
addBound(iargexcepType, nfexcepType, exceptionType);
addBound(istateexceptType, nfexcepType, exceptionType);
} }
public final void addStruct(final String name, final Class<?> clazz) { public final void addStruct(final String name, final Class<?> clazz) {

View File

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

View File

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

View File

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

View File

@ -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}
* *

View File

@ -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}.

View File

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

View File

@ -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.
// TODO: how should custom definitions be specified? */
private Definition definition = null;
@Inject
public PlanAScriptEngineService(Settings settings) {
super(settings);
}
public void setDefinition(final Definition definition) {
this.definition = new Definition(definition);
}
@Override
public String[] types() {
return new String[] { NAME };
}
@Override
public String[] extensions() {
return new String[] { NAME };
}
@Override
public boolean sandboxed() {
return true;
}
// context used during compilation
private static final AccessControlContext COMPILATION_CONTEXT; private static final AccessControlContext COMPILATION_CONTEXT;
/**
* Setup the allowed permissions.
*/
static { static {
Permissions none = new Permissions(); final Permissions none = new Permissions();
none.setReadOnly(); none.setReadOnly();
COMPILATION_CONTEXT = new AccessControlContext(new ProtectionDomain[] { COMPILATION_CONTEXT = new AccessControlContext(new ProtectionDomain[] {
new ProtectionDomain(null, none) new ProtectionDomain(null, none)
}); });
} }
/**
* Used only for testing.
*/
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
public PlanAScriptEngineService(final Settings settings) {
super(settings);
}
/**
* Get the type name(s) for the language.
* @return Always contains only the single name of the language.
*/
@Override @Override
public Object compile(String script, Map<String, String> params) { public String[] types() {
return new String[] { NAME };
}
/**
* Get the extension(s) for the language.
* @return Always contains only the single extension of the language.
*/
@Override
public String[] extensions() {
return new String[] { NAME };
}
/**
* Whether or not the engine is secure.
* @return Always true as the engine should be secure at runtime.
*/
@Override
public boolean sandboxed() {
return true;
}
/**
* Compiles a Plan A script with the specified parameters.
* @param script The code to be compiled.
* @param params The params used to modify the compiler settings on a per script basis.
* @return Compiled script object represented by an {@link Executable}.
*/
@Override
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.
} }
} }

View File

@ -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() { * Run the script.
return ((Number) run()).floatValue(); * @return The script result as a double.
} */
@Override
public long runAsLong() {
return ((Number) run()).longValue();
}
@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);
} }

View File

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

View File

@ -21,6 +21,7 @@ package org.elasticsearch.plan.a;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTree;
import org.elasticsearch.script.ScoreAccessor;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
@ -34,11 +35,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.elasticsearch.plan.a.Adapter.ExpressionMetadata;
import static org.elasticsearch.plan.a.Adapter.ExtNodeMetadata;
import static org.elasticsearch.plan.a.Adapter.ExternalMetadata;
import static org.elasticsearch.plan.a.Adapter.StatementMetadata;
import static org.elasticsearch.plan.a.Adapter.error;
import static org.elasticsearch.plan.a.Definition.Cast; import static org.elasticsearch.plan.a.Definition.Cast;
import static org.elasticsearch.plan.a.Definition.Constructor; import static org.elasticsearch.plan.a.Definition.Constructor;
import static org.elasticsearch.plan.a.Definition.Field; import static org.elasticsearch.plan.a.Definition.Field;
@ -46,6 +42,11 @@ import static org.elasticsearch.plan.a.Definition.Method;
import static org.elasticsearch.plan.a.Definition.Sort; import static org.elasticsearch.plan.a.Definition.Sort;
import static org.elasticsearch.plan.a.Definition.Transform; import static org.elasticsearch.plan.a.Definition.Transform;
import static org.elasticsearch.plan.a.Definition.Type; import static org.elasticsearch.plan.a.Definition.Type;
import static org.elasticsearch.plan.a.Metadata.ExpressionMetadata;
import static org.elasticsearch.plan.a.Metadata.ExtNodeMetadata;
import static org.elasticsearch.plan.a.Metadata.ExternalMetadata;
import static org.elasticsearch.plan.a.Metadata.StatementMetadata;
import static org.elasticsearch.plan.a.Metadata.error;
import static org.elasticsearch.plan.a.PlanAParser.ADD; import static org.elasticsearch.plan.a.PlanAParser.ADD;
import static org.elasticsearch.plan.a.PlanAParser.AfterthoughtContext; import static org.elasticsearch.plan.a.PlanAParser.AfterthoughtContext;
import static org.elasticsearch.plan.a.PlanAParser.ArgumentsContext; import static org.elasticsearch.plan.a.PlanAParser.ArgumentsContext;
@ -69,6 +70,7 @@ import static org.elasticsearch.plan.a.PlanAParser.DecltypeContext;
import static org.elasticsearch.plan.a.PlanAParser.DeclvarContext; import static org.elasticsearch.plan.a.PlanAParser.DeclvarContext;
import static org.elasticsearch.plan.a.PlanAParser.DoContext; import static org.elasticsearch.plan.a.PlanAParser.DoContext;
import static org.elasticsearch.plan.a.PlanAParser.EmptyContext; import static org.elasticsearch.plan.a.PlanAParser.EmptyContext;
import static org.elasticsearch.plan.a.PlanAParser.EmptyscopeContext;
import static org.elasticsearch.plan.a.PlanAParser.ExprContext; import static org.elasticsearch.plan.a.PlanAParser.ExprContext;
import static org.elasticsearch.plan.a.PlanAParser.ExpressionContext; import static org.elasticsearch.plan.a.PlanAParser.ExpressionContext;
import static org.elasticsearch.plan.a.PlanAParser.ExtbraceContext; import static org.elasticsearch.plan.a.PlanAParser.ExtbraceContext;
@ -103,7 +105,10 @@ import static org.elasticsearch.plan.a.PlanAParser.SUB;
import static org.elasticsearch.plan.a.PlanAParser.SingleContext; import static org.elasticsearch.plan.a.PlanAParser.SingleContext;
import static org.elasticsearch.plan.a.PlanAParser.SourceContext; import static org.elasticsearch.plan.a.PlanAParser.SourceContext;
import static org.elasticsearch.plan.a.PlanAParser.StatementContext; import static org.elasticsearch.plan.a.PlanAParser.StatementContext;
import static org.elasticsearch.plan.a.PlanAParser.ThrowContext;
import static org.elasticsearch.plan.a.PlanAParser.TrapContext;
import static org.elasticsearch.plan.a.PlanAParser.TrueContext; import static org.elasticsearch.plan.a.PlanAParser.TrueContext;
import static org.elasticsearch.plan.a.PlanAParser.TryContext;
import static org.elasticsearch.plan.a.PlanAParser.USH; import static org.elasticsearch.plan.a.PlanAParser.USH;
import static org.elasticsearch.plan.a.PlanAParser.UnaryContext; import static org.elasticsearch.plan.a.PlanAParser.UnaryContext;
import static org.elasticsearch.plan.a.PlanAParser.WhileContext; import static org.elasticsearch.plan.a.PlanAParser.WhileContext;
@ -112,18 +117,13 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private static class Branch { private static class Branch {
final ParserRuleContext source; final ParserRuleContext source;
Label begin; Label begin = null;
Label end; Label end = null;
Label tru; Label tru = null;
Label fals; Label fals = null;
private Branch(final ParserRuleContext source) { private Branch(final ParserRuleContext source) {
this.source = source; this.source = source;
begin = null;
end = null;
tru = null;
fals = null;
} }
} }
@ -133,6 +133,8 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private final static org.objectweb.asm.Type CLASS_TYPE = private final static org.objectweb.asm.Type CLASS_TYPE =
org.objectweb.asm.Type.getType("L" + CLASS_NAME.replace(".", "/") + ";"); org.objectweb.asm.Type.getType("L" + CLASS_NAME.replace(".", "/") + ";");
private final static org.objectweb.asm.Type PLAN_A_ERROR_TYPE = org.objectweb.asm.Type.getType(PlanAError.class);
private final static org.objectweb.asm.commons.Method CONSTRUCTOR = org.objectweb.asm.commons.Method.getMethod( private final static org.objectweb.asm.commons.Method CONSTRUCTOR = org.objectweb.asm.commons.Method.getMethod(
"void <init>(org.elasticsearch.plan.a.Definition, java.lang.String, java.lang.String)"); "void <init>(org.elasticsearch.plan.a.Definition, java.lang.String, java.lang.String)");
private final static org.objectweb.asm.commons.Method EXECUTE = org.objectweb.asm.commons.Method.getMethod( private final static org.objectweb.asm.commons.Method EXECUTE = org.objectweb.asm.commons.Method.getMethod(
@ -141,6 +143,14 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private final static org.objectweb.asm.Type DEFINITION_TYPE = org.objectweb.asm.Type.getType(Definition.class); private final static org.objectweb.asm.Type DEFINITION_TYPE = org.objectweb.asm.Type.getType(Definition.class);
private final static org.objectweb.asm.Type MAP_TYPE = org.objectweb.asm.Type.getType(Map.class);
private final static org.objectweb.asm.commons.Method MAP_GET =
org.objectweb.asm.commons.Method.getMethod("Object get(Object)");
private final static org.objectweb.asm.Type SCORE_ACCESSOR_TYPE = org.objectweb.asm.Type.getType(ScoreAccessor.class);
private final static org.objectweb.asm.commons.Method SCORE_ACCESSOR_FLOAT =
org.objectweb.asm.commons.Method.getMethod("float floatValue()");
private final static org.objectweb.asm.commons.Method DEF_METHOD_CALL = org.objectweb.asm.commons.Method.getMethod( private final static org.objectweb.asm.commons.Method DEF_METHOD_CALL = org.objectweb.asm.commons.Method.getMethod(
"java.lang.Object methodCall(java.lang.Object, java.lang.String, " + "java.lang.Object methodCall(java.lang.Object, java.lang.String, " +
"org.elasticsearch.plan.a.Definition, java.lang.Object[], boolean[])"); "org.elasticsearch.plan.a.Definition, java.lang.Object[], boolean[])");
@ -296,35 +306,31 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private final static org.objectweb.asm.commons.Method SUBWOOVERLOW_DOUBLE = private final static org.objectweb.asm.commons.Method SUBWOOVERLOW_DOUBLE =
org.objectweb.asm.commons.Method.getMethod("double subtractWithoutOverflow(double, double)"); org.objectweb.asm.commons.Method.getMethod("double subtractWithoutOverflow(double, double)");
static byte[] write(Adapter adapter) { static byte[] write(Metadata metadata) {
Writer writer = new Writer(adapter); Writer writer = new Writer(metadata);
return writer.getBytes(); return writer.getBytes();
} }
private final Adapter adapter; private final Metadata metadata;
private final Definition definition; private final Definition definition;
private final ParseTree root; private final ParseTree root;
private final String source; private final String source;
private final CompilerSettings settings; private final CompilerSettings settings;
private final Map<ParserRuleContext, Branch> branches; private final Map<ParserRuleContext, Branch> branches = new HashMap<>();
private final Deque<Branch> jumps; private final Deque<Branch> jumps = new ArrayDeque<>();
private final Set<ParserRuleContext> strings; private final Set<ParserRuleContext> strings = new HashSet<>();
private ClassWriter writer; private ClassWriter writer;
private GeneratorAdapter execute; private GeneratorAdapter execute;
private Writer(final Adapter adapter) { private Writer(final Metadata metadata) {
this.adapter = adapter; this.metadata = metadata;
definition = adapter.definition; definition = metadata.definition;
root = adapter.root; root = metadata.root;
source = adapter.source; source = metadata.source;
settings = adapter.settings; settings = metadata.settings;
branches = new HashMap<>();
jumps = new ArrayDeque<>();
strings = new HashSet<>();
writeBegin(); writeBegin();
writeConstructor(); writeConstructor();
@ -377,19 +383,39 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private void writeExecute() { private void writeExecute() {
final int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; final int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
execute = new GeneratorAdapter(access, EXECUTE, SIGNATURE, null, writer); execute = new GeneratorAdapter(access, EXECUTE, SIGNATURE, null, writer);
final Label fals = new Label();
final Label end = new Label();
execute.visitVarInsn(Opcodes.ALOAD, metadata.inputValueSlot);
execute.push("#score");
execute.invokeInterface(MAP_TYPE, MAP_GET);
execute.dup();
execute.ifNull(fals);
execute.checkCast(SCORE_ACCESSOR_TYPE);
execute.invokeVirtual(SCORE_ACCESSOR_TYPE, SCORE_ACCESSOR_FLOAT);
execute.goTo(end);
execute.mark(fals);
execute.pop();
execute.push(0F);
execute.mark(end);
execute.visitVarInsn(Opcodes.FSTORE, metadata.scoreValueSlot);
execute.push(settings.getMaxLoopCounter());
execute.visitVarInsn(Opcodes.ISTORE, metadata.loopCounterSlot);
visit(root); visit(root);
execute.endMethod(); execute.endMethod();
} }
@Override @Override
public Void visitSource(final SourceContext ctx) { public Void visitSource(final SourceContext ctx) {
final StatementMetadata sourcesmd = adapter.getStatementMetadata(ctx); final StatementMetadata sourcesmd = metadata.getStatementMetadata(ctx);
for (final StatementContext sctx : ctx.statement()) { for (final StatementContext sctx : ctx.statement()) {
visit(sctx); visit(sctx);
} }
if (!sourcesmd.allReturn) { if (!sourcesmd.methodEscape) {
execute.visitInsn(Opcodes.ACONST_NULL); execute.visitInsn(Opcodes.ACONST_NULL);
execute.returnValue(); execute.returnValue();
} }
@ -408,11 +434,11 @@ class Writer extends PlanAParserBaseVisitor<Void> {
visit(exprctx); visit(exprctx);
final BlockContext blockctx0 = ctx.block(0); final BlockContext blockctx0 = ctx.block(0);
final StatementMetadata blockmd0 = adapter.getStatementMetadata(blockctx0); final StatementMetadata blockmd0 = metadata.getStatementMetadata(blockctx0);
visit(blockctx0); visit(blockctx0);
if (els) { if (els) {
if (!blockmd0.allExit) { if (!blockmd0.allLast) {
execute.goTo(branch.end); execute.goTo(branch.end);
} }
@ -438,15 +464,20 @@ class Writer extends PlanAParserBaseVisitor<Void> {
visit(exprctx); visit(exprctx);
final BlockContext blockctx = ctx.block(); final BlockContext blockctx = ctx.block();
boolean allexit = false; boolean allLast = false;
if (blockctx != null) { if (blockctx != null) {
StatementMetadata blocksmd = adapter.getStatementMetadata(blockctx); final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx);
allexit = blocksmd.allExit; allLast = blocksmd.allLast;
writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1);
visit(blockctx); visit(blockctx);
} else if (ctx.empty() != null) {
writeLoopCounter(1);
} else {
throw new IllegalStateException(error(ctx) + "Unexpected writer state.");
} }
if (!allexit) { if (!allLast) {
execute.goTo(branch.begin); execute.goTo(branch.begin);
} }
@ -460,23 +491,21 @@ class Writer extends PlanAParserBaseVisitor<Void> {
public Void visitDo(final DoContext ctx) { public Void visitDo(final DoContext ctx) {
final ExpressionContext exprctx = ctx.expression(); final ExpressionContext exprctx = ctx.expression();
final Branch branch = markBranch(ctx, exprctx); final Branch branch = markBranch(ctx, exprctx);
Label start = new Label();
branch.begin = new Label(); branch.begin = new Label();
branch.end = new Label(); branch.end = new Label();
branch.fals = branch.end; branch.fals = branch.end;
final BlockContext blockctx = ctx.block();
final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx);
jumps.push(branch); jumps.push(branch);
execute.mark(start);
visit(blockctx);
execute.mark(branch.begin); execute.mark(branch.begin);
final BlockContext bctx = ctx.block();
final StatementMetadata blocksmd = adapter.getStatementMetadata(bctx);
visit(bctx);
visit(exprctx); visit(exprctx);
writeLoopCounter(blocksmd.count > 0 ? blocksmd.count : 1);
if (!blocksmd.allExit) { execute.goTo(start);
execute.goTo(branch.begin);
}
execute.mark(branch.end); execute.mark(branch.end);
jumps.pop(); jumps.pop();
@ -506,12 +535,24 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
final BlockContext blockctx = ctx.block(); final BlockContext blockctx = ctx.block();
boolean allexit = false; boolean allLast = false;
if (blockctx != null) { if (blockctx != null) {
StatementMetadata blocksmd = adapter.getStatementMetadata(blockctx); StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx);
allexit = blocksmd.allExit; allLast = blocksmd.allLast;
int count = blocksmd.count > 0 ? blocksmd.count : 1;
if (atctx != null) {
++count;
}
writeLoopCounter(count);
visit(blockctx); visit(blockctx);
} else if (ctx.empty() != null) {
writeLoopCounter(1);
} else {
throw new IllegalStateException(error(ctx) + "Unexpected writer state.");
} }
if (atctx != null) { if (atctx != null) {
@ -519,7 +560,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
visit(atctx); visit(atctx);
} }
if (atctx != null || !allexit) { if (atctx != null || !allLast) {
execute.goTo(start); execute.goTo(start);
} }
@ -560,14 +601,56 @@ class Writer extends PlanAParserBaseVisitor<Void> {
return null; return null;
} }
@Override
public Void visitTry(final TryContext ctx) {
final TrapContext[] trapctxs = new TrapContext[ctx.trap().size()];
ctx.trap().toArray(trapctxs);
final Branch branch = markBranch(ctx, trapctxs);
Label end = new Label();
branch.begin = new Label();
branch.end = new Label();
branch.tru = trapctxs.length > 1 ? end : null;
execute.mark(branch.begin);
final BlockContext blockctx = ctx.block();
final StatementMetadata blocksmd = metadata.getStatementMetadata(blockctx);
visit(blockctx);
if (!blocksmd.allLast) {
execute.goTo(end);
}
execute.mark(branch.end);
for (final TrapContext trapctx : trapctxs) {
visit(trapctx);
}
if (!blocksmd.allLast || trapctxs.length > 1) {
execute.mark(end);
}
return null;
}
@Override
public Void visitThrow(final ThrowContext ctx) {
visit(ctx.expression());
execute.throwException();
return null;
}
@Override @Override
public Void visitExpr(final ExprContext ctx) { public Void visitExpr(final ExprContext ctx) {
final StatementMetadata exprsmd = adapter.getStatementMetadata(ctx); final StatementMetadata exprsmd = metadata.getStatementMetadata(ctx);
final ExpressionContext exprctx = ctx.expression(); final ExpressionContext exprctx = ctx.expression();
final ExpressionMetadata expremd = adapter.getExpressionMetadata(exprctx); final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx);
visit(exprctx); visit(exprctx);
if (exprsmd.allReturn) { if (exprsmd.methodEscape) {
execute.returnValue(); execute.returnValue();
} else { } else {
writePop(expremd.to.type.getSize()); writePop(expremd.to.type.getSize());
@ -605,7 +688,9 @@ class Writer extends PlanAParserBaseVisitor<Void> {
if (declctx != null) { if (declctx != null) {
visit(declctx); visit(declctx);
} else if (exprctx != null) { } else if (exprctx != null) {
final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx);
visit(exprctx); visit(exprctx);
writePop(expremd.to.type.getSize());
} else { } else {
throw new IllegalStateException(error(ctx) + "Unexpected writer state."); throw new IllegalStateException(error(ctx) + "Unexpected writer state.");
} }
@ -615,7 +700,10 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitAfterthought(AfterthoughtContext ctx) { public Void visitAfterthought(AfterthoughtContext ctx) {
final ExpressionContext exprctx = ctx.expression();
final ExpressionMetadata expremd = metadata.getExpressionMetadata(exprctx);
visit(ctx.expression()); visit(ctx.expression());
writePop(expremd.to.type.getSize());
return null; return null;
} }
@ -636,7 +724,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitDeclvar(final DeclvarContext ctx) { public Void visitDeclvar(final DeclvarContext ctx) {
final ExpressionMetadata declvaremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata declvaremd = metadata.getExpressionMetadata(ctx);
final org.objectweb.asm.Type type = declvaremd.to.type; final org.objectweb.asm.Type type = declvaremd.to.type;
final Sort sort = declvaremd.to.sort; final Sort sort = declvaremd.to.sort;
final int slot = (int)declvaremd.postConst; final int slot = (int)declvaremd.postConst;
@ -666,6 +754,34 @@ class Writer extends PlanAParserBaseVisitor<Void> {
return null; return null;
} }
@Override
public Void visitTrap(final TrapContext ctx) {
final StatementMetadata trapsmd = metadata.getStatementMetadata(ctx);
final Branch branch = getBranch(ctx);
final Label jump = new Label();
final BlockContext blockctx = ctx.block();
final EmptyscopeContext emptyctx = ctx.emptyscope();
execute.mark(jump);
writeLoadStoreVariable(ctx, true, trapsmd.exception, trapsmd.slot);
if (blockctx != null) {
visit(ctx.block());
} else if (emptyctx == null) {
throw new IllegalStateException(error(ctx) + "Unexpected writer state.");
}
execute.visitTryCatchBlock(branch.begin, branch.end, jump, trapsmd.exception.type.getInternalName());
if (branch.tru != null && !trapsmd.allLast) {
execute.goTo(branch.tru);
}
return null;
}
@Override @Override
public Void visitPrecedence(final PrecedenceContext ctx) { public Void visitPrecedence(final PrecedenceContext ctx) {
throw new UnsupportedOperationException(error(ctx) + "Unexpected writer state."); throw new UnsupportedOperationException(error(ctx) + "Unexpected writer state.");
@ -673,7 +789,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitNumeric(final NumericContext ctx) { public Void visitNumeric(final NumericContext ctx) {
final ExpressionMetadata numericemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata numericemd = metadata.getExpressionMetadata(ctx);
final Object postConst = numericemd.postConst; final Object postConst = numericemd.postConst;
if (postConst == null) { if (postConst == null) {
@ -690,7 +806,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitChar(final CharContext ctx) { public Void visitChar(final CharContext ctx) {
final ExpressionMetadata charemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata charemd = metadata.getExpressionMetadata(ctx);
final Object postConst = charemd.postConst; final Object postConst = charemd.postConst;
if (postConst == null) { if (postConst == null) {
@ -707,7 +823,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitTrue(final TrueContext ctx) { public Void visitTrue(final TrueContext ctx) {
final ExpressionMetadata trueemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata trueemd = metadata.getExpressionMetadata(ctx);
final Object postConst = trueemd.postConst; final Object postConst = trueemd.postConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -727,7 +843,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitFalse(final FalseContext ctx) { public Void visitFalse(final FalseContext ctx) {
final ExpressionMetadata falseemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata falseemd = metadata.getExpressionMetadata(ctx);
final Object postConst = falseemd.postConst; final Object postConst = falseemd.postConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -747,7 +863,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitNull(final NullContext ctx) { public Void visitNull(final NullContext ctx) {
final ExpressionMetadata nullemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata nullemd = metadata.getExpressionMetadata(ctx);
execute.visitInsn(Opcodes.ACONST_NULL); execute.visitInsn(Opcodes.ACONST_NULL);
checkWriteCast(nullemd); checkWriteCast(nullemd);
@ -758,7 +874,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitExternal(final ExternalContext ctx) { public Void visitExternal(final ExternalContext ctx) {
final ExpressionMetadata expremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx);
visit(ctx.extstart()); visit(ctx.extstart());
checkWriteCast(expremd); checkWriteCast(expremd);
checkWriteBranch(ctx); checkWriteBranch(ctx);
@ -769,7 +885,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitPostinc(final PostincContext ctx) { public Void visitPostinc(final PostincContext ctx) {
final ExpressionMetadata expremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx);
visit(ctx.extstart()); visit(ctx.extstart());
checkWriteCast(expremd); checkWriteCast(expremd);
checkWriteBranch(ctx); checkWriteBranch(ctx);
@ -779,7 +895,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitPreinc(final PreincContext ctx) { public Void visitPreinc(final PreincContext ctx) {
final ExpressionMetadata expremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx);
visit(ctx.extstart()); visit(ctx.extstart());
checkWriteCast(expremd); checkWriteCast(expremd);
checkWriteBranch(ctx); checkWriteBranch(ctx);
@ -789,7 +905,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitUnary(final UnaryContext ctx) { public Void visitUnary(final UnaryContext ctx) {
final ExpressionMetadata unaryemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata unaryemd = metadata.getExpressionMetadata(ctx);
final Object postConst = unaryemd.postConst; final Object postConst = unaryemd.postConst;
final Object preConst = unaryemd.preConst; final Object preConst = unaryemd.preConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -891,7 +1007,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitCast(final CastContext ctx) { public Void visitCast(final CastContext ctx) {
final ExpressionMetadata castemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata castemd = metadata.getExpressionMetadata(ctx);
final Object postConst = castemd.postConst; final Object postConst = castemd.postConst;
if (postConst == null) { if (postConst == null) {
@ -908,7 +1024,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitBinary(final BinaryContext ctx) { public Void visitBinary(final BinaryContext ctx) {
final ExpressionMetadata binaryemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata binaryemd = metadata.getExpressionMetadata(ctx);
final Object postConst = binaryemd.postConst; final Object postConst = binaryemd.postConst;
final Object preConst = binaryemd.preConst; final Object preConst = binaryemd.preConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -930,7 +1046,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
final ExpressionContext exprctx0 = ctx.expression(0); final ExpressionContext exprctx0 = ctx.expression(0);
final ExpressionMetadata expremd0 = adapter.getExpressionMetadata(exprctx0); final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0);
strings.add(exprctx0); strings.add(exprctx0);
visit(exprctx0); visit(exprctx0);
@ -940,7 +1056,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
final ExpressionContext exprctx1 = ctx.expression(1); final ExpressionContext exprctx1 = ctx.expression(1);
final ExpressionMetadata expremd1 = adapter.getExpressionMetadata(exprctx1); final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1);
strings.add(exprctx1); strings.add(exprctx1);
visit(exprctx1); visit(exprctx1);
@ -990,7 +1106,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitComp(final CompContext ctx) { public Void visitComp(final CompContext ctx) {
final ExpressionMetadata compemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata compemd = metadata.getExpressionMetadata(ctx);
final Object postConst = compemd.postConst; final Object postConst = compemd.postConst;
final Object preConst = compemd.preConst; final Object preConst = compemd.preConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -1014,10 +1130,10 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
} else { } else {
final ExpressionContext exprctx0 = ctx.expression(0); final ExpressionContext exprctx0 = ctx.expression(0);
final ExpressionMetadata expremd0 = adapter.getExpressionMetadata(exprctx0); final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(exprctx0);
final ExpressionContext exprctx1 = ctx.expression(1); final ExpressionContext exprctx1 = ctx.expression(1);
final ExpressionMetadata expremd1 = adapter.getExpressionMetadata(exprctx1); final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(exprctx1);
final org.objectweb.asm.Type type = expremd1.to.type; final org.objectweb.asm.Type type = expremd1.to.type;
final Sort sort1 = expremd1.to.sort; final Sort sort1 = expremd1.to.sort;
@ -1156,7 +1272,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitBool(final BoolContext ctx) { public Void visitBool(final BoolContext ctx) {
final ExpressionMetadata boolemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata boolemd = metadata.getExpressionMetadata(ctx);
final Object postConst = boolemd.postConst; final Object postConst = boolemd.postConst;
final Object preConst = boolemd.preConst; final Object preConst = boolemd.preConst;
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
@ -1255,7 +1371,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitConditional(final ConditionalContext ctx) { public Void visitConditional(final ConditionalContext ctx) {
final ExpressionMetadata condemd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata condemd = metadata.getExpressionMetadata(ctx);
final Branch branch = getBranch(ctx); final Branch branch = getBranch(ctx);
final ExpressionContext expr0 = ctx.expression(0); final ExpressionContext expr0 = ctx.expression(0);
@ -1286,7 +1402,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitAssignment(final AssignmentContext ctx) { public Void visitAssignment(final AssignmentContext ctx) {
final ExpressionMetadata expremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata expremd = metadata.getExpressionMetadata(ctx);
visit(ctx.extstart()); visit(ctx.extstart());
checkWriteCast(expremd); checkWriteCast(expremd);
checkWriteBranch(ctx); checkWriteBranch(ctx);
@ -1296,10 +1412,10 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitExtstart(ExtstartContext ctx) { public Void visitExtstart(ExtstartContext ctx) {
final ExternalMetadata startemd = adapter.getExternalMetadata(ctx); final ExternalMetadata startemd = metadata.getExternalMetadata(ctx);
if (startemd.token == ADD) { if (startemd.token == ADD) {
final ExpressionMetadata storeemd = adapter.getExpressionMetadata(startemd.storeExpr); final ExpressionMetadata storeemd = metadata.getExpressionMetadata(startemd.storeExpr);
if (startemd.current.sort == Sort.STRING || storeemd.from.sort == Sort.STRING) { if (startemd.current.sort == Sort.STRING || storeemd.from.sort == Sort.STRING) {
writeNewStrings(); writeNewStrings();
@ -1372,7 +1488,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitExtcast(final ExtcastContext ctx) { public Void visitExtcast(final ExtcastContext ctx) {
ExtNodeMetadata castenmd = adapter.getExtNodeMetadata(ctx); ExtNodeMetadata castenmd = metadata.getExtNodeMetadata(ctx);
final ExtprecContext precctx = ctx.extprec(); final ExtprecContext precctx = ctx.extprec();
final ExtcastContext castctx = ctx.extcast(); final ExtcastContext castctx = ctx.extcast();
@ -1404,7 +1520,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitExtbrace(final ExtbraceContext ctx) { public Void visitExtbrace(final ExtbraceContext ctx) {
final ExpressionContext exprctx = adapter.updateExpressionTree(ctx.expression()); final ExpressionContext exprctx = metadata.updateExpressionTree(ctx.expression());
visit(exprctx); visit(exprctx);
writeLoadStoreExternal(ctx); writeLoadStoreExternal(ctx);
@ -1508,7 +1624,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitExtstring(ExtstringContext ctx) { public Void visitExtstring(ExtstringContext ctx) {
final ExtNodeMetadata stringenmd = adapter.getExtNodeMetadata(ctx); final ExtNodeMetadata stringenmd = metadata.getExtNodeMetadata(ctx);
writeConstant(ctx, stringenmd.target); writeConstant(ctx, stringenmd.target);
@ -1531,7 +1647,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
@Override @Override
public Void visitIncrement(IncrementContext ctx) { public Void visitIncrement(IncrementContext ctx) {
final ExpressionMetadata incremd = adapter.getExpressionMetadata(ctx); final ExpressionMetadata incremd = metadata.getExpressionMetadata(ctx);
final Object postConst = incremd.postConst; final Object postConst = incremd.postConst;
if (postConst == null) { if (postConst == null) {
@ -1546,6 +1662,18 @@ class Writer extends PlanAParserBaseVisitor<Void> {
return null; return null;
} }
private void writeLoopCounter(final int count) {
final Label end = new Label();
execute.iinc(metadata.loopCounterSlot, -count);
execute.visitVarInsn(Opcodes.ILOAD, metadata.loopCounterSlot);
execute.push(0);
execute.ifICmp(GeneratorAdapter.GT, end);
execute.throwException(PLAN_A_ERROR_TYPE,
"The maximum number of statements that can be executed in a loop has been reached.");
execute.mark(end);
}
private void writeConstant(final ParserRuleContext source, final Object constant) { private void writeConstant(final ParserRuleContext source, final Object constant) {
if (constant instanceof Number) { if (constant instanceof Number) {
writeNumeric(source, constant); writeNumeric(source, constant);
@ -1623,7 +1751,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
(sort == Sort.FLOAT || sort == Sort.DOUBLE) && (sort == Sort.FLOAT || sort == Sort.DOUBLE) &&
(token == MUL || token == DIV || token == REM || token == ADD || token == SUB)); (token == MUL || token == DIV || token == REM || token == ADD || token == SUB));
// if its a 64-bit shift, fixup the last argument to truncate to 32-bits // if its a 64-bit shift, fixup the lastSource argument to truncate to 32-bits
// note unlike java, this means we still do binary promotion of shifts, // note unlike java, this means we still do binary promotion of shifts,
// but it keeps things simple -- this check works because we promote shifts. // but it keeps things simple -- this check works because we promote shifts.
if (sort == Sort.LONG && (token == LSH || token == USH || token == RSH)) { if (sort == Sort.LONG && (token == LSH || token == USH || token == RSH)) {
@ -1845,8 +1973,8 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
private void writeLoadStoreExternal(final ParserRuleContext source) { private void writeLoadStoreExternal(final ParserRuleContext source) {
final ExtNodeMetadata sourceenmd = adapter.getExtNodeMetadata(source); final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
final ExternalMetadata parentemd = adapter.getExternalMetadata(sourceenmd.parent); final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent);
final boolean length = "#length".equals(sourceenmd.target); final boolean length = "#length".equals(sourceenmd.target);
final boolean array = "#brace".equals(sourceenmd.target); final boolean array = "#brace".equals(sourceenmd.target);
@ -1868,7 +1996,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
if (length) { if (length) {
execute.arrayLength(); execute.arrayLength();
} else if (sourceenmd.last && parentemd.storeExpr != null) { } else if (sourceenmd.last && parentemd.storeExpr != null) {
final ExpressionMetadata expremd = adapter.getExpressionMetadata(parentemd.storeExpr); final ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr);
final boolean cat = strings.contains(parentemd.storeExpr); final boolean cat = strings.contains(parentemd.storeExpr);
if (cat) { if (cat) {
@ -1973,7 +2101,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
final boolean store, final boolean variable, final boolean store, final boolean variable,
final boolean field, final boolean name, final boolean field, final boolean name,
final boolean array, final boolean shortcut) { final boolean array, final boolean shortcut) {
final ExtNodeMetadata sourceemd = adapter.getExtNodeMetadata(source); final ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source);
if (variable) { if (variable) {
writeLoadStoreVariable(source, store, sourceemd.type, (int)sourceemd.target); writeLoadStoreVariable(source, store, sourceemd.type, (int)sourceemd.target);
@ -2030,9 +2158,9 @@ class Writer extends PlanAParserBaseVisitor<Void> {
private void writeLoadStoreField(final ParserRuleContext source, final boolean store, final String name) { private void writeLoadStoreField(final ParserRuleContext source, final boolean store, final String name) {
if (store) { if (store) {
final ExtNodeMetadata sourceemd = adapter.getExtNodeMetadata(source); final ExtNodeMetadata sourceemd = metadata.getExtNodeMetadata(source);
final ExternalMetadata parentemd = adapter.getExternalMetadata(sourceemd.parent); final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceemd.parent);
final ExpressionMetadata expremd = adapter.getExpressionMetadata(parentemd.storeExpr); final ExpressionMetadata expremd = metadata.getExpressionMetadata(parentemd.storeExpr);
execute.push(name); execute.push(name);
execute.loadThis(); execute.loadThis();
@ -2054,12 +2182,12 @@ class Writer extends PlanAParserBaseVisitor<Void> {
if (type.sort == Sort.DEF) { if (type.sort == Sort.DEF) {
final ExtbraceContext bracectx = (ExtbraceContext)source; final ExtbraceContext bracectx = (ExtbraceContext)source;
final ExpressionMetadata expremd0 = adapter.getExpressionMetadata(bracectx.expression()); final ExpressionMetadata expremd0 = metadata.getExpressionMetadata(bracectx.expression());
if (store) { if (store) {
final ExtNodeMetadata braceenmd = adapter.getExtNodeMetadata(bracectx); final ExtNodeMetadata braceenmd = metadata.getExtNodeMetadata(bracectx);
final ExternalMetadata parentemd = adapter.getExternalMetadata(braceenmd.parent); final ExternalMetadata parentemd = metadata.getExternalMetadata(braceenmd.parent);
final ExpressionMetadata expremd1 = adapter.getExpressionMetadata(parentemd.storeExpr); final ExpressionMetadata expremd1 = metadata.getExpressionMetadata(parentemd.storeExpr);
execute.loadThis(); execute.loadThis();
execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE); execute.getField(CLASS_TYPE, "definition", DEFINITION_TYPE);
@ -2118,8 +2246,8 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
private void writeNewExternal(final ExtnewContext source) { private void writeNewExternal(final ExtnewContext source) {
final ExtNodeMetadata sourceenmd = adapter.getExtNodeMetadata(source); final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
final ExternalMetadata parentemd = adapter.getExternalMetadata(sourceenmd.parent); final ExternalMetadata parentemd = metadata.getExternalMetadata(sourceenmd.parent);
final boolean makearray = "#makearray".equals(sourceenmd.target); final boolean makearray = "#makearray".equals(sourceenmd.target);
final boolean constructor = sourceenmd.target instanceof Constructor; final boolean constructor = sourceenmd.target instanceof Constructor;
@ -2155,7 +2283,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
} }
private void writeCallExternal(final ExtcallContext source) { private void writeCallExternal(final ExtcallContext source) {
final ExtNodeMetadata sourceenmd = adapter.getExtNodeMetadata(source); final ExtNodeMetadata sourceenmd = metadata.getExtNodeMetadata(source);
final boolean method = sourceenmd.target instanceof Method; final boolean method = sourceenmd.target instanceof Method;
final boolean def = sourceenmd.target instanceof String; final boolean def = sourceenmd.target instanceof String;
@ -2205,7 +2333,7 @@ class Writer extends PlanAParserBaseVisitor<Void> {
for (int argument = 0; argument < arguments.size(); ++argument) { for (int argument = 0; argument < arguments.size(); ++argument) {
execute.dup(); execute.dup();
execute.push(argument); execute.push(argument);
execute.push(adapter.getExpressionMetadata(arguments.get(argument)).typesafe); execute.push(metadata.getExpressionMetadata(arguments.get(argument)).typesafe);
execute.arrayStore(definition.booleanType.type); execute.arrayStore(definition.booleanType.type);
} }

View File

@ -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;"));
}
}

View File

@ -28,7 +28,7 @@ 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() {

View File

@ -28,7 +28,7 @@ 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() {

View File

@ -28,7 +28,7 @@ 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() {

View File

@ -28,7 +28,7 @@ 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() {

View File

@ -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 */

View File

@ -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 {
@ -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."));
}
}
} }