Plan A Update...
New Features: Exceptions (throw, try/catch) Infinite Loop Checking Iterators added to the API Fixes: Improved loop path analysis Fixed set/add issue with shortcuts in lists Fixed minor promotion issue with incorrect type Fixed score issue related to score extending Number Documentation: Added JavaDocs for some files
This commit is contained in:
parent
da699ceae2
commit
2249a640bb
|
@ -34,18 +34,23 @@ statement
|
||||||
| CONTINUE SEMICOLON? # continue
|
| CONTINUE SEMICOLON? # continue
|
||||||
| BREAK SEMICOLON? # break
|
| BREAK SEMICOLON? # break
|
||||||
| RETURN expression SEMICOLON? # return
|
| RETURN expression SEMICOLON? # return
|
||||||
| TRY block ( CATCH LP ( TYPE ID ) RP block )+ # try
|
| TRY block trap+ # try
|
||||||
| THROW expression SEMICOLON? # throw
|
| THROW expression SEMICOLON? # throw
|
||||||
| expression SEMICOLON? # expr
|
| expression SEMICOLON? # expr
|
||||||
;
|
;
|
||||||
|
|
||||||
block
|
block
|
||||||
: LBRACK statement* RBRACK # multiple
|
: LBRACK statement+ RBRACK # multiple
|
||||||
| statement # single
|
| statement # single
|
||||||
;
|
;
|
||||||
|
|
||||||
empty
|
empty
|
||||||
: SEMICOLON
|
: emptyscope
|
||||||
|
| SEMICOLON
|
||||||
|
;
|
||||||
|
|
||||||
|
emptyscope
|
||||||
|
: LBRACK RBRACK
|
||||||
;
|
;
|
||||||
|
|
||||||
initializer
|
initializer
|
||||||
|
@ -69,6 +74,10 @@ declvar
|
||||||
: ID ( ASSIGN expression )?
|
: ID ( ASSIGN expression )?
|
||||||
;
|
;
|
||||||
|
|
||||||
|
trap
|
||||||
|
: CATCH LP ( TYPE ID ) RP ( block | emptyscope )
|
||||||
|
;
|
||||||
|
|
||||||
expression
|
expression
|
||||||
: LP expression RP # precedence
|
: LP expression RP # precedence
|
||||||
| ( OCTAL | HEX | INTEGER | DECIMAL ) # numeric
|
| ( OCTAL | HEX | INTEGER | DECIMAL ) # numeric
|
||||||
|
|
|
@ -1,278 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.plan.a;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.ParserRuleContext;
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.elasticsearch.plan.a.Definition.Cast;
|
|
||||||
import static org.elasticsearch.plan.a.Definition.Type;
|
|
||||||
import static org.elasticsearch.plan.a.PlanAParser.ExpressionContext;
|
|
||||||
import static org.elasticsearch.plan.a.PlanAParser.PrecedenceContext;
|
|
||||||
|
|
||||||
class Adapter {
|
|
||||||
static class StatementMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean last;
|
|
||||||
|
|
||||||
boolean allExit;
|
|
||||||
boolean allReturn;
|
|
||||||
boolean anyReturn;
|
|
||||||
boolean allBreak;
|
|
||||||
boolean anyBreak;
|
|
||||||
boolean allContinue;
|
|
||||||
boolean anyContinue;
|
|
||||||
|
|
||||||
private StatementMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
last = false;
|
|
||||||
|
|
||||||
allExit = false;
|
|
||||||
allReturn = false;
|
|
||||||
anyReturn = false;
|
|
||||||
allBreak = false;
|
|
||||||
anyBreak = false;
|
|
||||||
allContinue = false;
|
|
||||||
anyContinue = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExpressionMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean read;
|
|
||||||
boolean statement;
|
|
||||||
|
|
||||||
Object preConst;
|
|
||||||
Object postConst;
|
|
||||||
boolean isNull;
|
|
||||||
|
|
||||||
Type to;
|
|
||||||
Type from;
|
|
||||||
boolean explicit;
|
|
||||||
boolean typesafe;
|
|
||||||
|
|
||||||
Cast cast;
|
|
||||||
|
|
||||||
private ExpressionMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
read = true;
|
|
||||||
statement = false;
|
|
||||||
|
|
||||||
preConst = null;
|
|
||||||
postConst = null;
|
|
||||||
isNull = false;
|
|
||||||
|
|
||||||
to = null;
|
|
||||||
from = null;
|
|
||||||
explicit = false;
|
|
||||||
typesafe = true;
|
|
||||||
|
|
||||||
cast = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExternalMetadata {
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
boolean read;
|
|
||||||
ParserRuleContext storeExpr;
|
|
||||||
int token;
|
|
||||||
boolean pre;
|
|
||||||
boolean post;
|
|
||||||
|
|
||||||
int scope;
|
|
||||||
Type current;
|
|
||||||
boolean statik;
|
|
||||||
boolean statement;
|
|
||||||
Object constant;
|
|
||||||
|
|
||||||
private ExternalMetadata(final ParserRuleContext source) {
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
read = false;
|
|
||||||
storeExpr = null;
|
|
||||||
token = 0;
|
|
||||||
pre = false;
|
|
||||||
post = false;
|
|
||||||
|
|
||||||
scope = 0;
|
|
||||||
current = null;
|
|
||||||
statik = false;
|
|
||||||
statement = false;
|
|
||||||
constant = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ExtNodeMetadata {
|
|
||||||
final ParserRuleContext parent;
|
|
||||||
final ParserRuleContext source;
|
|
||||||
|
|
||||||
Object target;
|
|
||||||
boolean last;
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
Type promote;
|
|
||||||
|
|
||||||
Cast castFrom;
|
|
||||||
Cast castTo;
|
|
||||||
|
|
||||||
private ExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
target = null;
|
|
||||||
last = false;
|
|
||||||
|
|
||||||
type = null;
|
|
||||||
promote = null;
|
|
||||||
|
|
||||||
castFrom = null;
|
|
||||||
castTo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String error(final ParserRuleContext ctx) {
|
|
||||||
return "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: ";
|
|
||||||
}
|
|
||||||
|
|
||||||
final Definition definition;
|
|
||||||
final String source;
|
|
||||||
final ParserRuleContext root;
|
|
||||||
final CompilerSettings settings;
|
|
||||||
|
|
||||||
private final Map<ParserRuleContext, StatementMetadata> statementMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExpressionMetadata> expressionMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExternalMetadata> externalMetadata;
|
|
||||||
private final Map<ParserRuleContext, ExtNodeMetadata> extNodeMetadata;
|
|
||||||
|
|
||||||
Adapter(final Definition definition, final String source, final ParserRuleContext root, final CompilerSettings settings) {
|
|
||||||
this.definition = definition;
|
|
||||||
this.source = source;
|
|
||||||
this.root = root;
|
|
||||||
this.settings = settings;
|
|
||||||
|
|
||||||
statementMetadata = new HashMap<>();
|
|
||||||
expressionMetadata = new HashMap<>();
|
|
||||||
externalMetadata = new HashMap<>();
|
|
||||||
extNodeMetadata = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
StatementMetadata createStatementMetadata(final ParserRuleContext source) {
|
|
||||||
final StatementMetadata sourcesmd = new StatementMetadata(source);
|
|
||||||
statementMetadata.put(source, sourcesmd);
|
|
||||||
|
|
||||||
return sourcesmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
StatementMetadata getStatementMetadata(final ParserRuleContext source) {
|
|
||||||
final StatementMetadata sourcesmd = statementMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourcesmd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "Statement metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourcesmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionContext updateExpressionTree(ExpressionContext source) {
|
|
||||||
if (source instanceof PrecedenceContext) {
|
|
||||||
final ParserRuleContext parent = source.getParent();
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
for (final ParseTree child : parent.children) {
|
|
||||||
if (child == source) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (source instanceof PrecedenceContext) {
|
|
||||||
source = ((PrecedenceContext)source).expression();
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.children.set(index, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionMetadata createExpressionMetadata(ParserRuleContext source) {
|
|
||||||
final ExpressionMetadata sourceemd = new ExpressionMetadata(source);
|
|
||||||
expressionMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionMetadata getExpressionMetadata(final ParserRuleContext source) {
|
|
||||||
final ExpressionMetadata sourceemd = expressionMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "Expression metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalMetadata createExternalMetadata(final ParserRuleContext source) {
|
|
||||||
final ExternalMetadata sourceemd = new ExternalMetadata(source);
|
|
||||||
externalMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalMetadata getExternalMetadata(final ParserRuleContext source) {
|
|
||||||
final ExternalMetadata sourceemd = externalMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtNodeMetadata createExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
|
||||||
final ExtNodeMetadata sourceemd = new ExtNodeMetadata(parent, source);
|
|
||||||
extNodeMetadata.put(source, sourceemd);
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtNodeMetadata getExtNodeMetadata(final ParserRuleContext source) {
|
|
||||||
final ExtNodeMetadata sourceemd = extNodeMetadata.get(source);
|
|
||||||
|
|
||||||
if (sourceemd == null) {
|
|
||||||
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
|
||||||
" the parse node with text [" + source.getText() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceemd;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,88 +30,87 @@ import java.security.CodeSource;
|
||||||
import java.security.SecureClassLoader;
|
import java.security.SecureClassLoader;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Compiler is the entry point for generating a Plan A script. The compiler will generate an ANTLR
|
||||||
|
* parse tree based on the source code that is passed in. Two passes will then be run over the parse tree,
|
||||||
|
* one for analysis using the {@link Analyzer} and another to generate the actual byte code using ASM in
|
||||||
|
* the {@link Writer}.
|
||||||
|
*/
|
||||||
final class Compiler {
|
final class Compiler {
|
||||||
|
/**
|
||||||
|
* The default language API to be used with Plan A. The second construction is used
|
||||||
|
* to finalize all the variables, so there is no mistake of modification afterwards.
|
||||||
|
*/
|
||||||
private static Definition DEFAULT_DEFINITION = new Definition(new Definition());
|
private static Definition DEFAULT_DEFINITION = new Definition(new Definition());
|
||||||
|
|
||||||
/** we define the class with lowest privileges */
|
/**
|
||||||
|
* Define the class with lowest privileges.
|
||||||
|
*/
|
||||||
private static final CodeSource CODESOURCE;
|
private static final CodeSource CODESOURCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the code privileges.
|
||||||
|
*/
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
|
// Setup the code privileges.
|
||||||
CODESOURCE = new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[]) null);
|
CODESOURCE = new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[]) null);
|
||||||
} catch (MalformedURLException impossible) {
|
} catch (MalformedURLException impossible) {
|
||||||
throw new RuntimeException(impossible);
|
throw new RuntimeException(impossible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A secure class loader used to define Plan A scripts.
|
||||||
|
*/
|
||||||
static class Loader extends SecureClassLoader {
|
static class Loader extends SecureClassLoader {
|
||||||
Loader(ClassLoader parent) {
|
/**
|
||||||
|
* @param parent The parent ClassLoader.
|
||||||
|
*/
|
||||||
|
Loader(final ClassLoader parent) {
|
||||||
super(parent);
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends Executable> define(String name, byte[] bytes) {
|
/**
|
||||||
|
* Generates a Class object from the generated byte code.
|
||||||
|
* @param name The name of the class.
|
||||||
|
* @param bytes The generated byte code.
|
||||||
|
* @return A Class object extending {@link Executable}.
|
||||||
|
*/
|
||||||
|
Class<? extends Executable> define(final String name, final byte[] bytes) {
|
||||||
return defineClass(name, bytes, 0, bytes.length, CODESOURCE).asSubclass(Executable.class);
|
return defineClass(name, bytes, 0, bytes.length, CODESOURCE).asSubclass(Executable.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Executable compile(Loader loader, final String name, final String source, final Definition custom, CompilerSettings settings) {
|
/**
|
||||||
long start = System.currentTimeMillis();
|
* Runs the two-pass compiler to generate a Plan A script.
|
||||||
|
* @param loader The ClassLoader used to define the script.
|
||||||
final Definition definition = custom == null ? DEFAULT_DEFINITION : new Definition(custom);
|
* @param name The name of the script.
|
||||||
|
* @param source The source code for the script.
|
||||||
//long end = System.currentTimeMillis() - start;
|
* @param settings The CompilerSettings to be used during the compilation.
|
||||||
//System.out.println("types: " + end);
|
* @return An {@link Executable} Plan A script.
|
||||||
//start = System.currentTimeMillis();
|
*/
|
||||||
|
static Executable compile(final Loader loader, final String name, final String source,
|
||||||
//final ParserRuleContext root = createParseTree(source, types);
|
final Definition custom, final CompilerSettings settings) {
|
||||||
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
final Definition definition = custom != null ? new Definition(custom) : DEFAULT_DEFINITION;
|
||||||
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
final ParserRuleContext root = createParseTree(source, definition);
|
||||||
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
final Metadata metadata = new Metadata(definition, source, root, settings);
|
||||||
final ParserErrorStrategy strategy = new ParserErrorStrategy();
|
Analyzer.analyze(metadata);
|
||||||
|
final byte[] bytes = Writer.write(metadata);
|
||||||
lexer.removeErrorListeners();
|
|
||||||
lexer.setTypes(definition.structs.keySet());
|
|
||||||
|
|
||||||
//List<? extends Token> tokens = lexer.getAllTokens();
|
|
||||||
|
|
||||||
//for (final Token token : tokens) {
|
|
||||||
// System.out.println(token.getType() + " " + token.getText());
|
|
||||||
//}
|
|
||||||
|
|
||||||
parser.removeErrorListeners();
|
|
||||||
parser.setErrorHandler(strategy);
|
|
||||||
|
|
||||||
ParserRuleContext root = parser.source();
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("tree: " + end);
|
|
||||||
|
|
||||||
final Adapter adapter = new Adapter(definition, source, root, settings);
|
|
||||||
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
Analyzer.analyze(adapter);
|
|
||||||
//System.out.println(root.toStringTree(parser));
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("analyze: " + end);
|
|
||||||
//start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final byte[] bytes = Writer.write(adapter);
|
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("write: " + end);
|
|
||||||
//start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final Executable executable = createExecutable(loader, definition, name, source, bytes);
|
final Executable executable = createExecutable(loader, definition, name, source, bytes);
|
||||||
|
|
||||||
//end = System.currentTimeMillis() - start;
|
|
||||||
//System.out.println("create: " + end);
|
|
||||||
|
|
||||||
return executable;
|
return executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParserRuleContext createParseTree(String source, Definition definition) {
|
/**
|
||||||
|
* Generates the ANTLR tree from the given source code. Several methods below, are used
|
||||||
|
* to ensure that the first error generated by ANTLR will cause the compilation to fail rather than
|
||||||
|
* use ANTLR's recovery strategies that may be potentially dangerous.
|
||||||
|
* @param source The source code for the script.
|
||||||
|
* @param definition The Plan A API.
|
||||||
|
* @return The root node for the ANTLR parse tree.
|
||||||
|
*/
|
||||||
|
private static ParserRuleContext createParseTree(final String source, final Definition definition) {
|
||||||
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
final ANTLRInputStream stream = new ANTLRInputStream(source);
|
||||||
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
final ErrorHandlingLexer lexer = new ErrorHandlingLexer(stream);
|
||||||
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
final PlanAParser parser = new PlanAParser(new CommonTokenStream(lexer));
|
||||||
|
@ -119,36 +118,50 @@ final class Compiler {
|
||||||
|
|
||||||
lexer.removeErrorListeners();
|
lexer.removeErrorListeners();
|
||||||
lexer.setTypes(definition.structs.keySet());
|
lexer.setTypes(definition.structs.keySet());
|
||||||
|
|
||||||
parser.removeErrorListeners();
|
parser.removeErrorListeners();
|
||||||
parser.setErrorHandler(strategy);
|
parser.setErrorHandler(strategy);
|
||||||
|
|
||||||
ParserRuleContext root = parser.source();
|
ParserRuleContext root = parser.source();
|
||||||
// System.out.println(root.toStringTree(parser));
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Executable createExecutable(Loader loader, Definition definition, String name, String source, byte[] bytes) {
|
/**
|
||||||
|
* Generates an {@link Executable} that can run a Plan A script.
|
||||||
|
* @param loader The {@link Loader} to define the script's class file.
|
||||||
|
* @param definition The Plan A API.
|
||||||
|
* @param name The name of the script.
|
||||||
|
* @param source The source text of the script.
|
||||||
|
* @param bytes The ASM generated byte code to define the class with.
|
||||||
|
* @return A Plan A {@link Executable} script.
|
||||||
|
*/
|
||||||
|
private static Executable createExecutable(final Loader loader, final Definition definition,
|
||||||
|
final String name, final String source, final byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
// for debugging:
|
// Used for debugging. Uncomment this code and add -Dtests.security.manager=false when running to save
|
||||||
//try {
|
// the generated Java class files. The javap tool can then be used to inspect the generated byte code.
|
||||||
// FileOutputStream f = new FileOutputStream(new File("/Users/jdconrad/lang/generated/out.class"), false);
|
|
||||||
|
// 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() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.");
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
package org.elasticsearch.plan.a;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
* license agreements. See the NOTICE file distributed with
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
@ -19,6 +17,8 @@ package org.elasticsearch.plan.a;
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.LexerNoViableAltException;
|
import org.antlr.v4.runtime.LexerNoViableAltException;
|
||||||
import org.antlr.v4.runtime.misc.Interval;
|
import org.antlr.v4.runtime.misc.Interval;
|
||||||
|
|
|
@ -0,0 +1,619 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.elasticsearch.plan.a.Definition.Cast;
|
||||||
|
import org.elasticsearch.plan.a.Definition.Type;
|
||||||
|
import org.elasticsearch.plan.a.PlanAParser.ExpressionContext;
|
||||||
|
import org.elasticsearch.plan.a.PlanAParser.PrecedenceContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata is a wrapper for all the data that is collected by the {@link Analyzer}. Each node in the ANTLR parse tree
|
||||||
|
* will have one of the types of metadata to store information used either in a different node by the analyzer
|
||||||
|
* or by the {@link Writer} during byte code generation. Metadata also contains several objects passed into the
|
||||||
|
* {@link Analyzer} and {@link Writer} used during compilation including the {@link Definition}, the source code,
|
||||||
|
* the root of the ANTLR parse tree, and the {@link CompilerSettings}.
|
||||||
|
*/
|
||||||
|
class Metadata {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatementMetadata is used to store metadata mostly about
|
||||||
|
* control flow for ANTLR nodes related to if/else, do, while, for, etc.
|
||||||
|
*/
|
||||||
|
static class StatementMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lastSource variable will be set to true when the final statement from the root ANTLR node is about
|
||||||
|
* to be visited. This is used to determine whether or not the auto-return feature is allowed to be used,
|
||||||
|
* and if a null return value needs to be generated automatically since a return value is always required.
|
||||||
|
*/
|
||||||
|
boolean lastSource = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The beginLoop variable will be set to true whenever a loop node is initially visited including inner
|
||||||
|
* loops. This will not be propagated down the parse tree afterwards, though. This is used to determine
|
||||||
|
* whether or not inLoop should be set further down the tree. Note that inLoop alone is not enough
|
||||||
|
* information to determine whether we are in the last statement of a loop because we may inside of
|
||||||
|
* multiple loops, so this variable is necessary.
|
||||||
|
*/
|
||||||
|
boolean beginLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The inLoop variable is set to true when inside a loop. This will be propagated down the parse tree. This
|
||||||
|
* is used to determine whether or not continue and break statements are legal.
|
||||||
|
*/
|
||||||
|
boolean inLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lastLoop variable is set to true when the final statement of a loop is reached. This will be
|
||||||
|
* propagated down the parse tree until another loop is reached and then will not be propagated further for
|
||||||
|
* the current loop. This is used to determine whether or not a continue statement is superfluous.
|
||||||
|
*/
|
||||||
|
boolean lastLoop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The methodEscape variable is set to true when a statement would cause the method to potentially exit. This
|
||||||
|
* includes return, throw, and continuous loop statements. Note that a catch statement may possibly
|
||||||
|
* reset this to false after a throw statement. This will be propagated up the tree as far as necessary.
|
||||||
|
* This is used by the {@link Writer} to ensure that superfluous statements aren't unnecessarily written
|
||||||
|
* into generated bytecode.
|
||||||
|
*/
|
||||||
|
boolean methodEscape = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The loopEscape variable is set to true when a loop is going to be exited. This may be caused by a number of
|
||||||
|
* different statements including continue, break, return, etc. This will only be propagated as far as the
|
||||||
|
* loop node. This is used to ensure that in certain case an infinite loop will be caught at
|
||||||
|
* compile-time rather than run-time.
|
||||||
|
*/
|
||||||
|
boolean loopEscape = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The allLast variable is set whenever a final statement in a block is reached. This includes the end of loop,
|
||||||
|
* if, else, etc. This will be only propagated to the top of the block statement ANTLR node.
|
||||||
|
* This is used to ensure that there are no unreachable statements within the script.
|
||||||
|
*/
|
||||||
|
boolean allLast = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The anyContinue will be set to true when a continue statement is visited. This will be propagated to the
|
||||||
|
* loop node it's within. This is used to ensure that in certain case an infinite loop will be caught at
|
||||||
|
* compile-time rather than run-time.
|
||||||
|
*/
|
||||||
|
boolean anyContinue = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The anyBreak will be set to true when a break statement is visited. This will be propagated to the
|
||||||
|
* loop node it's within. This is used to in conjunction with methodEscape to ensure there are no unreachable
|
||||||
|
* statements within the script.
|
||||||
|
*/
|
||||||
|
boolean anyBreak = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The count variable is used as a rudimentary count of statements within a loop. This will be used in
|
||||||
|
* the {@link Writer} to keep a count of statements that have been executed at run-time to ensure that a loop
|
||||||
|
* will exit if it runs too long.
|
||||||
|
*/
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exception variable is used to store the exception type when a throw node is visited. This is used by
|
||||||
|
* the {@link Writer} to write the correct type of exception in the generated byte code.
|
||||||
|
*/
|
||||||
|
Type exception = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The slot variable is used to store the place on the stack of where a thrown exception will be stored to.
|
||||||
|
* This is used by the {@link Writer}.
|
||||||
|
*/
|
||||||
|
int slot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private StatementMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpressionMetadata is used to store metadata mostly about constants and casting
|
||||||
|
* for ANTLR nodes related to mathematical operations.
|
||||||
|
*/
|
||||||
|
static class ExpressionMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The read variable is used to determine whether or not the value of an expression will be read from.
|
||||||
|
* This is set to false when the expression is the left-hand side of an assignment that is not chained or
|
||||||
|
* when a method call is made alone. This will propagate down the tree as far as necessary.
|
||||||
|
* The {@link Writer} uses this to determine when a value may need to be popped from the stack
|
||||||
|
* such as when a method call returns a value that is never read.
|
||||||
|
*/
|
||||||
|
boolean read = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statement variable is set true when an expression is a complete meaning that there is some sort
|
||||||
|
* of effect on a variable or a method call is made. This will propagate up the tree as far as necessary.
|
||||||
|
* This prevents statements that have no effect on the output of a script from being executed.
|
||||||
|
*/
|
||||||
|
boolean statement = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preConst variable is set to a non-null value when a constant statement is made in a script. This is
|
||||||
|
* used to track the constant value prior to any casts being made on an ANTLR node.
|
||||||
|
*/
|
||||||
|
Object preConst = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The postConst variable is set to a non-null value when a cast is made on a node where a preConst variable
|
||||||
|
* has already been set when the cast would leave the constant as a non-object value except in the case of a
|
||||||
|
* String. This will be propagated up the tree and used to simplify constants when possible such as making
|
||||||
|
* the value of 2*2 be 4 in the * node, so that the {@link Writer} only has to push a 4 onto the stack.
|
||||||
|
*/
|
||||||
|
Object postConst = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The isNull variable is set to true when a null constant statement is made in the script. This allows the
|
||||||
|
* {@link Writer} to potentially shortcut certain comparison operations.
|
||||||
|
*/
|
||||||
|
boolean isNull = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The to variable is used to track what an ANTLR node's value should be cast to. This is set on every ANTLR
|
||||||
|
* node in the tree, and used by the {@link Writer} to make a run-time cast if necessary in the byte code.
|
||||||
|
* This is also used by the {@link Analyzer} to determine if a cast is legal.
|
||||||
|
*/
|
||||||
|
Type to = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The from variable is used to track what an ANTLR node's value should be cast from. This is set on every
|
||||||
|
* ANTLR node in the tree independent of other nodes. This is used by the {@link Analyzer} to determine if a
|
||||||
|
* cast is legal.
|
||||||
|
*/
|
||||||
|
Type from = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The explicit variable is set to true when a cast is explicitly made in the script. This tracks whether
|
||||||
|
* or not a cast is a legal up cast.
|
||||||
|
*/
|
||||||
|
boolean explicit = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The typesafe variable is set to true when a dynamic type is used as part of an expression. This propagates
|
||||||
|
* up the tree to the top of the expression. This allows for implicit up casts throughout the expression and
|
||||||
|
* is used by the {@link Analyzer}.
|
||||||
|
*/
|
||||||
|
boolean typesafe = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set to the combination of the to and from variables at the end of each node visit in the
|
||||||
|
* {@link Analyzer}. This is set on every ANTLR node in the tree independent of other nodes, and is
|
||||||
|
* used by {@link Writer} to make a run-time cast if necessary in the byte code.
|
||||||
|
*/
|
||||||
|
Cast cast = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExpressionMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExternalMetadata is used to store metadata about the overall state of a variable/method chain such as
|
||||||
|
* '(int)x.get(3)' where each piece of that chain is broken into it's indiviual pieces and stored in
|
||||||
|
* {@link ExtNodeMetadata}.
|
||||||
|
*/
|
||||||
|
static class ExternalMetadata {
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The read variable is set to true when the value of a variable/method chain is going to be read from.
|
||||||
|
* This is used by the {@link Analyzer} to determine if this variable/method chain will be in a standalone
|
||||||
|
* statement.
|
||||||
|
*/
|
||||||
|
boolean read = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The storeExpr variable is set to the right-hand side of an assignment in the variable/method chain if
|
||||||
|
* necessary. This is used by the {@link Analyzer} to set the proper metadata for a read versus a write,
|
||||||
|
* and is used by the {@link Writer} to determine if a bytecode operation should be a load or a store.
|
||||||
|
*/
|
||||||
|
ParserRuleContext storeExpr = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token variable is set to a constant value of the operator type (+, -, etc.) when a compound assignment
|
||||||
|
* is being visited. This is also used by the increment and decrement operators. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle the compound assignment.
|
||||||
|
*/
|
||||||
|
int token = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre variable is set to true when pre-increment or pre-decrement is visited. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle any reads of the variable/method chain that are
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
boolean pre = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The post variable is set to true when post-increment or post-decrement is visited. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to correctly handle any reads of the variable/method chain that are
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
boolean post = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scope variable is incremented and decremented when a precedence node is visited as part of a
|
||||||
|
* variable/method chain. This is used by the {@link Analyzer} to determine when the final piece of the
|
||||||
|
* variable/method chain has been reached.
|
||||||
|
*/
|
||||||
|
int scope = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current variable is set to whatever the current type is within the visited node of the variable/method
|
||||||
|
* chain. This changes as the nodes for the variable/method are walked through. This is used by the
|
||||||
|
* {@link Analyzer} to make decisions about whether or not a cast is legal, and what methods are available
|
||||||
|
* for that specific type.
|
||||||
|
*/
|
||||||
|
Type current = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statik variable is set to true when a variable/method chain begins with static type. This is used by
|
||||||
|
* the {@link Analyzer} to determine what methods/members are available for that specific type.
|
||||||
|
*/
|
||||||
|
boolean statik = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statement variable is set to true when a variable/method chain can be standalone statement. This is
|
||||||
|
* used by the {@link Analyzer} to error out if there a variable/method chain that is not a statement.
|
||||||
|
*/
|
||||||
|
boolean statement = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant variable is set when a String constant is part of the variable/method chain. String is a
|
||||||
|
* special case because methods/members need to be able to be called on a String constant, so this can't be
|
||||||
|
* only as part of {@link ExpressionMetadata}. This is used by the {@link Writer} to write out the String
|
||||||
|
* constant in the byte code.
|
||||||
|
*/
|
||||||
|
Object constant = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExternalMetadata(final ParserRuleContext source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExtNodeMetadata {
|
||||||
|
/**
|
||||||
|
* The parent variable is top-level ANTLR node of the variable/method chain. This is used to retrieve the
|
||||||
|
* ExternalMetadata for the variable/method chain this ExtNodeMetadata is a piece of.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source variable is the ANTLR node used to generate this metadata.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target variable is set to a value based on the type of ANTLR node that is visited. This is used by
|
||||||
|
* {@link Writer} to determine whether a cast, store, load, or method call should be written in byte code
|
||||||
|
* depending on what the target variable is.
|
||||||
|
*/
|
||||||
|
Object target = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last variable is set to true when the last ANTLR node of the variable/method chain is visted. This is
|
||||||
|
* used by the {@link Writer} in conjuction with the storeExpr variable to determine whether or not a store
|
||||||
|
* needs to be written as opposed to a load.
|
||||||
|
*/
|
||||||
|
boolean last = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type variable is set to the type that a visited node ends with. This is used by both the
|
||||||
|
* {@link Analyzer} and {@link Writer} to make decisions about compound assignments, String constants, and
|
||||||
|
* shortcuts.
|
||||||
|
*/
|
||||||
|
Type type = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The promote variable is set to the type of a promotion within a compound assignment. Compound assignments
|
||||||
|
* may require promotion between the left-hand side variable and right-hand side value. This is used by the
|
||||||
|
* {@link Writer} to make the correct decision about the byte code operation.
|
||||||
|
*/
|
||||||
|
Type promote = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The castFrom variable is set during a compound assignment. This is used by the {@link Writer} to
|
||||||
|
* cast the values to the promoted type during a compound assignment.
|
||||||
|
*/
|
||||||
|
Cast castFrom = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The castTo variable is set during an explicit cast in a variable/method chain or during a compound
|
||||||
|
* assignment. This is used by the {@link Writer} to either do an explicit cast, or cast the values
|
||||||
|
* from the promoted type back to the original type during a compound assignment.
|
||||||
|
*/
|
||||||
|
Cast castTo = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param parent The top-level ANTLR node for the variable/method chain.
|
||||||
|
* @param source The associated ANTLR node.
|
||||||
|
*/
|
||||||
|
private ExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility method to output consistent error messages.
|
||||||
|
* @param ctx The ANTLR node the error occurred in.
|
||||||
|
* @return The error message with tacked on line number and character position.
|
||||||
|
*/
|
||||||
|
static String error(final ParserRuleContext ctx) {
|
||||||
|
return "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acts as both the Plan A API and white-list for what types and methods are allowed.
|
||||||
|
*/
|
||||||
|
final Definition definition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original text of the input script. This is used to write out the source code into
|
||||||
|
* the byte code file for debugging purposes.
|
||||||
|
*/
|
||||||
|
final String source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toot node of the ANTLR tree for the Plan A script.
|
||||||
|
*/
|
||||||
|
final ParserRuleContext root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine certain compile-time constraints such as whether or not numeric overflow is allowed
|
||||||
|
* and how many statements are allowed before a loop will throw an exception.
|
||||||
|
*/
|
||||||
|
final CompilerSettings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the input variable is stored in. This is used in the {@link Writer} whenever
|
||||||
|
* the input variable is accessed.
|
||||||
|
*/
|
||||||
|
int inputValueSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the score variable is stored in. This is used in the {@link Writer} whenever
|
||||||
|
* the score variable is accessed.
|
||||||
|
*/
|
||||||
|
int scoreValueSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine what slot the loopCounter variable is stored in. This is used n the {@link Writer} whenever
|
||||||
|
* the loop variable is accessed.
|
||||||
|
*/
|
||||||
|
int loopCounterSlot = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, StatementMetadata> statementMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExpressionMetadata> expressionMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExternalMetadata> externalMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the relevant ANTLR node to its metadata.
|
||||||
|
*/
|
||||||
|
private final Map<ParserRuleContext, ExtNodeMetadata> extNodeMetadata = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param definition The Plan A definition.
|
||||||
|
* @param source The source text for the script.
|
||||||
|
* @param root The root ANTLR node.
|
||||||
|
* @param settings The compile-time settings.
|
||||||
|
*/
|
||||||
|
Metadata(final Definition definition, final String source, final ParserRuleContext root, final CompilerSettings settings) {
|
||||||
|
this.definition = definition;
|
||||||
|
this.source = source;
|
||||||
|
this.root = root;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StatementMetadata and stores it in the statementMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new StatementMetadata.
|
||||||
|
*/
|
||||||
|
StatementMetadata createStatementMetadata(final ParserRuleContext source) {
|
||||||
|
final StatementMetadata sourcesmd = new StatementMetadata(source);
|
||||||
|
statementMetadata.put(source, sourcesmd);
|
||||||
|
|
||||||
|
return sourcesmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves StatementMetadata from the statementMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved StatementMetadata.
|
||||||
|
*/
|
||||||
|
StatementMetadata getStatementMetadata(final ParserRuleContext source) {
|
||||||
|
final StatementMetadata sourcesmd = statementMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourcesmd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "Statement metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourcesmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ANTLR parse tree is modified in one single case; a parent node needs to check a child node to see if it's
|
||||||
|
* a precedence node, and if so, it must be removed from the tree permanently. Once the ANTLR tree is built,
|
||||||
|
* precedence nodes are no longer necessary to maintain the correct ordering of the tree, so they only
|
||||||
|
* add a level of indirection where complicated decisions about metadata passing would have to be made. This
|
||||||
|
* method removes the need for those decisions.
|
||||||
|
* @param source The child ANTLR node to check for precedence.
|
||||||
|
* @return The updated child ANTLR node.
|
||||||
|
*/
|
||||||
|
ExpressionContext updateExpressionTree(ExpressionContext source) {
|
||||||
|
// Check to see if the ANTLR node is a precedence node.
|
||||||
|
if (source instanceof PrecedenceContext) {
|
||||||
|
final ParserRuleContext parent = source.getParent();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
// Mark the index of the source node within the list of child nodes from the parent.
|
||||||
|
for (final ParseTree child : parent.children) {
|
||||||
|
if (child == source) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are multiple precedence nodes in a row, remove them all.
|
||||||
|
while (source instanceof PrecedenceContext) {
|
||||||
|
source = ((PrecedenceContext)source).expression();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the parent node with the child of the precedence node.
|
||||||
|
parent.children.set(index, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ExpressionMetadata and stores it in the expressionMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new ExpressionMetadata.
|
||||||
|
*/
|
||||||
|
ExpressionMetadata createExpressionMetadata(ParserRuleContext source) {
|
||||||
|
final ExpressionMetadata sourceemd = new ExpressionMetadata(source);
|
||||||
|
expressionMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExpressionMetadata from the expressionMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExpressionMetadata.
|
||||||
|
*/
|
||||||
|
ExpressionMetadata getExpressionMetadata(final ParserRuleContext source) {
|
||||||
|
final ExpressionMetadata sourceemd = expressionMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "Expression metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ExternalMetadata and stores it in the externalMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new ExternalMetadata.
|
||||||
|
*/
|
||||||
|
ExternalMetadata createExternalMetadata(final ParserRuleContext source) {
|
||||||
|
final ExternalMetadata sourceemd = new ExternalMetadata(source);
|
||||||
|
externalMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExternalMetadata from the externalMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExternalMetadata.
|
||||||
|
*/
|
||||||
|
ExternalMetadata getExternalMetadata(final ParserRuleContext source) {
|
||||||
|
final ExternalMetadata sourceemd = externalMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ExtNodeMetadata and stores it in the extNodeMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The new ExtNodeMetadata.
|
||||||
|
*/
|
||||||
|
ExtNodeMetadata createExtNodeMetadata(final ParserRuleContext parent, final ParserRuleContext source) {
|
||||||
|
final ExtNodeMetadata sourceemd = new ExtNodeMetadata(parent, source);
|
||||||
|
extNodeMetadata.put(source, sourceemd);
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ExtNodeMetadata from the extNodeMetadata map.
|
||||||
|
* @param source The ANTLR node for this metadata.
|
||||||
|
* @return The retrieved ExtNodeMetadata.
|
||||||
|
*/
|
||||||
|
ExtNodeMetadata getExtNodeMetadata(final ParserRuleContext source) {
|
||||||
|
final ExtNodeMetadata sourceemd = extNodeMetadata.get(source);
|
||||||
|
|
||||||
|
if (sourceemd == null) {
|
||||||
|
throw new IllegalStateException(error(source) + "External metadata does not exist at" +
|
||||||
|
" the parse node with text [" + source.getText() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceemd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PlanAError class is used to throw internal errors caused by Plan A scripts that cannot be
|
||||||
|
* caught using a standard {@link Exception}. This prevents the user from catching this specific error
|
||||||
|
* (as Exceptions are available in the Plan A API, but Errors are not,) and possibly continuing to do
|
||||||
|
* something hazardous. The alternative was extending {@link Throwable}, but that seemed worse than using
|
||||||
|
* an {@link Error} in this case.
|
||||||
|
*/
|
||||||
|
public class PlanAError extends Error {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param message The error message.
|
||||||
|
*/
|
||||||
|
public PlanAError(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -116,6 +116,13 @@ class PlanAParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements P
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitEmpty(PlanAParser.EmptyContext ctx) { return visitChildren(ctx); }
|
@Override public T visitEmpty(PlanAParser.EmptyContext ctx) { return visitChildren(ctx); }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns the result of calling
|
||||||
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
|
*/
|
||||||
|
@Override public T visitEmptyscope(PlanAParser.EmptyscopeContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -151,6 +158,13 @@ class PlanAParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements P
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitDeclvar(PlanAParser.DeclvarContext ctx) { return visitChildren(ctx); }
|
@Override public T visitDeclvar(PlanAParser.DeclvarContext ctx) { return visitChildren(ctx); }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns the result of calling
|
||||||
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
|
*/
|
||||||
|
@Override public T visitTrap(PlanAParser.TrapContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -113,6 +113,12 @@ interface PlanAParserVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitEmpty(PlanAParser.EmptyContext ctx);
|
T visitEmpty(PlanAParser.EmptyContext ctx);
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by {@link PlanAParser#emptyscope}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
T visitEmptyscope(PlanAParser.EmptyscopeContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by {@link PlanAParser#initializer}.
|
* Visit a parse tree produced by {@link PlanAParser#initializer}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
@ -143,6 +149,12 @@ interface PlanAParserVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitDeclvar(PlanAParser.DeclvarContext ctx);
|
T visitDeclvar(PlanAParser.DeclvarContext ctx);
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by {@link PlanAParser#trap}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
T visitTrap(PlanAParser.TrapContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by the {@code comp}
|
* Visit a parse tree produced by the {@code comp}
|
||||||
* labeled alternative in {@link PlanAParser#expression}.
|
* labeled alternative in {@link PlanAParser#expression}.
|
||||||
|
|
|
@ -34,7 +34,7 @@ public final class PlanAPlugin extends Plugin {
|
||||||
return "Plan A scripting language for Elasticsearch";
|
return "Plan A scripting language for Elasticsearch";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onModule(ScriptModule module) {
|
public void onModule(final ScriptModule module) {
|
||||||
module.addScriptEngine(PlanAScriptEngineService.class);
|
module.addScriptEngine(PlanAScriptEngineService.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,117 +40,199 @@ import java.security.ProtectionDomain;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of a ScriptEngine for the Plan A language.
|
||||||
|
*/
|
||||||
public class PlanAScriptEngineService extends AbstractComponent implements ScriptEngineService {
|
public class PlanAScriptEngineService extends AbstractComponent implements ScriptEngineService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard name of the Plan A language.
|
||||||
|
*/
|
||||||
public static final String NAME = "plan-a";
|
public static final String NAME = "plan-a";
|
||||||
// default settings, used unless otherwise specified
|
|
||||||
|
/**
|
||||||
|
* Default compiler settings to be used.
|
||||||
|
*/
|
||||||
private static final CompilerSettings DEFAULT_COMPILER_SETTINGS = new CompilerSettings();
|
private static final CompilerSettings DEFAULT_COMPILER_SETTINGS = new CompilerSettings();
|
||||||
|
|
||||||
public static final String NUMERIC_OVERFLOW = "numeric_overflow";
|
/**
|
||||||
|
* Permissions context used during compilation.
|
||||||
// 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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -48,7 +48,7 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||||
|
|
||||||
/** Compiles and returns the result of {@code script} with access to {@code vars} */
|
/** Compiles and returns the result of {@code script} with access to {@code vars} */
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
return exec(script, vars, Collections.singletonMap(PlanAScriptEngineService.NUMERIC_OVERFLOW, Boolean.toString(random().nextBoolean())));
|
return exec(script, vars, Collections.singletonMap(CompilerSettings.NUMERIC_OVERFLOW, Boolean.toString(random().nextBoolean())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
|
/** Compiles and returns the result of {@code script} with access to {@code vars} and compile-time parameters */
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.plan.a;
|
package org.elasticsearch.plan.a;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public class WhenThingsGoWrongTests extends ScriptTestCase {
|
public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
|
@ -49,4 +50,83 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
|
||||||
assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter"));
|
assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInfiniteLoops() {
|
||||||
|
try {
|
||||||
|
exec("boolean x = true; while (x) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) {int y = 5}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) { boolean x = true; while (x) {} }");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("while (true) { boolean x = false; while (x) {} }");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("boolean x = true; for (;x;) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("for (;;) {int x = 5}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("def x = true; do {int y = 5;} while (x)");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("try { int x } catch (PlanAError error) {}");
|
||||||
|
fail("should have hit ParseException");
|
||||||
|
} catch (RuntimeException expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"unexpected token ['PlanAError'] was expecting one of [TYPE]."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoopLimits() {
|
||||||
|
exec("for (int x = 0; x < 9999; ++x) {}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec("for (int x = 0; x < 10000; ++x) {}");
|
||||||
|
fail("should have hit PlanAError");
|
||||||
|
} catch (PlanAError expected) {
|
||||||
|
assertTrue(expected.getMessage().contains(
|
||||||
|
"The maximum number of statements that can be executed in a loop has been reached."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue