From 6e6aa320a4e86fc61f97c3f68828b69886662415 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Dec 2019 15:11:48 -0800 Subject: [PATCH 01/12] removed scopes for functions to see used variables --- .../painless/DecorateExecutePass.java | 34 +++++++++++ .../elasticsearch/painless/antlr/Walker.java | 10 +++ .../painless/ir/FunctionNode.java | 61 +++++++++++++------ .../painless/node/SFunction.java | 16 +---- 4 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java new file mode 100644 index 0000000000000..d98ab127a7570 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless; + +import org.elasticsearch.painless.ir.ClassNode; +import org.elasticsearch.painless.symbol.ScriptRoot; + +public class DecorateExecutePass { + + public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { + + } + + protected DecorateExecutePass() { + + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 8b467e70b2701..4db0fcb4541ce 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -251,6 +251,16 @@ public ANode visitSource(SourceContext ctx) { // part of the overall class List statements = new ArrayList<>(); + // add gets methods as declarations available for the user as variables + for (int index = 0; index < scriptClassInfo.getGetMethods().size(); ++index) { + org.objectweb.asm.commons.Method method = scriptClassInfo.getGetMethods().get(index); + String name = method.getName().substring(3); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + statements.add(new SDeclaration(location(ctx), + new DResolvedType(location(ctx), scriptClassInfo.getGetReturns().get(index), false), name, null)); + } + for (StatementContext statement : ctx.statement()) { statements.add((AStatement)visit(statement)); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index fdc2dcc2c3d11..a628d259747b2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -22,6 +22,7 @@ import org.elasticsearch.painless.ClassWriter; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction; import org.elasticsearch.painless.symbol.ScopeTable; import org.elasticsearch.painless.symbol.ScopeTable.Variable; import org.elasticsearch.painless.symbol.ScriptRoot; @@ -31,6 +32,7 @@ import org.objectweb.asm.commons.Method; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; @@ -197,22 +199,47 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals if ("execute".equals(name)) { methodWriter.mark(startTry); - // convert gets methods to local variables from appropriate context - requires - // the gets method name be modified from "getExample" to "example" - // each gets method is then called and stored in the generated local variable - for (int getMethodIndex = 0; getMethodIndex < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++getMethodIndex) { - Method getMethod = scriptRoot.getScriptClassInfo().getGetMethods().get(getMethodIndex); - Class returnType = scriptRoot.getScriptClassInfo().getGetReturns().get(getMethodIndex); - - String name = getMethod.getName().substring(3); - name = Character.toLowerCase(name.charAt(0)) + name.substring(1); - - if (scriptRoot.getUsedVariables().contains(name)) { - Variable variable = scopeTable.defineVariable(returnType, name); - - methodWriter.loadThis(); - methodWriter.invokeVirtual(Type.getType(scriptRoot.getScriptClassInfo().getBaseClass()), getMethod); - methodWriter.visitVarInsn(getMethod.getReturnType().getOpcode(Opcodes.ISTORE), variable.getSlot()); + // convert gets methods to a new set of inserted ir nodes as necessary - + // requires the gets method name be modified from "getExample" to "example" + // if a get method variable isn't used it's declaration node is removed from + // the ir tree permanently so there is no frivolous variable slotting + int statementIndex = 0; + + while (statementIndex < blockNode.getStatementsNodes().size()) { + StatementNode statementNode = blockNode.getStatementsNodes().get(statementIndex); + + if (statementNode instanceof DeclarationNode) { + DeclarationNode declarationNode = (DeclarationNode)statementNode; + boolean isRemoved = false; + + for (int getIndex = 0; getIndex < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++getIndex) { + Class returnType = scriptRoot.getScriptClassInfo().getGetReturns().get(getIndex); + Method getMethod = scriptRoot.getScriptClassInfo().getGetMethods().get(getIndex); + String name = getMethod.getName().substring(3); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + if (name.equals(declarationNode.getName())) { + if (scriptRoot.getUsedVariables().contains(name)) { + UnboundCallNode unboundCallNode = new UnboundCallNode(); + unboundCallNode.setLocation(declarationNode.getLocation()); + unboundCallNode.setExpressionType(declarationNode.getDeclarationType()); + unboundCallNode.setLocalFunction(new LocalFunction( + getMethod.getName(), returnType, Collections.emptyList(), true, false)); + declarationNode.setExpressionNode(unboundCallNode); + } else { + blockNode.getStatementsNodes().remove(statementIndex); + isRemoved = true; + } + + break; + } + } + + if (isRemoved == false) { + ++statementIndex; + } + } else { + ++statementIndex; } } } @@ -228,7 +255,7 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals methodWriter.visitVarInsn(Opcodes.ISTORE, loop.getSlot()); } - blockNode.write(classWriter, methodWriter, globals, scopeTable.newScope()); + blockNode.write(classWriter, methodWriter, globals, scopeTable); if (doesMethodEscape == false) { if (returnType == void.class) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index c07a1430d4d3e..a45a2c4f4824a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -125,20 +125,6 @@ void analyze(ScriptRoot scriptRoot) { functionScope.defineVariable(location, typeParameter, parameterName, false); } - // TODO: do not specialize for execute - // TODO: https://github.com/elastic/elasticsearch/issues/51841 - if ("execute".equals(name)) { - for (int get = 0; get < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++get) { - org.objectweb.asm.commons.Method method = scriptRoot.getScriptClassInfo().getGetMethods().get(get); - String name = method.getName().substring(3); - name = Character.toLowerCase(name.charAt(0)) + name.substring(1); - - Class rtn = scriptRoot.getScriptClassInfo().getGetReturns().get(get); - functionScope.defineVariable(new Location("getter [" + name + "]", 0), rtn, name, true); - } - } - // TODO: end - maxLoopCounter = scriptRoot.getCompilerSettings().getMaxLoopCounter(); if (block.statements.isEmpty()) { @@ -146,7 +132,7 @@ void analyze(ScriptRoot scriptRoot) { } block.lastSource = true; - block.analyze(scriptRoot, functionScope.newLocalScope()); + block.analyze(scriptRoot, functionScope); methodEscape = block.methodEscape; if (methodEscape == false && isAutoReturnEnabled == false && returnType != void.class) { From 0f8f2db1681620b7a4ff8b4fb6f1a5c85cde3c7b Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Dec 2019 16:20:18 -0800 Subject: [PATCH 02/12] change scoping to track variables in all scopes --- .../org/elasticsearch/painless/Compiler.java | 4 +- .../painless/DecorateExecutePass.java | 72 ++++++++++++++++++- .../org/elasticsearch/painless/Scope.java | 26 +++++-- .../painless/ir/FunctionNode.java | 48 +------------ .../painless/node/SFunction.java | 2 +- 5 files changed, 94 insertions(+), 58 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 82429f2ac2d98..76cfd6fd010e7 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -211,6 +211,7 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null); ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); + DecorateExecutePass.pass(scriptRoot, classNode); Map statics = classNode.write(); try { @@ -240,8 +241,9 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s byte[] compile(String name, String source, CompilerSettings settings, Printer debugStream) { ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass); SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, debugStream); - root.analyze(painlessLookup, settings); + ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); + DecorateExecutePass.pass(scriptRoot, classNode); classNode.write(); return classNode.getBytes(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java index d98ab127a7570..9d26d58481062 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java @@ -19,16 +19,84 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.ir.BlockNode; import org.elasticsearch.painless.ir.ClassNode; +import org.elasticsearch.painless.ir.DeclarationNode; +import org.elasticsearch.painless.ir.FunctionNode; +import org.elasticsearch.painless.ir.StatementNode; +import org.elasticsearch.painless.ir.UnboundCallNode; +import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction; import org.elasticsearch.painless.symbol.ScriptRoot; +import org.objectweb.asm.commons.Method; + +import java.util.Collections; public class DecorateExecutePass { public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { - + FunctionNode executeFunctionNode = null; + + for (FunctionNode functionNode : classNode.getFunctionsNodes()) { + if ("execute".equals(functionNode.getName())) { + executeFunctionNode = functionNode; + break; + } + } + + BlockNode blockNode = executeFunctionNode.getBlockNode(); + + // convert gets methods to a new set of inserted ir nodes as necessary - + // requires the gets method name be modified from "getExample" to "example" + // if a get method variable isn't used it's declaration node is removed from + // the ir tree permanently so there is no frivolous variable slotting + int statementIndex = 0; + + while (statementIndex < blockNode.getStatementsNodes().size()) { + StatementNode statementNode = blockNode.getStatementsNodes().get(statementIndex); + + if (statementNode instanceof DeclarationNode) { + DeclarationNode declarationNode = (DeclarationNode) statementNode; + boolean isRemoved = false; + + for (int getIndex = 0; getIndex < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++getIndex) { + Class returnType = scriptRoot.getScriptClassInfo().getGetReturns().get(getIndex); + Method getMethod = scriptRoot.getScriptClassInfo().getGetMethods().get(getIndex); + String name = getMethod.getName().substring(3); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + if (name.equals(declarationNode.getName())) { + if (scriptRoot.getUsedVariables().contains(name)) { + UnboundCallNode unboundCallNode = new UnboundCallNode(); + unboundCallNode.setLocation(declarationNode.getLocation()); + unboundCallNode.setExpressionType(declarationNode.getDeclarationType()); + unboundCallNode.setLocalFunction(new LocalFunction( + getMethod.getName(), returnType, Collections.emptyList(), true, false)); + declarationNode.setExpressionNode(unboundCallNode); + } else { + blockNode.getStatementsNodes().remove(statementIndex); + isRemoved = true; + } + + break; + } + } + + if (isRemoved == false) { + ++statementIndex; + } + } else { + ++statementIndex; + } + } + + /*blockNode = new BlockNode() + .addStatementNode(new TryNode()) + .setLocation(blockNode.getLocation()) + .setAllEscape(blockNode.doAllEscape()) + .setStatementCount(blockNode.getStatementCount());*/ } protected DecorateExecutePass() { - + // do nothing } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Scope.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Scope.java index 0ff6237cb98e6..8e0b00be01901 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Scope.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Scope.java @@ -87,9 +87,9 @@ public boolean isFinal() { public static class FunctionScope extends Scope { protected final Class returnType; - protected final Set usedVariables = new HashSet<>(); public FunctionScope(Class returnType) { + super(new HashSet<>()); this.returnType = Objects.requireNonNull(returnType); } @@ -128,10 +128,6 @@ public Class getReturnType() { public String getReturnCanonicalTypeName() { return PainlessLookupUtility.typeToCanonicalTypeName(returnType); } - - public Set getUsedVariables() { - return Collections.unmodifiableSet(usedVariables); - } } /** @@ -150,6 +146,7 @@ public static class LambdaScope extends Scope { protected final Set captures = new HashSet<>(); protected LambdaScope(Scope parent, Class returnType) { + super(parent.usedVariables); this.parent = parent; this.returnType = returnType; } @@ -180,6 +177,8 @@ public Variable getVariable(Location location, String name) { variable = parent.getVariable(location, name); variable = new Variable(variable.getType(), variable.getName(), true); captures.add(variable); + } else { + usedVariables.add(name); } return variable; @@ -213,6 +212,7 @@ public static class BlockScope extends Scope { protected final Scope parent; protected BlockScope(Scope parent) { + super(parent.usedVariables); this.parent = parent; } @@ -238,6 +238,8 @@ public Variable getVariable(Location location, String name) { if (variable == null) { variable = parent.getVariable(location, name); + } else { + usedVariables.add(name); } return variable; @@ -263,9 +265,10 @@ public static FunctionScope newFunctionScope(Class returnType) { } protected final Map variables = new HashMap<>(); + protected final Set usedVariables; - protected Scope() { - // do nothing + protected Scope(Set usedVariables) { + this.usedVariables = usedVariables; } /** @@ -312,4 +315,13 @@ public boolean isInternalVariableDefined(String name) { public Variable getInternalVariable(Location location, String name) { return getVariable(location, "#" + name); } + + /** + * Returns the set of variables used within a top-level {@link FunctionScope} + * including local variables no longer in scope upon completion of writing a + * function to ASM bytecode. + */ + public Set getUsedVariables() { + return Collections.unmodifiableSet(usedVariables); + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index a628d259747b2..c30d135a38644 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -22,7 +22,6 @@ import org.elasticsearch.painless.ClassWriter; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction; import org.elasticsearch.painless.symbol.ScopeTable; import org.elasticsearch.painless.symbol.ScopeTable.Variable; import org.elasticsearch.painless.symbol.ScriptRoot; @@ -32,7 +31,6 @@ import org.objectweb.asm.commons.Method; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; @@ -198,50 +196,6 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals if ("execute".equals(name)) { methodWriter.mark(startTry); - - // convert gets methods to a new set of inserted ir nodes as necessary - - // requires the gets method name be modified from "getExample" to "example" - // if a get method variable isn't used it's declaration node is removed from - // the ir tree permanently so there is no frivolous variable slotting - int statementIndex = 0; - - while (statementIndex < blockNode.getStatementsNodes().size()) { - StatementNode statementNode = blockNode.getStatementsNodes().get(statementIndex); - - if (statementNode instanceof DeclarationNode) { - DeclarationNode declarationNode = (DeclarationNode)statementNode; - boolean isRemoved = false; - - for (int getIndex = 0; getIndex < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++getIndex) { - Class returnType = scriptRoot.getScriptClassInfo().getGetReturns().get(getIndex); - Method getMethod = scriptRoot.getScriptClassInfo().getGetMethods().get(getIndex); - String name = getMethod.getName().substring(3); - name = Character.toLowerCase(name.charAt(0)) + name.substring(1); - - if (name.equals(declarationNode.getName())) { - if (scriptRoot.getUsedVariables().contains(name)) { - UnboundCallNode unboundCallNode = new UnboundCallNode(); - unboundCallNode.setLocation(declarationNode.getLocation()); - unboundCallNode.setExpressionType(declarationNode.getDeclarationType()); - unboundCallNode.setLocalFunction(new LocalFunction( - getMethod.getName(), returnType, Collections.emptyList(), true, false)); - declarationNode.setExpressionNode(unboundCallNode); - } else { - blockNode.getStatementsNodes().remove(statementIndex); - isRemoved = true; - } - - break; - } - } - - if (isRemoved == false) { - ++statementIndex; - } - } else { - ++statementIndex; - } - } } // TODO: end @@ -255,7 +209,7 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals methodWriter.visitVarInsn(Opcodes.ISTORE, loop.getSlot()); } - blockNode.write(classWriter, methodWriter, globals, scopeTable); + blockNode.write(classWriter, methodWriter, globals, scopeTable.newScope()); if (doesMethodEscape == false) { if (returnType == void.class) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index a45a2c4f4824a..98edd4b4b62be 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -132,7 +132,7 @@ void analyze(ScriptRoot scriptRoot) { } block.lastSource = true; - block.analyze(scriptRoot, functionScope); + block.analyze(scriptRoot, functionScope.newLocalScope()); methodEscape = block.methodEscape; if (methodEscape == false && isAutoReturnEnabled == false && returnType != void.class) { From 92281afdd77398b9928520621d4410a1cc0d0c92 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Dec 2019 17:05:47 -0800 Subject: [PATCH 03/12] partially removed execute method exceptino customization --- .../painless/DecorateExecutePass.java | 87 ++++++++++++++++++- .../painless/ir/FunctionNode.java | 5 +- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java index 9d26d58481062..61a557ec0343c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java @@ -90,10 +90,93 @@ public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { } /*blockNode = new BlockNode() - .addStatementNode(new TryNode()) + .addStatementNode(new TryNode() + .setBlockNode(blockNode) + .addCatchNode(new CatchNode() + .setDeclarationNode(new DeclarationNode() + .setDeclarationTypeNode(new TypeNode() + .setLocation(blockNode.getLocation()) + .setType(PainlessExplainError.class) + ) + .setName("exception") + .setLocation(blockNode.getLocation()) + ) + .setBlockNode(new BlockNode() + .addStatementNode(new ThrowNode() + .setExpressionNode(new UnboundCallNode() + .setTypeNode(new TypeNode() + .setLocation(blockNode.getLocation()) + .setType(ScriptException.class) + ) + .addArgumentNode(new VariableNode() + .setTypeNode(new TypeNode() + .setLocation(blockNode.getLocation()) + .setType(ScriptException.class) + ) + ) + .setLocalFunction(new LocalFunction( + "convertToScriptException", + ScriptException.class, + Arrays.asList(Throwable.class, Map.class), + true, + false + ) + ) + .setLocation(blockNode.getLocation()) + ) + .setLocation(blockNode.getLocation()) + ) + .setLocation(blockNode.getLocation()) + .setAllEscape(true) + .setStatementCount(1) + ) + .setLocation(blockNode.getLocation()) + ) + .setLocation(blockNode.getLocation()) + ) .setLocation(blockNode.getLocation()) .setAllEscape(blockNode.doAllEscape()) - .setStatementCount(blockNode.getStatementCount());*/ + .setStatementCount(blockNode.getStatementCount()); + + executeFunctionNode.setBlockNode(blockNode);*/ + + /* + if ("execute".equals(name)) { + methodWriter.mark(endTry); + methodWriter.goTo(endCatch); + // This looks like: + // } catch (PainlessExplainError e) { + // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) + // } + methodWriter.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName()); + methodWriter.mark(startExplainCatch); + methodWriter.loadThis(); + methodWriter.swap(); + methodWriter.dup(); + methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); + methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); + methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); + methodWriter.throwException(); + // This looks like: + // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { + // throw this.convertToScriptException(e, e.getHeaders()) + // } + // We *think* it is ok to catch OutOfMemoryError and StackOverflowError because Painless is stateless + methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, PAINLESS_ERROR_TYPE.getInternalName()); + methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, BOOTSTRAP_METHOD_ERROR_TYPE.getInternalName()); + methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, OUT_OF_MEMORY_ERROR_TYPE.getInternalName()); + methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, STACK_OVERFLOW_ERROR_TYPE.getInternalName()); + methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, EXCEPTION_TYPE.getInternalName()); + methodWriter.mark(startOtherCatch); + methodWriter.loadThis(); + methodWriter.swap(); + methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); + methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); + methodWriter.throwException(); + methodWriter.mark(endCatch); + } + // TODO: end + */ } protected DecorateExecutePass() { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index c30d135a38644..485c6b37630b7 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -33,7 +33,6 @@ import java.util.ArrayList; import java.util.List; -import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; import static org.elasticsearch.painless.WriterConstants.BOOTSTRAP_METHOD_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; import static org.elasticsearch.painless.WriterConstants.COLLECTIONS_TYPE; @@ -252,7 +251,7 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals methodWriter.dup(); methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); - methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); + methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); methodWriter.throwException(); // This looks like: // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { @@ -268,7 +267,7 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals methodWriter.loadThis(); methodWriter.swap(); methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); - methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); + methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); methodWriter.throwException(); methodWriter.mark(endCatch); } From d1dfeda725afc5bc5e615d9aa0d99492923f929b Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Jan 2020 11:38:30 -0800 Subject: [PATCH 04/12] move autoreturn out of ir nodes --- .../painless/ir/FunctionNode.java | 43 -------------- .../elasticsearch/painless/node/ELambda.java | 6 +- .../painless/node/SFunction.java | 59 ++++++++++++++++++- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index 485c6b37630b7..8dd30ab2ceb6c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -69,8 +69,6 @@ public BlockNode getBlockNode() { private List parameterNames = new ArrayList<>(); private boolean isStatic; private boolean isSynthetic; - private boolean isAutoReturnEnabled; - private boolean doesMethodEscape; private int maxLoopCounter; public void setScriptRoot(ScriptRoot scriptRoot) { @@ -129,22 +127,6 @@ public boolean isSynthetic() { return isSynthetic; } - public void setAutoReturnEnabled(boolean isAutoReturnEnabled) { - this.isAutoReturnEnabled = isAutoReturnEnabled; - } - - public boolean isAutoReturnEnabled() { - return isAutoReturnEnabled; - } - - public void setMethodEscape(boolean doesMethodEscape) { - this.doesMethodEscape = doesMethodEscape; - } - - public boolean doesMethodEscape() { - return doesMethodEscape; - } - public void setMaxLoopCounter(int maxLoopCounter) { this.maxLoopCounter = maxLoopCounter; } @@ -210,31 +192,6 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals blockNode.write(classWriter, methodWriter, globals, scopeTable.newScope()); - if (doesMethodEscape == false) { - if (returnType == void.class) { - methodWriter.returnValue(); - } else if (isAutoReturnEnabled) { - if (returnType == boolean.class) { - methodWriter.push(false); - } else if (returnType == byte.class || returnType == char.class || returnType == short.class || returnType == int.class) { - methodWriter.push(0); - } else if (returnType == long.class) { - methodWriter.push(0L); - } else if (returnType == float.class) { - methodWriter.push(0f); - } else if (returnType == double.class) { - methodWriter.push(0d); - } else { - methodWriter.visitInsn(Opcodes.ACONST_NULL); - } - - methodWriter.returnValue(); - } else { - throw getLocation().createError(new IllegalStateException("not all paths provide a return value " + - "for function [" + name + "] with [" + typeParameters.size() + "] parameters")); - } - } - // TODO: do not specialize for execute // TODO: https://github.com/elastic/elasticsearch/issues/51841 if ("execute".equals(name)) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index 757f59175ffe8..2e93d50df52e2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -166,9 +166,7 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { block.analyze(scriptRoot, lambdaScope); captures = new ArrayList<>(lambdaScope.getCaptures()); - methodEscape = block.methodEscape; - - if (methodEscape == false) { + if (block.methodEscape == false) { throw createError(new IllegalArgumentException("not all paths return a value for lambda")); } @@ -214,8 +212,6 @@ LambdaNode write(ClassNode classNode) { functionNode.getParameterNames().addAll(parameterNames); functionNode.setStatic(true); functionNode.setSynthetic(true); - functionNode.setAutoReturnEnabled(false); - functionNode.setMethodEscape(methodEscape); functionNode.setMaxLoopCounter(maxLoopCounter); classNode.addFunctionNode(functionNode); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index 98edd4b4b62be..f71b7fbb8f538 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -21,8 +21,13 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Scope.FunctionScope; +import org.elasticsearch.painless.ir.BlockNode; import org.elasticsearch.painless.ir.ClassNode; +import org.elasticsearch.painless.ir.ConstantNode; +import org.elasticsearch.painless.ir.ExpressionNode; import org.elasticsearch.painless.ir.FunctionNode; +import org.elasticsearch.painless.ir.NullNode; +import org.elasticsearch.painless.ir.ReturnNode; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.symbol.ScriptRoot; @@ -150,9 +155,59 @@ void analyze(ScriptRoot scriptRoot) { @Override public FunctionNode write(ClassNode classNode) { + BlockNode blockNode = block.write(classNode); + + if (methodEscape == false) { + ExpressionNode expressionNode; + + if (returnType == void.class) { + expressionNode = null; + } else if (isAutoReturnEnabled) { + if (returnType.isPrimitive()) { + ConstantNode constantNode = new ConstantNode(); + constantNode.setLocation(location); + constantNode.setExpressionType(returnType); + + if (returnType == boolean.class) { + constantNode.setConstant(false); + } else if (returnType == byte.class + || returnType == char.class + || returnType == short.class + || returnType == int.class) { + constantNode.setConstant(0); + } else if (returnType == long.class) { + constantNode.setConstant(0L); + } else if (returnType == float.class) { + constantNode.setConstant(0f); + } else if (returnType == double.class) { + constantNode.setConstant(0d); + } else { + throw createError(new IllegalStateException("unexpected automatic return type " + + "[" + PainlessLookupUtility.typeToCanonicalTypeName(returnType) + "] " + + "for function [" + name + "] with [" + typeParameters.size() + "] parameters")); + } + + expressionNode = constantNode; + } else { + expressionNode = new NullNode(); + expressionNode.setLocation(location); + expressionNode.setExpressionType(returnType); + } + } else { + throw createError(new IllegalStateException("not all paths provide a return value " + + "for function [" + name + "] with [" + typeParameters.size() + "] parameters")); + } + + ReturnNode returnNode = new ReturnNode(); + returnNode.setLocation(location); + returnNode.setExpressionNode(expressionNode); + + blockNode.addStatementNode(returnNode); + } + FunctionNode functionNode = new FunctionNode(); - functionNode.setBlockNode(block.write(classNode)); + functionNode.setBlockNode(blockNode); functionNode.setLocation(location); functionNode.setScriptRoot(scriptRoot); @@ -162,8 +217,6 @@ public FunctionNode write(ClassNode classNode) { functionNode.getParameterNames().addAll(paramNameStrs); functionNode.setStatic(isStatic); functionNode.setSynthetic(synthetic); - functionNode.setAutoReturnEnabled(isAutoReturnEnabled); - functionNode.setMethodEscape(methodEscape); functionNode.setMaxLoopCounter(maxLoopCounter); return functionNode; From 65147708c75fc0c16023c7576b2aa9ba52b1980a Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Jan 2020 15:30:09 -0800 Subject: [PATCH 05/12] add es specific exceptions in decorate execute phase --- .../painless/DecorateExecutePass.java | 254 ++++++++++++++---- .../elasticsearch/painless/MethodWriter.java | 2 +- .../elasticsearch/painless/antlr/Walker.java | 6 +- .../painless/ir/DeclarationNode.java | 37 ++- .../painless/ir/FunctionNode.java | 105 ++++---- .../painless/ir/UnboundCallNode.java | 5 +- .../painless/ir/UnboundFieldNode.java | 69 +++++ .../painless/node/SDeclaration.java | 5 +- 8 files changed, 355 insertions(+), 128 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java index 61a557ec0343c..365c627064d63 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java @@ -20,22 +20,36 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.ir.BlockNode; +import org.elasticsearch.painless.ir.CallNode; +import org.elasticsearch.painless.ir.CallSubNode; +import org.elasticsearch.painless.ir.CatchNode; import org.elasticsearch.painless.ir.ClassNode; import org.elasticsearch.painless.ir.DeclarationNode; import org.elasticsearch.painless.ir.FunctionNode; import org.elasticsearch.painless.ir.StatementNode; +import org.elasticsearch.painless.ir.StaticNode; +import org.elasticsearch.painless.ir.ThrowNode; +import org.elasticsearch.painless.ir.TryNode; import org.elasticsearch.painless.ir.UnboundCallNode; +import org.elasticsearch.painless.ir.UnboundFieldNode; +import org.elasticsearch.painless.ir.VariableNode; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction; import org.elasticsearch.painless.symbol.ScriptRoot; +import org.elasticsearch.script.ScriptException; import org.objectweb.asm.commons.Method; +import java.util.Arrays; import java.util.Collections; +import java.util.Map; public class DecorateExecutePass { public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { FunctionNode executeFunctionNode = null; + // look up the execute method for decoration for (FunctionNode functionNode : classNode.getFunctionsNodes()) { if ("execute".equals(functionNode.getName())) { executeFunctionNode = functionNode; @@ -55,7 +69,7 @@ public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { StatementNode statementNode = blockNode.getStatementsNodes().get(statementIndex); if (statementNode instanceof DeclarationNode) { - DeclarationNode declarationNode = (DeclarationNode) statementNode; + DeclarationNode declarationNode = (DeclarationNode)statementNode; boolean isRemoved = false; for (int getIndex = 0; getIndex < scriptRoot.getScriptClassInfo().getGetMethods().size(); ++getIndex) { @@ -89,56 +103,198 @@ public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { } } - /*blockNode = new BlockNode() - .addStatementNode(new TryNode() - .setBlockNode(blockNode) - .addCatchNode(new CatchNode() - .setDeclarationNode(new DeclarationNode() - .setDeclarationTypeNode(new TypeNode() - .setLocation(blockNode.getLocation()) - .setType(PainlessExplainError.class) - ) - .setName("exception") - .setLocation(blockNode.getLocation()) - ) - .setBlockNode(new BlockNode() - .addStatementNode(new ThrowNode() - .setExpressionNode(new UnboundCallNode() - .setTypeNode(new TypeNode() - .setLocation(blockNode.getLocation()) - .setType(ScriptException.class) - ) - .addArgumentNode(new VariableNode() - .setTypeNode(new TypeNode() - .setLocation(blockNode.getLocation()) - .setType(ScriptException.class) - ) - ) - .setLocalFunction(new LocalFunction( - "convertToScriptException", - ScriptException.class, - Arrays.asList(Throwable.class, Map.class), - true, - false - ) - ) - .setLocation(blockNode.getLocation()) - ) - .setLocation(blockNode.getLocation()) - ) - .setLocation(blockNode.getLocation()) - .setAllEscape(true) - .setStatementCount(1) - ) - .setLocation(blockNode.getLocation()) + // decorate the execute method with nodes to wrap the user statements with + // the sandboxed errors as follows: + // } catch (PainlessExplainError e) { + // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) + // } + // and + // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { + // throw this.convertToScriptException(e, e.getHeaders()) + // } + try { + Location internalLocation = new Location("", 0); + + TryNode tryNode = new TryNode(); + tryNode.setLocation(internalLocation); + tryNode.setBlockNode(blockNode); + + CatchNode catchNode = new CatchNode(); + catchNode.setLocation(internalLocation); + + tryNode.addCatchNode(catchNode); + + DeclarationNode declarationNode = new DeclarationNode(); + declarationNode.setLocation(internalLocation); + declarationNode.setDeclarationType(PainlessExplainError.class); + declarationNode.setName("#painlessExplainError"); + declarationNode.setRequiresDefault(false); + + catchNode.setDeclarationNode(declarationNode); + + BlockNode catchBlockNode = new BlockNode(); + catchBlockNode.setLocation(internalLocation); + catchBlockNode.setAllEscape(true); + catchBlockNode.setStatementCount(1); + + catchNode.setBlockNode(catchBlockNode); + + ThrowNode throwNode = new ThrowNode(); + throwNode.setLocation(internalLocation); + + catchBlockNode.addStatementNode(throwNode); + + UnboundCallNode unboundCallNode = new UnboundCallNode(); + unboundCallNode.setLocation(internalLocation); + unboundCallNode.setExpressionType(ScriptException.class); + unboundCallNode.setLocalFunction(new LocalFunction( + "convertToScriptException", + ScriptException.class, + Arrays.asList(Throwable.class, Map.class), + true, + false + ) + ); + + throwNode.setExpressionNode(unboundCallNode); + + VariableNode variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(ScriptException.class); + variableNode.setName("#painlessExplainError"); + + unboundCallNode.addArgumentNode(variableNode); + + CallNode callNode = new CallNode(); + callNode.setLocation(internalLocation); + callNode.setExpressionType(Map.class); + + unboundCallNode.addArgumentNode(callNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(PainlessExplainError.class); + variableNode.setName("#painlessExplainError"); + + callNode.setLeftNode(variableNode); + + CallSubNode callSubNode = new CallSubNode(); + callSubNode.setLocation(internalLocation); + callSubNode.setExpressionType(Map.class); + callSubNode.setBox(PainlessExplainError.class); + callSubNode.setMethod(new PainlessMethod( + PainlessExplainError.class.getMethod( + "getHeaders", + PainlessLookup.class), + PainlessExplainError.class, + null, + Collections.emptyList(), + null, + null, + null + )); + + callNode.setRightNode(callSubNode); + + UnboundFieldNode unboundFieldNode = new UnboundFieldNode(); + unboundFieldNode.setLocation(internalLocation); + unboundFieldNode.setExpressionType(PainlessLookup.class); + unboundFieldNode.setName("$DEFINITION"); + unboundFieldNode.setStatic(true); + + callSubNode.addArgumentNode(unboundFieldNode); + + for (Class throwable : new Class[] { + PainlessError.class, BootstrapMethodError.class, OutOfMemoryError.class, StackOverflowError.class, Exception.class}) { + + String name = throwable.getSimpleName(); + name = "#" + Character.toLowerCase(name.charAt(0)) + name.substring(1); + + catchNode = new CatchNode(); + catchNode.setLocation(internalLocation); + + tryNode.addCatchNode(catchNode); + + declarationNode = new DeclarationNode(); + declarationNode.setLocation(internalLocation); + declarationNode.setDeclarationType(throwable); + declarationNode.setName(name); + declarationNode.setRequiresDefault(false); + + catchNode.setDeclarationNode(declarationNode); + + catchBlockNode = new BlockNode(); + catchBlockNode.setLocation(internalLocation); + catchBlockNode.setAllEscape(true); + catchBlockNode.setStatementCount(1); + + catchNode.setBlockNode(catchBlockNode); + + throwNode = new ThrowNode(); + throwNode.setLocation(internalLocation); + + catchBlockNode.addStatementNode(throwNode); + + unboundCallNode = new UnboundCallNode(); + unboundCallNode.setLocation(internalLocation); + unboundCallNode.setExpressionType(ScriptException.class); + unboundCallNode.setLocalFunction(new LocalFunction( + "convertToScriptException", + ScriptException.class, + Arrays.asList(Throwable.class, Map.class), + true, + false ) - .setLocation(blockNode.getLocation()) - ) - .setLocation(blockNode.getLocation()) - .setAllEscape(blockNode.doAllEscape()) - .setStatementCount(blockNode.getStatementCount()); + ); + + throwNode.setExpressionNode(unboundCallNode); - executeFunctionNode.setBlockNode(blockNode);*/ + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(ScriptException.class); + variableNode.setName(name); + + unboundCallNode.addArgumentNode(variableNode); + + callNode = new CallNode(); + callNode.setLocation(internalLocation); + callNode.setExpressionType(Map.class); + + unboundCallNode.addArgumentNode(callNode); + + StaticNode staticNode = new StaticNode(); + staticNode.setLocation(internalLocation); + staticNode.setExpressionType(Collections.class); + + callNode.setLeftNode(staticNode); + + callSubNode = new CallSubNode(); + callSubNode.setLocation(internalLocation); + callSubNode.setExpressionType(Map.class); + callSubNode.setBox(Collections.class); + callSubNode.setMethod(new PainlessMethod( + Collections.class.getMethod("emptyMap"), + Collections.class, + null, + Collections.emptyList(), + null, + null, + null + )); + + callNode.setRightNode(callSubNode); + } + + blockNode = new BlockNode(); + blockNode.setLocation(blockNode.getLocation()); + blockNode.setAllEscape(blockNode.doAllEscape()); + blockNode.setStatementCount(blockNode.getStatementCount()); + blockNode.addStatementNode(tryNode); + + executeFunctionNode.setBlockNode(blockNode); + } catch (Exception exception) { + throw new RuntimeException(exception); + } /* if ("execute".equals(name)) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java index 5cb385a211521..ea7e23ee75c3d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java @@ -502,7 +502,7 @@ public void invokeMethodCall(PainlessMethod painlessMethod) { // method since java 8 did not check, but java 9 and 10 do if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) { visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(), - painlessMethod.javaMethod.getName(), painlessMethod.methodType.toMethodDescriptorString(), true); + painlessMethod.javaMethod.getName(), method.getDescriptor(), true); } else { invokeStatic(type, method); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 4db0fcb4541ce..66155bda3f10b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -258,7 +258,7 @@ public ANode visitSource(SourceContext ctx) { name = Character.toLowerCase(name.charAt(0)) + name.substring(1); statements.add(new SDeclaration(location(ctx), - new DResolvedType(location(ctx), scriptClassInfo.getGetReturns().get(index), false), name, null)); + new DResolvedType(location(ctx), scriptClassInfo.getGetReturns().get(index), false), name, false, null)); } for (StatementContext statement : ctx.statement()) { @@ -515,7 +515,7 @@ public ANode visitDeclaration(DeclarationContext ctx) { AExpression expression = declvar.expression() == null ? null : (AExpression)visit(declvar.expression()); DUnresolvedType unresolvedType = new DUnresolvedType(location(declvar), type); - declarations.add(new SDeclaration(location(declvar), unresolvedType, name, expression)); + declarations.add(new SDeclaration(location(declvar), unresolvedType, name, true, expression)); } return new SDeclBlock(location(ctx), declarations); @@ -538,7 +538,7 @@ public ANode visitTrap(TrapContext ctx) { SBlock block = (SBlock)visit(ctx.block()); return new SCatch(location(ctx), new DResolvedType(location(ctx), Exception.class), - new SDeclaration(location(ctx.TYPE()), new DUnresolvedType(location(ctx.TYPE()), type), name, null), block); + new SDeclaration(location(ctx.TYPE()), new DUnresolvedType(location(ctx.TYPE()), type), name, false, null), block); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/DeclarationNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/DeclarationNode.java index 2e9ad4dfc6071..7c62847697d95 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/DeclarationNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/DeclarationNode.java @@ -45,6 +45,7 @@ public ExpressionNode getExpressionNode() { protected String name; protected Class declarationType; + protected boolean requiresDefault; public void setName(String name) { this.name = name; @@ -66,6 +67,14 @@ public String getDeclarationCanonicalTypeName() { return PainlessLookupUtility.typeToCanonicalTypeName(declarationType); } + public void setRequiresDefault(boolean requiresDefault) { + this.requiresDefault = requiresDefault; + } + + public boolean requiresDefault() { + return requiresDefault; + } + /* ---- end node data ---- */ @Override @@ -75,19 +84,21 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals Variable variable = scopeTable.defineVariable(declarationType, name); if (expressionNode == null) { - Class sort = variable.getType(); - - if (sort == void.class || sort == boolean.class || sort == byte.class || - sort == short.class || sort == char.class || sort == int.class) { - methodWriter.push(0); - } else if (sort == long.class) { - methodWriter.push(0L); - } else if (sort == float.class) { - methodWriter.push(0F); - } else if (sort == double.class) { - methodWriter.push(0D); - } else { - methodWriter.visitInsn(Opcodes.ACONST_NULL); + if (requiresDefault) { + Class sort = variable.getType(); + + if (sort == void.class || sort == boolean.class || sort == byte.class || + sort == short.class || sort == char.class || sort == int.class) { + methodWriter.push(0); + } else if (sort == long.class) { + methodWriter.push(0L); + } else if (sort == float.class) { + methodWriter.push(0F); + } else if (sort == double.class) { + methodWriter.push(0D); + } else { + methodWriter.visitInsn(Opcodes.ACONST_NULL); + } } } else { expressionNode.write(classWriter, methodWriter, globals, scopeTable); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index 8dd30ab2ceb6c..b04b7a7069a0e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -25,7 +25,6 @@ import org.elasticsearch.painless.symbol.ScopeTable; import org.elasticsearch.painless.symbol.ScopeTable.Variable; import org.elasticsearch.painless.symbol.ScriptRoot; -import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; @@ -33,19 +32,6 @@ import java.util.ArrayList; import java.util.List; -import static org.elasticsearch.painless.WriterConstants.BOOTSTRAP_METHOD_ERROR_TYPE; -import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -import static org.elasticsearch.painless.WriterConstants.COLLECTIONS_TYPE; -import static org.elasticsearch.painless.WriterConstants.CONVERT_TO_SCRIPT_EXCEPTION_METHOD; -import static org.elasticsearch.painless.WriterConstants.DEFINITION_TYPE; -import static org.elasticsearch.painless.WriterConstants.EMPTY_MAP_METHOD; -import static org.elasticsearch.painless.WriterConstants.EXCEPTION_TYPE; -import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE; -import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; -import static org.elasticsearch.painless.WriterConstants.PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD; -import static org.elasticsearch.painless.WriterConstants.PAINLESS_EXPLAIN_ERROR_TYPE; -import static org.elasticsearch.painless.WriterConstants.STACK_OVERFLOW_ERROR_TYPE; - public class FunctionNode extends IRNode { /* ---- begin tree structure ---- */ @@ -168,17 +154,16 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals // TODO: do not specialize for execute // TODO: https://github.com/elastic/elasticsearch/issues/51841 - // create labels for the potential try/catch blocks in "execute" - Label startTry = new Label(); - Label endTry = new Label(); - Label startExplainCatch = new Label(); - Label startOtherCatch = new Label(); - Label endCatch = new Label(); - - if ("execute".equals(name)) { - methodWriter.mark(startTry); - } - // TODO: end +// Label startTry = new Label(); +// Label endTry = new Label(); +// Label startExplainCatch = new Label(); +// Label startOtherCatch = new Label(); +// Label endCatch = new Label(); +// +// if ("execute".equals(name)) { +// methodWriter.mark(startTry); +// } +// // TODO: end if (maxLoopCounter > 0) { // if there is infinite loop protection, we do this once: @@ -194,41 +179,41 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals // TODO: do not specialize for execute // TODO: https://github.com/elastic/elasticsearch/issues/51841 - if ("execute".equals(name)) { - methodWriter.mark(endTry); - methodWriter.goTo(endCatch); - // This looks like: - // } catch (PainlessExplainError e) { - // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) - // } - methodWriter.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName()); - methodWriter.mark(startExplainCatch); - methodWriter.loadThis(); - methodWriter.swap(); - methodWriter.dup(); - methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); - methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); - methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); - methodWriter.throwException(); - // This looks like: - // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { - // throw this.convertToScriptException(e, e.getHeaders()) - // } - // We *think* it is ok to catch OutOfMemoryError and StackOverflowError because Painless is stateless - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, PAINLESS_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, BOOTSTRAP_METHOD_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, OUT_OF_MEMORY_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, STACK_OVERFLOW_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, EXCEPTION_TYPE.getInternalName()); - methodWriter.mark(startOtherCatch); - methodWriter.loadThis(); - methodWriter.swap(); - methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); - methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); - methodWriter.throwException(); - methodWriter.mark(endCatch); - } - // TODO: end +// if ("execute".equals(name)) { +// methodWriter.mark(endTry); +// methodWriter.goTo(endCatch); +// // This looks like: +// // } catch (PainlessExplainError e) { +// // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) +// // } +// methodWriter.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName()); +// methodWriter.mark(startExplainCatch); +// methodWriter.loadThis(); +// methodWriter.swap(); +// methodWriter.dup(); +// methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); +// methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); +// methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); +// methodWriter.throwException(); +// // This looks like: +// // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { +// // throw this.convertToScriptException(e, e.getHeaders()) +// // } +// // We *think* it is ok to catch OutOfMemoryError and StackOverflowError because Painless is stateless +// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, PAINLESS_ERROR_TYPE.getInternalName()); +// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, BOOTSTRAP_METHOD_ERROR_TYPE.getInternalName()); +// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, OUT_OF_MEMORY_ERROR_TYPE.getInternalName()); +// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, STACK_OVERFLOW_ERROR_TYPE.getInternalName()); +// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, EXCEPTION_TYPE.getInternalName()); +// methodWriter.mark(startOtherCatch); +// methodWriter.loadThis(); +// methodWriter.swap(); +// methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); +// methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); +// methodWriter.throwException(); +// methodWriter.mark(endCatch); +// } +// // TODO: end methodWriter.endMethod(); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java index 722a9c527abaf..4030df6cc31f2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java @@ -99,6 +99,10 @@ public void write(ClassWriter classWriter, MethodWriter methodWriter, Globals gl methodWriter.writeDebugInfo(location); if (localFunction != null) { + if (localFunction.isStatic() == false) { + methodWriter.loadThis(); + } + for (ExpressionNode argumentNode : getArgumentNodes()) { argumentNode.write(classWriter, methodWriter, globals, scopeTable); } @@ -106,7 +110,6 @@ public void write(ClassWriter classWriter, MethodWriter methodWriter, Globals gl if (localFunction.isStatic()) { methodWriter.invokeStatic(CLASS_TYPE, localFunction.getAsmMethod()); } else { - methodWriter.loadThis(); methodWriter.invokeVirtual(CLASS_TYPE, localFunction.getAsmMethod()); } } else if (importedMethod != null) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java new file mode 100644 index 0000000000000..7b81f43341aff --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java @@ -0,0 +1,69 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless.ir; + +import org.elasticsearch.painless.ClassWriter; +import org.elasticsearch.painless.Globals; +import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.symbol.ScopeTable; + +import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; + +public class UnboundFieldNode extends ExpressionNode { + + /* ---- begin node data ---- */ + + protected String name; + protected boolean isStatic; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setStatic(boolean isStatic) { + this.isStatic = isStatic; + } + + public boolean isStatic() { + return isStatic; + } + + /* ---- end node data ---- */ + + public UnboundFieldNode() { + // do nothing + } + + @Override + public void write(ClassWriter classWriter, MethodWriter methodWriter, Globals globals, ScopeTable scopeTable) { + methodWriter.writeDebugInfo(location); + + if (isStatic) { + methodWriter.getStatic(CLASS_TYPE, name, MethodWriter.getType(getExpressionType())); + } else { + methodWriter.loadThis(); + methodWriter.getField(CLASS_TYPE, name, MethodWriter.getType(getExpressionType())); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java index 07ac3326fedb0..d1dfce2150a9c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java @@ -34,13 +34,15 @@ public final class SDeclaration extends AStatement { private DType type; protected final String name; + protected final boolean requiresDefault; private AExpression expression; - public SDeclaration(Location location, DType type, String name, AExpression expression) { + public SDeclaration(Location location, DType type, String name, boolean requiresDefault, AExpression expression) { super(location); this.type = Objects.requireNonNull(type); this.name = Objects.requireNonNull(name); + this.requiresDefault = requiresDefault; this.expression = expression; } @@ -67,6 +69,7 @@ DeclarationNode write(ClassNode classNode) { declarationNode.setLocation(location); declarationNode.setDeclarationType(((DResolvedType)type).getType()); declarationNode.setName(name); + declarationNode.setRequiresDefault(requiresDefault); return declarationNode; } From 6115179c2b5b9bb6ab690212f35babfb7ed6103f Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Jan 2020 15:44:59 -0800 Subject: [PATCH 06/12] remove commented out code --- .../org/elasticsearch/painless/Compiler.java | 4 +- ...utePass.java => DecorateExecutePhase.java} | 51 ++++--------------- .../painless/ir/FunctionNode.java | 51 ------------------- 3 files changed, 12 insertions(+), 94 deletions(-) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{DecorateExecutePass.java => DecorateExecutePhase.java} (83%) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 76cfd6fd010e7..c8d48f8f362a3 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -211,7 +211,7 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null); ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); - DecorateExecutePass.pass(scriptRoot, classNode); + DecorateExecutePhase.phase(scriptRoot, classNode); Map statics = classNode.write(); try { @@ -243,7 +243,7 @@ byte[] compile(String name, String source, CompilerSettings settings, Printer de SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, debugStream); ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); - DecorateExecutePass.pass(scriptRoot, classNode); + DecorateExecutePhase.phase(scriptRoot, classNode); classNode.write(); return classNode.getBytes(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java similarity index 83% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java index 365c627064d63..799406ca3f340 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java @@ -44,9 +44,16 @@ import java.util.Collections; import java.util.Map; -public class DecorateExecutePass { +/** + * This injects additional ir nodes required to for + * the "execute" method. This includes injection of ir nodes + * to convert get methods into local variables for those + * that are used and adds additional sandboxing by wrapping + * the main "execute" block with several exceptions. + */ +public class DecorateExecutePhase { - public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { + public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { FunctionNode executeFunctionNode = null; // look up the execute method for decoration @@ -295,47 +302,9 @@ public static void pass(ScriptRoot scriptRoot, ClassNode classNode) { } catch (Exception exception) { throw new RuntimeException(exception); } - - /* - if ("execute".equals(name)) { - methodWriter.mark(endTry); - methodWriter.goTo(endCatch); - // This looks like: - // } catch (PainlessExplainError e) { - // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) - // } - methodWriter.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName()); - methodWriter.mark(startExplainCatch); - methodWriter.loadThis(); - methodWriter.swap(); - methodWriter.dup(); - methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); - methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); - methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); - methodWriter.throwException(); - // This looks like: - // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { - // throw this.convertToScriptException(e, e.getHeaders()) - // } - // We *think* it is ok to catch OutOfMemoryError and StackOverflowError because Painless is stateless - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, PAINLESS_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, BOOTSTRAP_METHOD_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, OUT_OF_MEMORY_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, STACK_OVERFLOW_ERROR_TYPE.getInternalName()); - methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, EXCEPTION_TYPE.getInternalName()); - methodWriter.mark(startOtherCatch); - methodWriter.loadThis(); - methodWriter.swap(); - methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); - methodWriter.invokeInterface(BASE_INTERFACE_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); - methodWriter.throwException(); - methodWriter.mark(endCatch); - } - // TODO: end - */ } - protected DecorateExecutePass() { + protected DecorateExecutePhase() { // do nothing } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index b04b7a7069a0e..532c9854256f4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -152,19 +152,6 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals methodWriter = classWriter.newMethodWriter(access, method); methodWriter.visitCode(); - // TODO: do not specialize for execute - // TODO: https://github.com/elastic/elasticsearch/issues/51841 -// Label startTry = new Label(); -// Label endTry = new Label(); -// Label startExplainCatch = new Label(); -// Label startOtherCatch = new Label(); -// Label endCatch = new Label(); -// -// if ("execute".equals(name)) { -// methodWriter.mark(startTry); -// } -// // TODO: end - if (maxLoopCounter > 0) { // if there is infinite loop protection, we do this once: // int #loop = settings.getMaxLoopCounter() @@ -177,44 +164,6 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals blockNode.write(classWriter, methodWriter, globals, scopeTable.newScope()); - // TODO: do not specialize for execute - // TODO: https://github.com/elastic/elasticsearch/issues/51841 -// if ("execute".equals(name)) { -// methodWriter.mark(endTry); -// methodWriter.goTo(endCatch); -// // This looks like: -// // } catch (PainlessExplainError e) { -// // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) -// // } -// methodWriter.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName()); -// methodWriter.mark(startExplainCatch); -// methodWriter.loadThis(); -// methodWriter.swap(); -// methodWriter.dup(); -// methodWriter.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); -// methodWriter.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD); -// methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); -// methodWriter.throwException(); -// // This looks like: -// // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { -// // throw this.convertToScriptException(e, e.getHeaders()) -// // } -// // We *think* it is ok to catch OutOfMemoryError and StackOverflowError because Painless is stateless -// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, PAINLESS_ERROR_TYPE.getInternalName()); -// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, BOOTSTRAP_METHOD_ERROR_TYPE.getInternalName()); -// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, OUT_OF_MEMORY_ERROR_TYPE.getInternalName()); -// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, STACK_OVERFLOW_ERROR_TYPE.getInternalName()); -// methodWriter.visitTryCatchBlock(startTry, endTry, startOtherCatch, EXCEPTION_TYPE.getInternalName()); -// methodWriter.mark(startOtherCatch); -// methodWriter.loadThis(); -// methodWriter.swap(); -// methodWriter.invokeStatic(COLLECTIONS_TYPE, EMPTY_MAP_METHOD); -// methodWriter.invokeVirtual(CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD); -// methodWriter.throwException(); -// methodWriter.mark(endCatch); -// } -// // TODO: end - methodWriter.endMethod(); } } From 10d8757a1ae0e457251ebc4bc5ae9056c7126971 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 11 Feb 2020 08:37:08 -0800 Subject: [PATCH 07/12] response to PR comments --- .../org/elasticsearch/painless/Compiler.java | 4 +- ...tePhase.java => ScriptInjectionPhase.java} | 60 +++++++++---------- ...boundCallNode.java => MemberCallNode.java} | 2 +- ...undFieldNode.java => MemberFieldNode.java} | 4 +- .../painless/node/ECallLocal.java | 26 ++++---- 5 files changed, 48 insertions(+), 48 deletions(-) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{DecorateExecutePhase.java => ScriptInjectionPhase.java} (85%) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/{UnboundCallNode.java => MemberCallNode.java} (99%) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/{UnboundFieldNode.java => MemberFieldNode.java} (95%) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index c8d48f8f362a3..c75e0470e0a02 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -211,7 +211,7 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null); ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); - DecorateExecutePhase.phase(scriptRoot, classNode); + ScriptInjectionPhase.phase(scriptRoot, classNode); Map statics = classNode.write(); try { @@ -243,7 +243,7 @@ byte[] compile(String name, String source, CompilerSettings settings, Printer de SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, debugStream); ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); ClassNode classNode = root.writeClass(); - DecorateExecutePhase.phase(scriptRoot, classNode); + ScriptInjectionPhase.phase(scriptRoot, classNode); classNode.write(); return classNode.getBytes(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java similarity index 85% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java index 799406ca3f340..365d398eee7c6 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DecorateExecutePhase.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java @@ -30,8 +30,8 @@ import org.elasticsearch.painless.ir.StaticNode; import org.elasticsearch.painless.ir.ThrowNode; import org.elasticsearch.painless.ir.TryNode; -import org.elasticsearch.painless.ir.UnboundCallNode; -import org.elasticsearch.painless.ir.UnboundFieldNode; +import org.elasticsearch.painless.ir.MemberCallNode; +import org.elasticsearch.painless.ir.MemberFieldNode; import org.elasticsearch.painless.ir.VariableNode; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -45,13 +45,13 @@ import java.util.Map; /** - * This injects additional ir nodes required to for + * This injects additional ir nodes required for * the "execute" method. This includes injection of ir nodes * to convert get methods into local variables for those * that are used and adds additional sandboxing by wrapping * the main "execute" block with several exceptions. */ -public class DecorateExecutePhase { +public class ScriptInjectionPhase { public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { FunctionNode executeFunctionNode = null; @@ -87,12 +87,12 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { if (name.equals(declarationNode.getName())) { if (scriptRoot.getUsedVariables().contains(name)) { - UnboundCallNode unboundCallNode = new UnboundCallNode(); - unboundCallNode.setLocation(declarationNode.getLocation()); - unboundCallNode.setExpressionType(declarationNode.getDeclarationType()); - unboundCallNode.setLocalFunction(new LocalFunction( + MemberCallNode memberCallNode = new MemberCallNode(); + memberCallNode.setLocation(declarationNode.getLocation()); + memberCallNode.setExpressionType(declarationNode.getDeclarationType()); + memberCallNode.setLocalFunction(new LocalFunction( getMethod.getName(), returnType, Collections.emptyList(), true, false)); - declarationNode.setExpressionNode(unboundCallNode); + declarationNode.setExpressionNode(memberCallNode); } else { blockNode.getStatementsNodes().remove(statementIndex); isRemoved = true; @@ -151,10 +151,10 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { catchBlockNode.addStatementNode(throwNode); - UnboundCallNode unboundCallNode = new UnboundCallNode(); - unboundCallNode.setLocation(internalLocation); - unboundCallNode.setExpressionType(ScriptException.class); - unboundCallNode.setLocalFunction(new LocalFunction( + MemberCallNode memberCallNode = new MemberCallNode(); + memberCallNode.setLocation(internalLocation); + memberCallNode.setExpressionType(ScriptException.class); + memberCallNode.setLocalFunction(new LocalFunction( "convertToScriptException", ScriptException.class, Arrays.asList(Throwable.class, Map.class), @@ -163,20 +163,20 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { ) ); - throwNode.setExpressionNode(unboundCallNode); + throwNode.setExpressionNode(memberCallNode); VariableNode variableNode = new VariableNode(); variableNode.setLocation(internalLocation); variableNode.setExpressionType(ScriptException.class); variableNode.setName("#painlessExplainError"); - unboundCallNode.addArgumentNode(variableNode); + memberCallNode.addArgumentNode(variableNode); CallNode callNode = new CallNode(); callNode.setLocation(internalLocation); callNode.setExpressionType(Map.class); - unboundCallNode.addArgumentNode(callNode); + memberCallNode.addArgumentNode(callNode); variableNode = new VariableNode(); variableNode.setLocation(internalLocation); @@ -203,13 +203,13 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { callNode.setRightNode(callSubNode); - UnboundFieldNode unboundFieldNode = new UnboundFieldNode(); - unboundFieldNode.setLocation(internalLocation); - unboundFieldNode.setExpressionType(PainlessLookup.class); - unboundFieldNode.setName("$DEFINITION"); - unboundFieldNode.setStatic(true); + MemberFieldNode memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(PainlessLookup.class); + memberFieldNode.setName("$DEFINITION"); + memberFieldNode.setStatic(true); - callSubNode.addArgumentNode(unboundFieldNode); + callSubNode.addArgumentNode(memberFieldNode); for (Class throwable : new Class[] { PainlessError.class, BootstrapMethodError.class, OutOfMemoryError.class, StackOverflowError.class, Exception.class}) { @@ -242,10 +242,10 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { catchBlockNode.addStatementNode(throwNode); - unboundCallNode = new UnboundCallNode(); - unboundCallNode.setLocation(internalLocation); - unboundCallNode.setExpressionType(ScriptException.class); - unboundCallNode.setLocalFunction(new LocalFunction( + memberCallNode = new MemberCallNode(); + memberCallNode.setLocation(internalLocation); + memberCallNode.setExpressionType(ScriptException.class); + memberCallNode.setLocalFunction(new LocalFunction( "convertToScriptException", ScriptException.class, Arrays.asList(Throwable.class, Map.class), @@ -254,20 +254,20 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { ) ); - throwNode.setExpressionNode(unboundCallNode); + throwNode.setExpressionNode(memberCallNode); variableNode = new VariableNode(); variableNode.setLocation(internalLocation); variableNode.setExpressionType(ScriptException.class); variableNode.setName(name); - unboundCallNode.addArgumentNode(variableNode); + memberCallNode.addArgumentNode(variableNode); callNode = new CallNode(); callNode.setLocation(internalLocation); callNode.setExpressionType(Map.class); - unboundCallNode.addArgumentNode(callNode); + memberCallNode.addArgumentNode(callNode); StaticNode staticNode = new StaticNode(); staticNode.setLocation(internalLocation); @@ -304,7 +304,7 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { } } - protected DecorateExecutePhase() { + private ScriptInjectionPhase() { // do nothing } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberCallNode.java similarity index 99% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberCallNode.java index 4030df6cc31f2..8dcc87363bac0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundCallNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberCallNode.java @@ -33,7 +33,7 @@ import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -public class UnboundCallNode extends ArgumentsNode { +public class MemberCallNode extends ArgumentsNode { /* ---- begin node data ---- */ diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberFieldNode.java similarity index 95% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberFieldNode.java index 7b81f43341aff..7008fa9894c89 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/UnboundFieldNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/MemberFieldNode.java @@ -26,7 +26,7 @@ import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -public class UnboundFieldNode extends ExpressionNode { +public class MemberFieldNode extends ExpressionNode { /* ---- begin node data ---- */ @@ -51,7 +51,7 @@ public boolean isStatic() { /* ---- end node data ---- */ - public UnboundFieldNode() { + public MemberFieldNode() { // do nothing } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java index 79c3bcd0b4181..a0e6d1fd9615e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java @@ -22,7 +22,7 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Scope; import org.elasticsearch.painless.ir.ClassNode; -import org.elasticsearch.painless.ir.UnboundCallNode; +import org.elasticsearch.painless.ir.MemberCallNode; import org.elasticsearch.painless.lookup.PainlessClassBinding; import org.elasticsearch.painless.lookup.PainlessInstanceBinding; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -150,23 +150,23 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { } @Override - UnboundCallNode write(ClassNode classNode) { - UnboundCallNode unboundCallNode = new UnboundCallNode(); + MemberCallNode write(ClassNode classNode) { + MemberCallNode memberCallNode = new MemberCallNode(); for (AExpression argument : arguments) { - unboundCallNode.addArgumentNode(argument.write(classNode)); + memberCallNode.addArgumentNode(argument.write(classNode)); } - unboundCallNode.setLocation(location); - unboundCallNode.setExpressionType(actual); - unboundCallNode.setLocalFunction(localFunction); - unboundCallNode.setImportedMethod(importedMethod); - unboundCallNode.setClassBinding(classBinding); - unboundCallNode.setClassBindingOffset(classBindingOffset); - unboundCallNode.setBindingName(bindingName); - unboundCallNode.setInstanceBinding(instanceBinding); + memberCallNode.setLocation(location); + memberCallNode.setExpressionType(actual); + memberCallNode.setLocalFunction(localFunction); + memberCallNode.setImportedMethod(importedMethod); + memberCallNode.setClassBinding(classBinding); + memberCallNode.setClassBindingOffset(classBindingOffset); + memberCallNode.setBindingName(bindingName); + memberCallNode.setInstanceBinding(instanceBinding); - return unboundCallNode; + return memberCallNode; } @Override From 922e4e2f759291c533f94c9ddb9f6ac0766ff74d Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 8 Jan 2020 07:44:20 -0800 Subject: [PATCH 08/12] move static constant generation --- .../org/elasticsearch/painless/Compiler.java | 23 +++++++-------- .../elasticsearch/painless/ir/ClassNode.java | 29 +++---------------- .../elasticsearch/painless/ir/FieldNode.java | 9 ------ .../painless/node/ECallLocal.java | 5 ++-- .../elasticsearch/painless/node/ERegex.java | 2 +- .../elasticsearch/painless/node/SClass.java | 10 +++---- .../elasticsearch/painless/node/SField.java | 10 +------ .../painless/symbol/ScriptRoot.java | 16 +++++++++- 8 files changed, 39 insertions(+), 65 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index c75e0470e0a02..9bb897e73b7f3 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -209,20 +209,17 @@ private static void addFactoryMethod(Map> additionalClasses, Cl ScriptRoot compile(Loader loader, String name, String source, CompilerSettings settings) { ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass); SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null); - ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); + ScriptRoot scriptRoot = new ScriptRoot(painlessLookup, settings, scriptClassInfo, root); + root.analyze(scriptRoot); ClassNode classNode = root.writeClass(); ScriptInjectionPhase.phase(scriptRoot, classNode); - Map statics = classNode.write(); + byte[] bytes = classNode.write(); try { - Class clazz = loader.defineScript(CLASS_NAME, classNode.getBytes()); - clazz.getField("$NAME").set(null, name); - clazz.getField("$SOURCE").set(null, source); - clazz.getField("$STATEMENTS").set(null, classNode.getStatements()); - clazz.getField("$DEFINITION").set(null, painlessLookup); - - for (Map.Entry statik : statics.entrySet()) { - clazz.getField(statik.getKey()).set(null, statik.getValue()); + Class clazz = loader.defineScript(CLASS_NAME, bytes); + + for (Map.Entry staticConstant : scriptRoot.getStaticConstants().entrySet()) { + clazz.getField(staticConstant.getKey()).set(null, staticConstant.getValue()); } return scriptRoot; @@ -241,11 +238,11 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s byte[] compile(String name, String source, CompilerSettings settings, Printer debugStream) { ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass); SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, debugStream); - ScriptRoot scriptRoot = root.analyze(painlessLookup, settings); + ScriptRoot scriptRoot = new ScriptRoot(painlessLookup, settings, scriptClassInfo, root); + root.analyze(scriptRoot); ClassNode classNode = root.writeClass(); ScriptInjectionPhase.phase(scriptRoot, classNode); - classNode.write(); - return classNode.getBytes(); + return classNode.write(); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java index 6b9805fcd8047..4c140dcea4641 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java @@ -37,9 +37,7 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; import static org.elasticsearch.painless.WriterConstants.BITSET_TYPE; @@ -128,18 +126,10 @@ public ScriptRoot getScriptRoot() { /* ---- end node data ---- */ protected Globals globals; - protected byte[] bytes; - public BitSet getStatements() { - return globals.getStatements(); - } - - public byte[] getBytes() { - return bytes; - } - - public Map write() { - this.globals = new Globals(new BitSet(sourceText.length())); + public byte[] write() { + globals = new Globals(new BitSet(sourceText.length())); + scriptRoot.addStaticConstant("$STATEMENTS", globals.getStatements()); // Create the ClassWriter. @@ -255,17 +245,6 @@ public Map write() { // End writing the class and store the generated bytes. classVisitor.visitEnd(); - bytes = classWriter.getClassBytes(); - - Map statics = new HashMap<>(); - statics.put("$FUNCTIONS", scriptRoot.getFunctionTable()); - - for (FieldNode fieldNode : fieldNodes) { - if (fieldNode.getInstance() != null) { - statics.put(fieldNode.getName(), fieldNode.getInstance()); - } - } - - return statics; + return classWriter.getClassBytes(); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FieldNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FieldNode.java index aa9ee2dcbb37d..95e113cbf57ba 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FieldNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FieldNode.java @@ -33,7 +33,6 @@ public class FieldNode extends IRNode { private int modifiers; private Class fieldType; private String name; - private Object instance; public void setModifiers(int modifiers) { this.modifiers = modifiers; @@ -63,14 +62,6 @@ public String getName() { return name; } - public void setInstance(Object instance) { - this.instance = instance; - } - - public Object getInstance() { - return instance; - } - /* ---- end node data ---- */ @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java index a0e6d1fd9615e..00f2032d183a1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java @@ -123,13 +123,14 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { actual = classBinding.returnType; bindingName = scriptRoot.getNextSyntheticName("class_binding"); scriptRoot.getClassNode().addField(new SField(location, - Modifier.PRIVATE, bindingName, classBinding.javaConstructor.getDeclaringClass(), null)); + Modifier.PRIVATE, bindingName, classBinding.javaConstructor.getDeclaringClass())); } else if (instanceBinding != null) { typeParameters = new ArrayList<>(instanceBinding.typeParameters); actual = instanceBinding.returnType; bindingName = scriptRoot.getNextSyntheticName("instance_binding"); scriptRoot.getClassNode().addField(new SField(location, Modifier.STATIC | Modifier.PUBLIC, - bindingName, instanceBinding.targetInstance.getClass(), instanceBinding.targetInstance)); + bindingName, instanceBinding.targetInstance.getClass())); + scriptRoot.addStaticConstant(bindingName, instanceBinding.targetInstance); } else { throw new IllegalStateException("Illegal tree structure."); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java index 1650c9d825c92..a4a1be53006ce 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java @@ -76,7 +76,7 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { String name = scriptRoot.getNextSyntheticName("regex"); scriptRoot.getClassNode().addField( - new SField(location, Modifier.FINAL | Modifier.STATIC | Modifier.PRIVATE, name, Pattern.class, null)); + new SField(location, Modifier.FINAL | Modifier.STATIC | Modifier.PRIVATE, name, Pattern.class)); constant = new Constant(location, MethodWriter.getType(Pattern.class), name, this::initializeConstant); actual = Pattern.class; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java index a14a9ffa248c2..d946b9b1c69b5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java @@ -19,12 +19,10 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.CompilerSettings; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.ScriptClassInfo; import org.elasticsearch.painless.ir.ClassNode; import org.elasticsearch.painless.ir.StatementNode; -import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.symbol.FunctionTable; import org.elasticsearch.painless.symbol.ScriptRoot; import org.objectweb.asm.util.Printer; @@ -67,11 +65,13 @@ void addField(SField field) { fields.add(field); } - public ScriptRoot analyze(PainlessLookup painlessLookup, CompilerSettings settings) { - scriptRoot = new ScriptRoot(painlessLookup, settings, scriptClassInfo, this); + public ScriptRoot analyze(ScriptRoot scriptRoot) { + this.scriptRoot = scriptRoot; + scriptRoot.addStaticConstant("$NAME", name); + scriptRoot.addStaticConstant("$SOURCE", sourceText); for (SFunction function : functions) { - function.generateSignature(painlessLookup); + function.generateSignature(scriptRoot.getPainlessLookup()); String key = FunctionTable.buildLocalFunctionKey(function.name, function.typeParameters.size()); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SField.java index c0b6f95c14695..944802fb7ac39 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SField.java @@ -31,7 +31,6 @@ public class SField extends ANode { private final int modifiers; private final String name; private final Class type; - private final Object instance; /** * Standard constructor. @@ -39,25 +38,19 @@ public class SField extends ANode { * @param modifiers java modifiers for the field * @param name name of the field * @param type type of the field - * @param instance initial value for the field */ - public SField(Location location, int modifiers, String name, Class type, Object instance) { + public SField(Location location, int modifiers, String name, Class type) { super(location); this.modifiers = modifiers; this.name = name; this.type = type; - this.instance = instance; } public String getName() { return name; } - public Object getInstance() { - return instance; - } - @Override FieldNode write(ClassNode classNode) { FieldNode fieldNode = new FieldNode(); @@ -66,7 +59,6 @@ FieldNode write(ClassNode classNode) { fieldNode.setModifiers(modifiers); fieldNode.setName(name); fieldNode.setFieldType(type); - fieldNode.setInstance(instance); return fieldNode; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/ScriptRoot.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/ScriptRoot.java index 2ffadc8bad528..71f67d534b9ec 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/ScriptRoot.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/ScriptRoot.java @@ -25,6 +25,8 @@ import org.elasticsearch.painless.node.SClass; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -44,12 +46,16 @@ public class ScriptRoot { protected boolean deterministic = true; protected Set usedVariables = Collections.emptySet(); + protected Map staticConstants = new HashMap<>(); public ScriptRoot(PainlessLookup painlessLookup, CompilerSettings compilerSettings, ScriptClassInfo scriptClassInfo, SClass classRoot) { this.painlessLookup = Objects.requireNonNull(painlessLookup); this.compilerSettings = Objects.requireNonNull(compilerSettings); this.scriptClassInfo = Objects.requireNonNull(scriptClassInfo); this.classNode = Objects.requireNonNull(classRoot); + + staticConstants.put("$DEFINITION", painlessLookup); + staticConstants.put("$FUNCTIONS", functionTable); } public PainlessLookup getPainlessLookup() { @@ -92,6 +98,14 @@ public void setUsedVariables(Set usedVariables) { } public Set getUsedVariables() { - return usedVariables; + return Collections.unmodifiableSet(usedVariables); + } + + public void addStaticConstant(String name, Object constant) { + staticConstants.put(name, constant); + } + + public Map getStaticConstants() { + return Collections.unmodifiableMap(staticConstants); } } From a3f5c270fcc1347d3fc91aa4dca1066dec19e8ce Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 8 Jan 2020 14:25:26 -0800 Subject: [PATCH 09/12] create bootstrap methods with external phase --- .../org/elasticsearch/painless/Compiler.java | 2 + .../painless/DefBootstrapInjectionPhase.java | 210 ++++++++++++++++++ .../painless/ScriptInjectionPhase.java | 95 ++++++-- .../elasticsearch/painless/ir/ClassNode.java | 26 --- .../painless/ir/FunctionNode.java | 37 +-- .../painless/node/SFunction.java | 4 +- 6 files changed, 304 insertions(+), 70 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 9bb897e73b7f3..ab09010d248d4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -212,6 +212,7 @@ ScriptRoot compile(Loader loader, String name, String source, CompilerSettings s ScriptRoot scriptRoot = new ScriptRoot(painlessLookup, settings, scriptClassInfo, root); root.analyze(scriptRoot); ClassNode classNode = root.writeClass(); + DefBootstrapInjectionPhase.phase(classNode); ScriptInjectionPhase.phase(scriptRoot, classNode); byte[] bytes = classNode.write(); @@ -241,6 +242,7 @@ byte[] compile(String name, String source, CompilerSettings settings, Printer de ScriptRoot scriptRoot = new ScriptRoot(painlessLookup, settings, scriptClassInfo, root); root.analyze(scriptRoot); ClassNode classNode = root.writeClass(); + DefBootstrapInjectionPhase.phase(classNode); ScriptInjectionPhase.phase(scriptRoot, classNode); return classNode.write(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java new file mode 100644 index 0000000000000..019a3f9ab5cd5 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java @@ -0,0 +1,210 @@ +package org.elasticsearch.painless;/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.elasticsearch.painless.ir.BlockNode; +import org.elasticsearch.painless.ir.CallNode; +import org.elasticsearch.painless.ir.CallSubNode; +import org.elasticsearch.painless.ir.ClassNode; +import org.elasticsearch.painless.ir.FieldNode; +import org.elasticsearch.painless.ir.FunctionNode; +import org.elasticsearch.painless.ir.MemberFieldNode; +import org.elasticsearch.painless.ir.ReturnNode; +import org.elasticsearch.painless.ir.StaticNode; +import org.elasticsearch.painless.ir.VariableNode; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.symbol.FunctionTable; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.util.Arrays; + +public class DefBootstrapInjectionPhase { + + public static void phase(ClassNode classNode) { + injectStaticFields(classNode); + injectDefBootstrapMethod(classNode); + } + + // adds static fields required for def bootstrapping + protected static void injectStaticFields(ClassNode classNode) { + Location internalLocation = new Location("$internal$DefBootstrapInjectionPhase$injectStaticFields", 0); + int modifiers = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; + + FieldNode fieldNode = new FieldNode(); + fieldNode.setLocation(internalLocation); + fieldNode.setModifiers(modifiers); + fieldNode.setFieldType(PainlessLookup.class); + fieldNode.setName("$DEFINITION"); + + classNode.addFieldNode(fieldNode); + + fieldNode = new FieldNode(); + fieldNode.setLocation(internalLocation); + fieldNode.setModifiers(modifiers); + fieldNode.setFieldType(FunctionTable.class); + fieldNode.setName("$FUNCTIONS"); + + classNode.addFieldNode(fieldNode); + } + + protected static void injectDefBootstrapMethod(ClassNode classNode) { + Location internalLocation = new Location("$internal$DefBootstrapInjectionPhase$injectDefBootstrapMethod", 0); + + try { + FunctionNode functionNode = new FunctionNode(); + functionNode.setLocation(internalLocation); + functionNode.setReturnType(CallSite.class); + functionNode.setName("$bootstrapDef"); + functionNode.getTypeParameters().addAll( + Arrays.asList(Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class)); + functionNode.getParameterNames().addAll( + Arrays.asList("methodHandlesLookup", "name", "type", "initialDepth", "flavor", "args")); + functionNode.setStatic(true); + functionNode.setVarArgs(true); + functionNode.setSynthetic(true); + functionNode.setMaxLoopCounter(0); + + classNode.addFunctionNode(functionNode); + + BlockNode blockNode = new BlockNode(); + blockNode.setLocation(internalLocation); + blockNode.setAllEscape(true); + blockNode.setStatementCount(1); + + functionNode.setBlockNode(blockNode); + + ReturnNode returnNode = new ReturnNode(); + returnNode.setLocation(internalLocation); + + blockNode.addStatementNode(returnNode); + + CallNode callNode = new CallNode(); + callNode.setLocation(internalLocation); + callNode.setExpressionType(CallSite.class); + + returnNode.setExpressionNode(callNode); + + StaticNode staticNode = new StaticNode(); + staticNode.setLocation(internalLocation); + staticNode.setExpressionType(DefBootstrap.class); + + callNode.setLeftNode(staticNode); + + CallSubNode callSubNode = new CallSubNode(); + callSubNode.setLocation(internalLocation); + callSubNode.setExpressionType(CallSite.class); + callSubNode.setMethod(new PainlessMethod( + DefBootstrap.class.getMethod("bootstrap", + PainlessLookup.class, + FunctionTable.class, + Lookup.class, + String.class, + MethodType.class, + int.class, + int.class, + Object[].class), + DefBootstrap.class, + CallSite.class, + Arrays.asList( + PainlessLookup.class, + FunctionTable.class, + Lookup.class, + String.class, + MethodType.class, + int.class, + int.class, + Object[].class), + null, + null, + null + ) + ); + callSubNode.setBox(DefBootstrap.class); + + callNode.setRightNode(callSubNode); + + MemberFieldNode memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(PainlessLookup.class); + memberFieldNode.setName("$DEFINITION"); + memberFieldNode.setStatic(true); + + callSubNode.addArgumentNode(memberFieldNode); + + memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(FunctionTable.class); + memberFieldNode.setName("$FUNCTIONS"); + memberFieldNode.setStatic(true); + + callSubNode.addArgumentNode(memberFieldNode); + + VariableNode variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(Lookup.class); + variableNode.setName("methodHandlesLookup"); + + callSubNode.addArgumentNode(variableNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(String.class); + variableNode.setName("name"); + + callSubNode.addArgumentNode(variableNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(MethodType.class); + variableNode.setName("type"); + + callSubNode.addArgumentNode(variableNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(int.class); + variableNode.setName("initialDepth"); + + callSubNode.addArgumentNode(variableNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(int.class); + variableNode.setName("flavor"); + + callSubNode.addArgumentNode(variableNode); + + variableNode = new VariableNode(); + variableNode.setLocation(internalLocation); + variableNode.setExpressionType(Object[].class); + variableNode.setName("args"); + + callSubNode.addArgumentNode(variableNode); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + private DefBootstrapInjectionPhase() { + // do nothing + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java index 365d398eee7c6..72c5d47c3f9bd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java @@ -25,22 +25,25 @@ import org.elasticsearch.painless.ir.CatchNode; import org.elasticsearch.painless.ir.ClassNode; import org.elasticsearch.painless.ir.DeclarationNode; +import org.elasticsearch.painless.ir.FieldNode; import org.elasticsearch.painless.ir.FunctionNode; +import org.elasticsearch.painless.ir.MemberCallNode; +import org.elasticsearch.painless.ir.MemberFieldNode; import org.elasticsearch.painless.ir.StatementNode; import org.elasticsearch.painless.ir.StaticNode; import org.elasticsearch.painless.ir.ThrowNode; import org.elasticsearch.painless.ir.TryNode; -import org.elasticsearch.painless.ir.MemberCallNode; -import org.elasticsearch.painless.ir.MemberFieldNode; import org.elasticsearch.painless.ir.VariableNode; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction; import org.elasticsearch.painless.symbol.ScriptRoot; import org.elasticsearch.script.ScriptException; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.Method; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.Map; @@ -54,6 +57,8 @@ public class ScriptInjectionPhase { public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { + injectStaticFields(classNode); + FunctionNode executeFunctionNode = null; // look up the execute method for decoration @@ -64,12 +69,51 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { } } - BlockNode blockNode = executeFunctionNode.getBlockNode(); + if (executeFunctionNode == null) { + throw new IllegalStateException("all scripts must have an [execute] method"); + } + + injectGetsDeclarations(scriptRoot, classNode, executeFunctionNode); + injectSandboxExceptions(classNode, executeFunctionNode); + } + + // adds static fields required by PainlessScript for exception handling + protected static void injectStaticFields(ClassNode classNode) { + Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectStaticFields", 0); + int modifiers = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; - // convert gets methods to a new set of inserted ir nodes as necessary - - // requires the gets method name be modified from "getExample" to "example" - // if a get method variable isn't used it's declaration node is removed from - // the ir tree permanently so there is no frivolous variable slotting + FieldNode fieldNode = new FieldNode(); + fieldNode.setLocation(internalLocation); + fieldNode.setModifiers(modifiers); + fieldNode.setFieldType(String.class); + fieldNode.setName("$NAME"); + + classNode.addFieldNode(fieldNode); + + fieldNode = new FieldNode(); + fieldNode.setLocation(internalLocation); + fieldNode.setModifiers(modifiers); + fieldNode.setFieldType(String.class); + fieldNode.setName("$SOURCE"); + + classNode.addFieldNode(fieldNode); + + fieldNode = new FieldNode(); + fieldNode.setLocation(internalLocation); + fieldNode.setModifiers(modifiers); + fieldNode.setFieldType(BitSet.class); + fieldNode.setName("$STATEMENTS"); + + classNode.addFieldNode(fieldNode); + } + + // convert gets methods to a new set of inserted ir nodes as necessary - + // requires the gets method name be modified from "getExample" to "example" + // if a get method variable isn't used it's declaration node is removed from + // the ir tree permanently so there is no frivolous variable slotting + protected static void injectGetsDeclarations(ScriptRoot scriptRoot, ClassNode classNode, FunctionNode functionNode) { + Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectGetsDeclarations", 0); + BlockNode blockNode = functionNode.getBlockNode(); int statementIndex = 0; while (statementIndex < blockNode.getStatementsNodes().size()) { @@ -87,12 +131,12 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { if (name.equals(declarationNode.getName())) { if (scriptRoot.getUsedVariables().contains(name)) { - MemberCallNode memberCallNode = new MemberCallNode(); - memberCallNode.setLocation(declarationNode.getLocation()); - memberCallNode.setExpressionType(declarationNode.getDeclarationType()); - memberCallNode.setLocalFunction(new LocalFunction( - getMethod.getName(), returnType, Collections.emptyList(), true, false)); - declarationNode.setExpressionNode(memberCallNode); + MemberCallNode memberCallNode = new MemberCallNode(); + memberCallNode.setLocation(internalLocation); + memberCallNode.setExpressionType(declarationNode.getDeclarationType()); + memberCallNode.setLocalFunction(new LocalFunction( + getMethod.getName(), returnType, Collections.emptyList(), true, false)); + declarationNode.setExpressionNode(memberCallNode); } else { blockNode.getStatementsNodes().remove(statementIndex); isRemoved = true; @@ -109,18 +153,21 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { ++statementIndex; } } + } - // decorate the execute method with nodes to wrap the user statements with - // the sandboxed errors as follows: - // } catch (PainlessExplainError e) { - // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) - // } - // and - // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { - // throw this.convertToScriptException(e, e.getHeaders()) - // } + // decorate the execute method with nodes to wrap the user statements with + // the sandboxed errors as follows: + // } catch (PainlessExplainError e) { + // throw this.convertToScriptException(e, e.getHeaders($DEFINITION)) + // } + // and + // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { + // throw this.convertToScriptException(e, e.getHeaders()) + // } + protected static void injectSandboxExceptions(ClassNode classNode, FunctionNode functionNode) { try { - Location internalLocation = new Location("", 0); + Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectSandboxExceptions", 0); + BlockNode blockNode = functionNode.getBlockNode(); TryNode tryNode = new TryNode(); tryNode.setLocation(internalLocation); @@ -298,7 +345,7 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { blockNode.setStatementCount(blockNode.getStatementCount()); blockNode.addStatementNode(tryNode); - executeFunctionNode.setBlockNode(blockNode); + functionNode.setBlockNode(blockNode); } catch (Exception exception) { throw new RuntimeException(exception); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java index 4c140dcea4641..a7df00f77f40d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java @@ -42,11 +42,6 @@ import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; import static org.elasticsearch.painless.WriterConstants.BITSET_TYPE; import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -import static org.elasticsearch.painless.WriterConstants.DEFINITION_TYPE; -import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_DELEGATE_METHOD; -import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_DELEGATE_TYPE; -import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_METHOD; -import static org.elasticsearch.painless.WriterConstants.FUNCTION_TABLE_TYPE; import static org.elasticsearch.painless.WriterConstants.GET_NAME_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; @@ -144,27 +139,6 @@ public byte[] write() { ClassVisitor classVisitor = classWriter.getClassVisitor(); classVisitor.visitSource(Location.computeSourceName(name), null); - // Write the a method to bootstrap def calls - MethodWriter bootstrapDef = classWriter.newMethodWriter(Opcodes.ACC_STATIC | Opcodes.ACC_VARARGS, DEF_BOOTSTRAP_METHOD); - bootstrapDef.visitCode(); - bootstrapDef.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); - bootstrapDef.getStatic(CLASS_TYPE, "$FUNCTIONS", FUNCTION_TABLE_TYPE); - bootstrapDef.loadArgs(); - bootstrapDef.invokeStatic(DEF_BOOTSTRAP_DELEGATE_TYPE, DEF_BOOTSTRAP_DELEGATE_METHOD); - bootstrapDef.returnValue(); - bootstrapDef.endMethod(); - - // Write static variables for name, source and statements used for writing exception messages - classVisitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$NAME", STRING_TYPE.getDescriptor(), null, null).visitEnd(); - classVisitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$SOURCE", STRING_TYPE.getDescriptor(), null, null).visitEnd(); - classVisitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$STATEMENTS", BITSET_TYPE.getDescriptor(), null, null).visitEnd(); - - // Write the static variables used by the method to bootstrap def calls - classVisitor.visitField( - Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$DEFINITION", DEFINITION_TYPE.getDescriptor(), null, null).visitEnd(); - classVisitor.visitField( - Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$FUNCTIONS", FUNCTION_TABLE_TYPE.getDescriptor(), null, null).visitEnd(); - org.objectweb.asm.commons.Method init; if (scriptClassInfo.getBaseClass().getConstructors().length == 0) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java index 532c9854256f4..47c944f3b8e27 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/FunctionNode.java @@ -24,7 +24,6 @@ import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.symbol.ScopeTable; import org.elasticsearch.painless.symbol.ScopeTable.Variable; -import org.elasticsearch.painless.symbol.ScriptRoot; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; @@ -48,22 +47,14 @@ public BlockNode getBlockNode() { /* ---- end tree structure, begin node data ---- */ - private ScriptRoot scriptRoot; - private String name; - private Class returnType; - private List> typeParameters = new ArrayList<>(); - private List parameterNames = new ArrayList<>(); - private boolean isStatic; - private boolean isSynthetic; - private int maxLoopCounter; - - public void setScriptRoot(ScriptRoot scriptRoot) { - this.scriptRoot = scriptRoot; - } - - public ScriptRoot getScriptRoot() { - return scriptRoot; - } + protected String name; + Class returnType; + List> typeParameters = new ArrayList<>(); + List parameterNames = new ArrayList<>(); + protected boolean isStatic; + protected boolean hasVarArgs; + protected boolean isSynthetic; + protected int maxLoopCounter; public void setName(String name) { this.name = name; @@ -105,6 +96,14 @@ public boolean isStatic() { return isStatic; } + public void setVarArgs(boolean hasVarArgs) { + this.hasVarArgs = hasVarArgs; + } + + public boolean hasVarArgs() { + return hasVarArgs; + } + public void setSynthetic(boolean isSythetic) { this.isSynthetic = isSythetic; } @@ -133,6 +132,10 @@ protected void write(ClassWriter classWriter, MethodWriter methodWriter, Globals scopeTable.defineInternalVariable(Object.class, "this"); } + if (hasVarArgs) { + access |= Opcodes.ACC_VARARGS; + } + if (isSynthetic) { access |= Opcodes.ACC_SYNTHETIC; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index f71b7fbb8f538..2ae9ef8d5d78f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -69,7 +69,6 @@ public final class SFunction extends ANode { org.objectweb.asm.commons.Method method; - private ScriptRoot scriptRoot; private boolean methodEscape; public SFunction(Location location, String rtnType, String name, @@ -121,7 +120,6 @@ void generateSignature(PainlessLookup painlessLookup) { } void analyze(ScriptRoot scriptRoot) { - this.scriptRoot = scriptRoot; FunctionScope functionScope = newFunctionScope(returnType); for (int index = 0; index < typeParameters.size(); ++index) { @@ -210,12 +208,12 @@ public FunctionNode write(ClassNode classNode) { functionNode.setBlockNode(blockNode); functionNode.setLocation(location); - functionNode.setScriptRoot(scriptRoot); functionNode.setName(name); functionNode.setReturnType(returnType); functionNode.getTypeParameters().addAll(typeParameters); functionNode.getParameterNames().addAll(paramNameStrs); functionNode.setStatic(isStatic); + functionNode.setVarArgs(false); functionNode.setSynthetic(synthetic); functionNode.setMaxLoopCounter(maxLoopCounter); From 1ec36436a238837f7818cfdefc97172fe5ee58d2 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 9 Jan 2020 10:43:27 -0800 Subject: [PATCH 10/12] move gets methods and static variables out of class node --- .../painless/DefBootstrapInjectionPhase.java | 8 ++ .../painless/ScriptInjectionPhase.java | 111 ++++++++++++++++-- .../elasticsearch/painless/ir/ClassNode.java | 26 ---- .../elasticsearch/painless/node/ELambda.java | 1 + .../painless/node/ENewArrayFunctionRef.java | 1 - 5 files changed, 111 insertions(+), 36 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java index 019a3f9ab5cd5..603defb7c82d1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrapInjectionPhase.java @@ -37,6 +37,13 @@ import java.lang.invoke.MethodType; import java.util.Arrays; +/** + * This injects additional ir nodes required for + * resolving the def type at runtime. This includes injection + * of ir nodes to add a function to call + * {@link DefBootstrap#bootstrap(PainlessLookup, FunctionTable, Lookup, String, MethodType, int, int, Object...)} + * to do the runtime resolution. + */ public class DefBootstrapInjectionPhase { public static void phase(ClassNode classNode) { @@ -66,6 +73,7 @@ protected static void injectStaticFields(ClassNode classNode) { classNode.addFieldNode(fieldNode); } + // adds the bootstrap method required for dynamic binding for def type resolution protected static void injectDefBootstrapMethod(ClassNode classNode) { Location internalLocation = new Location("$internal$DefBootstrapInjectionPhase$injectDefBootstrapMethod", 0); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java index 72c5d47c3f9bd..039e494193ef1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java @@ -29,6 +29,7 @@ import org.elasticsearch.painless.ir.FunctionNode; import org.elasticsearch.painless.ir.MemberCallNode; import org.elasticsearch.painless.ir.MemberFieldNode; +import org.elasticsearch.painless.ir.ReturnNode; import org.elasticsearch.painless.ir.StatementNode; import org.elasticsearch.painless.ir.StaticNode; import org.elasticsearch.painless.ir.ThrowNode; @@ -57,8 +58,6 @@ public class ScriptInjectionPhase { public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { - injectStaticFields(classNode); - FunctionNode executeFunctionNode = null; // look up the execute method for decoration @@ -73,13 +72,14 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { throw new IllegalStateException("all scripts must have an [execute] method"); } - injectGetsDeclarations(scriptRoot, classNode, executeFunctionNode); - injectSandboxExceptions(classNode, executeFunctionNode); + injectStaticFieldsAndGetters(classNode); + injectGetsDeclarations(scriptRoot, executeFunctionNode); + injectSandboxExceptions(executeFunctionNode); } - // adds static fields required by PainlessScript for exception handling - protected static void injectStaticFields(ClassNode classNode) { - Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectStaticFields", 0); + // adds static fields and getter methods required by PainlessScript for exception handling + protected static void injectStaticFieldsAndGetters(ClassNode classNode) { + Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectStaticFieldsAndGetters", 0); int modifiers = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; FieldNode fieldNode = new FieldNode(); @@ -105,13 +105,106 @@ protected static void injectStaticFields(ClassNode classNode) { fieldNode.setName("$STATEMENTS"); classNode.addFieldNode(fieldNode); + + FunctionNode functionNode = new FunctionNode(); + functionNode.setLocation(internalLocation); + functionNode.setName("getName"); + functionNode.setReturnType(String.class); + functionNode.setStatic(false); + functionNode.setVarArgs(false); + functionNode.setSynthetic(true); + functionNode.setMaxLoopCounter(0); + + classNode.addFunctionNode(functionNode); + + BlockNode blockNode = new BlockNode(); + blockNode.setLocation(internalLocation); + blockNode.setAllEscape(true); + blockNode.setStatementCount(1); + + functionNode.setBlockNode(blockNode); + + ReturnNode returnNode = new ReturnNode(); + returnNode.setLocation(internalLocation); + + blockNode.addStatementNode(returnNode); + + MemberFieldNode memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(String.class); + memberFieldNode.setName("$NAME"); + memberFieldNode.setStatic(true); + + returnNode.setExpressionNode(memberFieldNode); + + functionNode = new FunctionNode(); + functionNode.setLocation(internalLocation); + functionNode.setName("getSource"); + functionNode.setReturnType(String.class); + functionNode.setStatic(false); + functionNode.setVarArgs(false); + functionNode.setSynthetic(true); + functionNode.setMaxLoopCounter(0); + + classNode.addFunctionNode(functionNode); + + blockNode = new BlockNode(); + blockNode.setLocation(internalLocation); + blockNode.setAllEscape(true); + blockNode.setStatementCount(1); + + functionNode.setBlockNode(blockNode); + + returnNode = new ReturnNode(); + returnNode.setLocation(internalLocation); + + blockNode.addStatementNode(returnNode); + + memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(String.class); + memberFieldNode.setName("$SOURCE"); + memberFieldNode.setStatic(true); + + returnNode.setExpressionNode(memberFieldNode); + + functionNode = new FunctionNode(); + functionNode.setLocation(internalLocation); + functionNode.setName("getStatements"); + functionNode.setReturnType(BitSet.class); + functionNode.setStatic(false); + functionNode.setVarArgs(false); + functionNode.setSynthetic(true); + functionNode.setMaxLoopCounter(0); + + classNode.addFunctionNode(functionNode); + + blockNode = new BlockNode(); + blockNode.setLocation(internalLocation); + blockNode.setAllEscape(true); + blockNode.setStatementCount(1); + + functionNode.setBlockNode(blockNode); + + returnNode = new ReturnNode(); + returnNode.setLocation(internalLocation); + + blockNode.addStatementNode(returnNode); + + memberFieldNode = new MemberFieldNode(); + memberFieldNode.setLocation(internalLocation); + memberFieldNode.setExpressionType(BitSet.class); + memberFieldNode.setName("$STATEMENTS"); + memberFieldNode.setStatic(true); + + returnNode.setExpressionNode(memberFieldNode); } // convert gets methods to a new set of inserted ir nodes as necessary - // requires the gets method name be modified from "getExample" to "example" // if a get method variable isn't used it's declaration node is removed from // the ir tree permanently so there is no frivolous variable slotting - protected static void injectGetsDeclarations(ScriptRoot scriptRoot, ClassNode classNode, FunctionNode functionNode) { + protected static void injectGetsDeclarations(ScriptRoot scriptRoot, FunctionNode functionNode) { Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectGetsDeclarations", 0); BlockNode blockNode = functionNode.getBlockNode(); int statementIndex = 0; @@ -164,7 +257,7 @@ protected static void injectGetsDeclarations(ScriptRoot scriptRoot, ClassNode cl // } catch (PainlessError | BootstrapMethodError | OutOfMemoryError | StackOverflowError | Exception e) { // throw this.convertToScriptException(e, e.getHeaders()) // } - protected static void injectSandboxExceptions(ClassNode classNode, FunctionNode functionNode) { + protected static void injectSandboxExceptions(FunctionNode functionNode) { try { Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectSandboxExceptions", 0); BlockNode blockNode = functionNode.getBlockNode(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java index a7df00f77f40d..2defc38e83a76 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java @@ -40,12 +40,7 @@ import java.util.List; import static org.elasticsearch.painless.WriterConstants.BASE_INTERFACE_TYPE; -import static org.elasticsearch.painless.WriterConstants.BITSET_TYPE; import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -import static org.elasticsearch.painless.WriterConstants.GET_NAME_METHOD; -import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; -import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; -import static org.elasticsearch.painless.WriterConstants.STRING_TYPE; public class ClassNode extends IRNode { @@ -157,27 +152,6 @@ public byte[] write() { constructor.returnValue(); constructor.endMethod(); - // Write a method to get static variable source - MethodWriter nameMethod = classWriter.newMethodWriter(Opcodes.ACC_PUBLIC, GET_NAME_METHOD); - nameMethod.visitCode(); - nameMethod.getStatic(CLASS_TYPE, "$NAME", STRING_TYPE); - nameMethod.returnValue(); - nameMethod.endMethod(); - - // Write a method to get static variable source - MethodWriter sourceMethod = classWriter.newMethodWriter(Opcodes.ACC_PUBLIC, GET_SOURCE_METHOD); - sourceMethod.visitCode(); - sourceMethod.getStatic(CLASS_TYPE, "$SOURCE", STRING_TYPE); - sourceMethod.returnValue(); - sourceMethod.endMethod(); - - // Write a method to get static variable statements - MethodWriter statementsMethod = classWriter.newMethodWriter(Opcodes.ACC_PUBLIC, GET_STATEMENTS_METHOD); - statementsMethod.visitCode(); - statementsMethod.getStatic(CLASS_TYPE, "$STATEMENTS", BITSET_TYPE); - statementsMethod.returnValue(); - statementsMethod.endMethod(); - // Write all fields: for (FieldNode fieldNode : fieldNodes) { fieldNode.write(classWriter, null, null, null); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index 2e93d50df52e2..5dbebcf554af3 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -211,6 +211,7 @@ LambdaNode write(ClassNode classNode) { functionNode.getTypeParameters().addAll(typeParameters); functionNode.getParameterNames().addAll(parameterNames); functionNode.setStatic(true); + functionNode.setVarArgs(false); functionNode.setSynthetic(true); functionNode.setMaxLoopCounter(maxLoopCounter); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java index a232c66cec68f..5302f17d85907 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java @@ -57,7 +57,6 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { function.generateSignature(scriptRoot.getPainlessLookup()); function.analyze(scriptRoot); scriptRoot.getFunctionTable().addFunction(function.name, function.returnType, function.typeParameters, true, true); - //scriptRoot.getClassNode().addFunction(function); if (expected == null) { ref = null; From 0f33c7c13ce738d9ee05fee5a89e5ec4572c2d3a Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 9 Jan 2020 11:01:12 -0800 Subject: [PATCH 11/12] inject needs methods --- .../painless/ScriptInjectionPhase.java | 43 +++++++++++++++++++ .../elasticsearch/painless/ir/ClassNode.java | 12 ------ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java index 039e494193ef1..6fe930bb33fea 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInjectionPhase.java @@ -24,6 +24,7 @@ import org.elasticsearch.painless.ir.CallSubNode; import org.elasticsearch.painless.ir.CatchNode; import org.elasticsearch.painless.ir.ClassNode; +import org.elasticsearch.painless.ir.ConstantNode; import org.elasticsearch.painless.ir.DeclarationNode; import org.elasticsearch.painless.ir.FieldNode; import org.elasticsearch.painless.ir.FunctionNode; @@ -74,6 +75,7 @@ public static void phase(ScriptRoot scriptRoot, ClassNode classNode) { injectStaticFieldsAndGetters(classNode); injectGetsDeclarations(scriptRoot, executeFunctionNode); + injectNeedsMethods(scriptRoot, classNode); injectSandboxExceptions(executeFunctionNode); } @@ -248,6 +250,47 @@ protected static void injectGetsDeclarations(ScriptRoot scriptRoot, FunctionNode } } + // injects needs methods as defined by ScriptClassInfo + protected static void injectNeedsMethods(ScriptRoot scriptRoot, ClassNode classNode) { + Location internalLocation = new Location("$internal$ScriptInjectionPhase$injectNeedsMethods", 0); + + for (org.objectweb.asm.commons.Method needsMethod : scriptRoot.getScriptClassInfo().getNeedsMethods()) { + String name = needsMethod.getName(); + name = name.substring(5); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + FunctionNode functionNode = new FunctionNode(); + functionNode.setLocation(internalLocation); + functionNode.setName(needsMethod.getName()); + functionNode.setReturnType(boolean.class); + functionNode.setStatic(false); + functionNode.setVarArgs(false); + functionNode.setSynthetic(true); + functionNode.setMaxLoopCounter(0); + + classNode.addFunctionNode(functionNode); + + BlockNode blockNode = new BlockNode(); + blockNode.setLocation(internalLocation); + blockNode.setAllEscape(true); + blockNode.setStatementCount(1); + + functionNode.setBlockNode(blockNode); + + ReturnNode returnNode = new ReturnNode(); + returnNode.setLocation(internalLocation); + + blockNode.addStatementNode(returnNode); + + ConstantNode constantNode = new ConstantNode(); + constantNode.setLocation(internalLocation); + constantNode.setExpressionType(boolean.class); + constantNode.setConstant(scriptRoot.getUsedVariables().contains(name)); + + returnNode.setExpressionNode(constantNode); + } + } + // decorate the execute method with nodes to wrap the user statements with // the sandboxed errors as follows: // } catch (PainlessExplainError e) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java index 2defc38e83a76..fe2fb0145d888 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ClassNode.java @@ -178,18 +178,6 @@ public byte[] write() { clinit.endMethod(); } - // Write any needsVarName methods for used variables - for (org.objectweb.asm.commons.Method needsMethod : scriptClassInfo.getNeedsMethods()) { - String name = needsMethod.getName(); - name = name.substring(5); - name = Character.toLowerCase(name.charAt(0)) + name.substring(1); - MethodWriter ifaceMethod = classWriter.newMethodWriter(Opcodes.ACC_PUBLIC, needsMethod); - ifaceMethod.visitCode(); - ifaceMethod.push(scriptRoot.getUsedVariables().contains(name)); - ifaceMethod.returnValue(); - ifaceMethod.endMethod(); - } - // End writing the class and store the generated bytes. classVisitor.visitEnd(); From c11d88212df2ad43c04eb5951d776650d145da5e Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 19 Feb 2020 11:24:34 -0800 Subject: [PATCH 12/12] removed extraneous constants --- .../painless/WriterConstants.java | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index fd0491977ef12..c3e3b5108af01 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -19,11 +19,7 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.api.Augmentation; -import org.elasticsearch.painless.lookup.PainlessLookup; -import org.elasticsearch.painless.symbol.FunctionTable; import org.elasticsearch.script.JodaCompatibleZonedDateTime; -import org.elasticsearch.script.ScriptException; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -34,11 +30,8 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.time.ZonedDateTime; -import java.util.BitSet; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import java.util.Map; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,47 +45,20 @@ public final class WriterConstants { public static final int ASM_VERSION = Opcodes.ASM5; public static final String BASE_INTERFACE_NAME = PainlessScript.class.getName(); public static final Type BASE_INTERFACE_TYPE = Type.getType(PainlessScript.class); - public static final Method CONVERT_TO_SCRIPT_EXCEPTION_METHOD = getAsmMethod(ScriptException.class, "convertToScriptException", - Throwable.class, Map.class); public static final String CLASS_NAME = BASE_INTERFACE_NAME + "$Script"; - public static final Type CLASS_TYPE = Type.getObjectType(CLASS_NAME.replace('.', '/')); + public static final Type CLASS_TYPE = Type.getObjectType(CLASS_NAME.replace('.', '/')); public static final String CTOR_METHOD_NAME = ""; public static final Method CLINIT = getAsmMethod(void.class, ""); - public static final String GET_NAME_NAME = "getName"; - public static final Method GET_NAME_METHOD = getAsmMethod(String.class, GET_NAME_NAME); - - public static final String GET_SOURCE_NAME = "getSource"; - public static final Method GET_SOURCE_METHOD = getAsmMethod(String.class, GET_SOURCE_NAME); - - public static final String GET_STATEMENTS_NAME = "getStatements"; - public static final Method GET_STATEMENTS_METHOD = getAsmMethod(BitSet.class, GET_STATEMENTS_NAME); - - // All of these types are caught by the main method and rethrown as ScriptException - public static final Type PAINLESS_ERROR_TYPE = Type.getType(PainlessError.class); - public static final Type BOOTSTRAP_METHOD_ERROR_TYPE = Type.getType(BootstrapMethodError.class); - public static final Type OUT_OF_MEMORY_ERROR_TYPE = Type.getType(OutOfMemoryError.class); - public static final Type STACK_OVERFLOW_ERROR_TYPE = Type.getType(StackOverflowError.class); - public static final Type EXCEPTION_TYPE = Type.getType(Exception.class); - public static final Type PAINLESS_EXPLAIN_ERROR_TYPE = Type.getType(PainlessExplainError.class); - public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", PainlessLookup.class); + public static final Type PAINLESS_ERROR_TYPE = Type.getType(PainlessError.class); public static final Type OBJECT_TYPE = Type.getType(Object.class); - public static final Type BITSET_TYPE = Type.getType(BitSet.class); - - public static final Type DEFINITION_TYPE = Type.getType(PainlessLookup.class); - - public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class); - public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap"); public static final MethodType NEEDS_PARAMETER_METHOD_TYPE = MethodType.methodType(boolean.class); - public static final Type MAP_TYPE = Type.getType(Map.class); - public static final Method MAP_GET = getAsmMethod(Object.class, "get", Object.class); - public static final Type ITERATOR_TYPE = Type.getType(Iterator.class); public static final Method ITERATOR_HASNEXT = getAsmMethod(boolean.class, "hasNext"); public static final Method ITERATOR_NEXT = getAsmMethod(Object.class, "next"); @@ -101,16 +67,10 @@ public final class WriterConstants { public static final Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class); public static final Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class); - public static final Type FUNCTION_TABLE_TYPE = Type.getType(FunctionTable.class); - // TODO: remove this when the transition from Joda to Java datetimes is completed public static final Method JCZDT_TO_ZONEDDATETIME = getAsmMethod(ZonedDateTime.class, "JCZDTToZonedDateTime", JodaCompatibleZonedDateTime.class); - public static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); - - public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class); - /** * A Method instance for {@linkplain Pattern}. This isn't available from PainlessLookup because we intentionally don't add it * there so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building @@ -126,9 +86,6 @@ public final class WriterConstants { String.class, MethodType.class, int.class, int.class, Object[].class); static final Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), "$bootstrapDef", DEF_BOOTSTRAP_METHOD.getDescriptor(), false); - public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class); - public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class, - FunctionTable.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); public static final Type DEF_UTIL_TYPE = Type.getType(Def.class); @@ -172,8 +129,6 @@ public final class WriterConstants { // TODO: remove this when the transition from Joda to Java datetimes is completed public static final Method DEF_TO_ZONEDDATETIME = getAsmMethod(ZonedDateTime.class, "defToZonedDateTime", Object.class); - public static final Type DEF_ARRAY_LENGTH_METHOD_TYPE = Type.getMethodType(Type.INT_TYPE, Type.getType(Object.class)); - /** invokedynamic bootstrap for lambda expression/method references */ public static final MethodType LAMBDA_BOOTSTRAP_TYPE = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class,