Merge pull request #18932 from rmuir/painless_debug_exception

improve Debugger to print code even if it hits exception
This commit is contained in:
Robert Muir 2016-06-16 17:47:14 -04:00 committed by GitHub
commit d741e65da1
6 changed files with 52 additions and 30 deletions

View File

@ -22,6 +22,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.bootstrap.BootstrapInfo; import org.elasticsearch.bootstrap.BootstrapInfo;
import org.elasticsearch.painless.antlr.Walker; import org.elasticsearch.painless.antlr.Walker;
import org.elasticsearch.painless.node.SSource; import org.elasticsearch.painless.node.SSource;
import org.objectweb.asm.util.Printer;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -98,7 +99,7 @@ final class Compiler {
" plugin if a script longer than this length is a requirement."); " plugin if a script longer than this length is a requirement.");
} }
SSource root = Walker.buildPainlessTree(name, source, settings); SSource root = Walker.buildPainlessTree(name, source, settings, null);
root.analyze(); root.analyze();
root.write(); root.write();
@ -120,14 +121,14 @@ final class Compiler {
* @param settings The CompilerSettings to be used during the compilation. * @param settings The CompilerSettings to be used during the compilation.
* @return The bytes for compilation. * @return The bytes for compilation.
*/ */
static byte[] compile(String name, String source, CompilerSettings settings) { static byte[] compile(String name, String source, CompilerSettings settings, Printer debugStream) {
if (source.length() > MAXIMUM_SOURCE_LENGTH) { if (source.length() > MAXIMUM_SOURCE_LENGTH) {
throw new IllegalArgumentException("Scripts may be no longer than " + MAXIMUM_SOURCE_LENGTH + throw new IllegalArgumentException("Scripts may be no longer than " + MAXIMUM_SOURCE_LENGTH +
" characters. The passed in script is " + source.length() + " characters. Consider using a" + " characters. The passed in script is " + source.length() + " characters. Consider using a" +
" plugin if a script longer than this length is a requirement."); " plugin if a script longer than this length is a requirement.");
} }
SSource root = Walker.buildPainlessTree(name, source, settings); SSource root = Walker.buildPainlessTree(name, source, settings, debugStream);
root.analyze(); root.analyze();
root.write(); root.write();

View File

@ -22,7 +22,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Sort;
import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Definition.Type;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.GeneratorAdapter;
@ -82,7 +82,7 @@ public final class MethodWriter extends GeneratorAdapter {
private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs = private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs =
(INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>(); (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>();
public MethodWriter(int access, Method method, ClassWriter cw, BitSet statements) { public MethodWriter(int access, Method method, ClassVisitor cw, BitSet statements) {
super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null), super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null),
access, method.getName(), method.getDescriptor()); access, method.getName(), method.getDescriptor());

View File

@ -146,6 +146,7 @@ import org.elasticsearch.painless.node.SSource;
import org.elasticsearch.painless.node.SThrow; import org.elasticsearch.painless.node.SThrow;
import org.elasticsearch.painless.node.STry; import org.elasticsearch.painless.node.STry;
import org.elasticsearch.painless.node.SWhile; import org.elasticsearch.painless.node.SWhile;
import org.objectweb.asm.util.Printer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
@ -158,12 +159,13 @@ import java.util.List;
*/ */
public final class Walker extends PainlessParserBaseVisitor<Object> { public final class Walker extends PainlessParserBaseVisitor<Object> {
public static SSource buildPainlessTree(String sourceName, String sourceText, CompilerSettings settings) { public static SSource buildPainlessTree(String sourceName, String sourceText, CompilerSettings settings, Printer debugStream) {
return new Walker(sourceName, sourceText, settings).source; return new Walker(sourceName, sourceText, settings, debugStream).source;
} }
private final SSource source; private final SSource source;
private final CompilerSettings settings; private final CompilerSettings settings;
private final Printer debugStream;
private final String sourceName; private final String sourceName;
private final String sourceText; private final String sourceText;
@ -171,7 +173,8 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
private final List<SFunction> synthetic = new ArrayList<>(); private final List<SFunction> synthetic = new ArrayList<>();
private int syntheticCounter = 0; private int syntheticCounter = 0;
private Walker(String sourceName, String sourceText, CompilerSettings settings) { private Walker(String sourceName, String sourceText, CompilerSettings settings, Printer debugStream) {
this.debugStream = debugStream;
this.settings = settings; this.settings = settings;
this.sourceName = Location.computeSourceName(sourceName, sourceText); this.sourceName = Location.computeSourceName(sourceName, sourceText);
this.sourceText = sourceText; this.sourceText = sourceText;
@ -236,7 +239,8 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
functions.addAll(synthetic); functions.addAll(synthetic);
return new SSource(sourceName, sourceText, (ExecuteReserved)reserved.pop(), location(ctx), functions, statements); return new SSource(sourceName, sourceText, debugStream,
(ExecuteReserved)reserved.pop(), location(ctx), functions, statements);
} }
@Override @Override

View File

@ -31,6 +31,7 @@ import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.WriterConstants; import org.elasticsearch.painless.WriterConstants;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle; import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
@ -146,8 +147,8 @@ public class SFunction extends AStatement {
locals.addConstant(location, WriterConstants.METHOD_HANDLE_TYPE, staticHandleFieldName, this::initializeConstant); locals.addConstant(location, WriterConstants.METHOD_HANDLE_TYPE, staticHandleFieldName, this::initializeConstant);
} }
/** Writes the function to given ClassWriter. */ /** Writes the function to given ClassVisitor. */
void write (ClassWriter writer, BitSet statements) { void write (ClassVisitor writer, BitSet statements) {
int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
if (synthetic) { if (synthetic) {
access |= Opcodes.ACC_SYNTHETIC; access |= Opcodes.ACC_SYNTHETIC;

View File

@ -29,9 +29,11 @@ import org.elasticsearch.painless.Locals.Variable;
import org.elasticsearch.painless.WriterConstants; import org.elasticsearch.painless.WriterConstants;
import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.TraceClassVisitor;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collections; import java.util.Collections;
@ -53,6 +55,7 @@ public final class SSource extends AStatement {
final String name; final String name;
final String source; final String source;
final Printer debugStream;
final ExecuteReserved reserved; final ExecuteReserved reserved;
final List<SFunction> functions; final List<SFunction> functions;
final List<AStatement> statements; final List<AStatement> statements;
@ -61,12 +64,13 @@ public final class SSource extends AStatement {
private BitSet expressions; private BitSet expressions;
private byte[] bytes; private byte[] bytes;
public SSource(String name, String source, ExecuteReserved reserved, Location location, public SSource(String name, String source, Printer debugStream, ExecuteReserved reserved, Location location,
List<SFunction> functions, List<AStatement> statements) { List<SFunction> functions, List<AStatement> statements) {
super(location); super(location);
this.name = name; this.name = name;
this.source = source; this.source = source;
this.debugStream = debugStream;
this.reserved = reserved; this.reserved = reserved;
this.functions = Collections.unmodifiableList(functions); this.functions = Collections.unmodifiableList(functions);
this.statements = Collections.unmodifiableList(statements); this.statements = Collections.unmodifiableList(statements);
@ -132,13 +136,19 @@ public final class SSource extends AStatement {
String classInterfaces[] = reserved.usesScore() ? new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null; String classInterfaces[] = reserved.usesScore() ? new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null;
ClassWriter writer = new ClassWriter(classFrames); ClassWriter writer = new ClassWriter(classFrames);
writer.visit(classVersion, classAccess, className, null, classBase, classInterfaces); ClassVisitor visitor = writer;
writer.visitSource(Location.computeSourceName(name, source), null);
if (debugStream != null) {
visitor = new TraceClassVisitor(visitor, debugStream, null);
}
visitor.visit(classVersion, classAccess, className, null, classBase, classInterfaces);
visitor.visitSource(Location.computeSourceName(name, source), null);
expressions = new BitSet(source.length()); expressions = new BitSet(source.length());
// Write the constructor: // Write the constructor:
MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, writer, expressions); MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, expressions);
constructor.visitCode();
constructor.loadThis(); constructor.loadThis();
constructor.loadArgs(); constructor.loadArgs();
constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR); constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR);
@ -146,20 +156,21 @@ public final class SSource extends AStatement {
constructor.endMethod(); constructor.endMethod();
// Write the execute method: // Write the execute method:
MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, writer, expressions); MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, visitor, expressions);
execute.visitCode();
write(execute); write(execute);
execute.endMethod(); execute.endMethod();
// Write all functions: // Write all functions:
for (SFunction function : functions) { for (SFunction function : functions) {
function.write(writer, expressions); function.write(visitor, expressions);
} }
// Write the constants // Write the constants
if (false == locals.getConstants().isEmpty()) { if (false == locals.getConstants().isEmpty()) {
// Fields // Fields
for (Constant constant : locals.getConstants()) { for (Constant constant : locals.getConstants()) {
writer.visitField( visitor.visitField(
Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
constant.name, constant.name,
constant.type.getDescriptor(), constant.type.getDescriptor(),
@ -169,7 +180,7 @@ public final class SSource extends AStatement {
// Initialize the constants in a static initializer // Initialize the constants in a static initializer
final MethodWriter clinit = new MethodWriter(Opcodes.ACC_STATIC, final MethodWriter clinit = new MethodWriter(Opcodes.ACC_STATIC,
WriterConstants.CLINIT, writer, expressions); WriterConstants.CLINIT, visitor, expressions);
for (Constant constant : locals.getConstants()) { for (Constant constant : locals.getConstants()) {
constant.initializer.accept(clinit); constant.initializer.accept(clinit);
clinit.putStatic(CLASS_TYPE, constant.name, constant.type); clinit.putStatic(CLASS_TYPE, constant.name, constant.type);
@ -180,7 +191,7 @@ public final class SSource extends AStatement {
// End writing the class and store the generated bytes. // End writing the class and store the generated bytes.
writer.visitEnd(); visitor.visitEnd();
bytes = writer.toByteArray(); bytes = writer.toByteArray();
} }

View File

@ -19,8 +19,8 @@
package org.elasticsearch.painless; package org.elasticsearch.painless;
import org.objectweb.asm.ClassReader; import org.apache.lucene.util.IOUtils;
import org.objectweb.asm.util.TraceClassVisitor; import org.objectweb.asm.util.Textifier;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
@ -35,14 +35,19 @@ final class Debugger {
/** compiles to bytecode, and returns debugging output */ /** compiles to bytecode, and returns debugging output */
static String toString(String source, CompilerSettings settings) { static String toString(String source, CompilerSettings settings) {
final byte[] bytes = Compiler.compile("<debugging>", source, settings); StringWriter output = new StringWriter();
final StringWriter output = new StringWriter(); PrintWriter outputWriter = new PrintWriter(output);
final PrintWriter outputWriter = new PrintWriter(output); Textifier textifier = new Textifier();
final ClassReader reader = new ClassReader(bytes); try {
Compiler.compile("<debugging>", source, settings, textifier);
reader.accept(new TraceClassVisitor(outputWriter), 0); } catch (Exception e) {
outputWriter.flush(); textifier.print(outputWriter);
e.addSuppressed(new Exception("current bytecode: \n" + output));
IOUtils.reThrowUnchecked(e);
throw new AssertionError();
}
textifier.print(outputWriter);
return output.toString(); return output.toString();
} }
} }