Package de.loskutov.bco.editors

Source Code of de.loskutov.bco.editors.BytecodeDocumentProvider$DocumentProxy4Debugger

package de.loskutov.bco.editors;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.BufferManager;
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileDocumentProvider;
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;

import de.loskutov.bco.BytecodeOutlinePlugin;

/**
* Overriden to get control over document content for bytecode editors
* @author Andrei
*/
public class BytecodeDocumentProvider extends ClassFileDocumentProvider {

    public BytecodeDocumentProvider(BytecodeClassFileEditor classFileEditor) {
        super();
    }


    /*
     * Overriden to get control over document content for bytecode editors
     * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)
     */
    protected boolean setDocumentContent(IDocument document,
        IEditorInput editorInput, String encoding) throws CoreException {

        if (editorInput instanceof IClassFileEditorInput) {
            IClassFile classFile = ((IClassFileEditorInput) editorInput)
                .getClassFile();

            String source = null;
            try {
                source = classFile.getSource();
            } catch (JavaModelException e) {
                // ignore, this may happen if *class* file is not on class path but inside
                // of source tree without associated source
            }
            if (source == null) {
                // this could be the case for class files which are not on the class path
                // buffer should be already opened and created in our editor->doOpenBuffer
                // method
                IBuffer buffer = BufferManager.getDefaultBufferManager().getBuffer(classFile);
                if (buffer != null) {
                    source = buffer.getContents();
                }
            }
            document.set(source);
            return true;
        }
        return super.setDocumentContent(document, editorInput, encoding);
    }

    /**
     *
     * During debug session, debugger tries to get line information for the current line
     * in the stack, and then uses this info to set cursor and select text in editor.
     *
     * The problem is, that Java debugger knows only "source" - based lines, but our editor
     * contains "bytecode" text, where the lines are NOT aligned to the source code lines.
     * (it is simply not possible).
     *
     * So if we do not change the default implementation, the selected bytecode text will
     * never match requested sourcecode lines.
     *
     * As workaround we "just" pass to the debugger another document, as one we use to
     * represent the text in our editor. This document is a proxy and just replaces
     * the implementation of "getLineInformation" method. Our implementation mapps the
     * requested sourcecode line to the bytecode line in editor.
     *
     * All other clients of this method shouldn't be affected and should receive always
     * the original document.
     */
    public IDocument getDocument(Object element) {
        IDocument document = super.getDocument(element);
        if (element instanceof IClassFileEditorInput && isDebuggerCall()) {
            IClassFileEditorInput input = (IClassFileEditorInput) element;

            return new DocumentProxy4Debugger(document, input.getClassFile());
        }
        return document;
    }

    /**
     * We are looking for two stack patterns, which both are related to debug session and
     * coming from SourceLookupFacility.display(ISourceLookupResult result, IWorkbenchPage page):
     * first is the highlighting the editor current line, corresponding to
     * the line in the bytecode stack (light gray color),
     * and second is the annotation the current debugger position (same line as before) in
     * editor (light green color)
     *
     * This is a VERY BAD and VERY DIRTY hack, but it works.
     * @return
     */
    private boolean isDebuggerCall() {
        Exception e = new Exception();
        StackTraceElement[] stackTrace = e.getStackTrace();
        boolean stackOk = true;
        // at 0 is our method name, and 1 id the "getDocument" call, so we start with 2
        for (int i = 2; i < stackTrace.length; i++) {
            StackTraceElement elt = stackTrace[i];
            switch (i) {
                case 2 :
                    stackOk = "getLineInformation".equals(elt.getMethodName())
                        || "addAnnotation".equals(elt.getMethodName());
                    break;
                case 3 :
                    stackOk = "positionEditor".equals(elt.getMethodName())
                     || "display".equals(elt.getMethodName());
                    break;
                default :
                    break;
            }
            if(! stackOk || i > 3){
                return false;
            }

            if (stackOk && i == 3) {
                IEditorPart activeEditor = PlatformUI.getWorkbench()
                    .getActiveWorkbenchWindow().getActivePage()
                    .getActiveEditor();
                if(activeEditor instanceof BytecodeClassFileEditor){
                    BytecodeClassFileEditor editor = (BytecodeClassFileEditor) activeEditor;
                    return editor.isDecompiled();
                }
            }
        }
        return false;
    }

    public IRegion getDecompiledLineInfo(IEditorInput input, int decompiledLine) {
        IDocument document = getDocument(input);
        try {
            return document.getLineInformation(decompiledLine);
        } catch (BadLocationException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return null;
    }

    /*
     * SourceLookupFacility.class: The code is responsible for the
     * positioning of current debugger line during debugging Positions the text editor for
     * the given stack frame :
     * private void positionEditor(ITextEditor editor, IStackFrame frame)
     * private IRegion getLineInformation(ITextEditor editor, int lineNumber)
     *
     * Other place for debug instruction pointer with the "wrong line" is InstructionPointerManager
     *  public void addAnnotation(ITextEditor textEditor, IStackFrame frame, Annotation annotation)
     */

    /**
     * This class is non-functional replacement for IDocument. The only one purpose is to
     * override getLineInformation() implementation for debug purposes
     */
    private static final class DocumentProxy4Debugger extends AbstractDocument {

        private final IDocument delegate;
        private final IClassFile cf;

        public DocumentProxy4Debugger(IDocument delegate, IClassFile cf) {
            super();
            this.delegate = delegate;
            this.cf = cf;
        }

        public IRegion getLineInformation(int line) throws BadLocationException {
            BytecodeSourceMapper mapper = BytecodeClassFileEditor
                .getSourceMapper();

            int decompiledLine;
            if(line < -1){
                /* this is the case if debugger does not have line information in bytecode
                 * if bytecode does not contain line info, we should at least
                 * return the line with the first method instruction.
                 */
                decompiledLine = mapper.mapDebuggerToDecompiled(cf);
            } else {
                // SourceLookupFacility decrement source line by 1
                decompiledLine = mapper.mapToDecompiled(line + 1, cf);
                if(decompiledLine == -1){
                    /*
                     * The line is from inner class (it is in another class file)
                     * the mapping does not work for inner/anon. classes, as the debugger
                     * expect that their source code is in our editor which is not the case for
                     * bytecode of inner classes => for inner classes we use another strategy
                     */
                    return BytecodeClassFileEditor.checkForInnerClass(line, cf);
                }
            }
            // editor start lines with 1
            return delegate.getLineInformation(decompiledLine + 1);
        }
    }

}
TOP

Related Classes of de.loskutov.bco.editors.BytecodeDocumentProvider$DocumentProxy4Debugger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.