SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/cc/squirreljme/runtime/cldc/debug/CallTraceElement.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.runtime.cldc.debug;

import java.util.Objects;

/**
 * This represents a single entry within the call stack. This is used for
 * debugging purposes to determine where code has thrown an exception.
 *
 * @since 2018/02/21
 */
public final class CallTraceElement
{
    /** The class name. */
    protected final String className;
    
    /** The method name. */
    protected final String methodName;
    
    /** The method descriptor. */
    protected final String methodType;
    
    /** The execution pointer of the address. */
    protected final long address;
    
    /** The source code file. */
    protected final String file;
    
    /** The line in the file. */
    protected final int line;
    
    /** The Java byte code instruction. */
    protected final int byteCodeOp;
    
    /** The Java byte code address. */
    protected final int byteCodeAddr;
    
    /** The task ID. */
    protected final int taskId;
    
    /** The native instruction type. */
    protected final int nativeOp;
    
    /** Hash code. */
    private int _hash;
    
    /**
     * Initializes an empty call trace element.
     *
     * @since 2018/02/21
     */
    public CallTraceElement()
    {
        this(null, null, null, -1);
    }
    
    /**
     * Initializes a call trace element.
     *
     * @param __cl The class name.
     * @param __mn The method name.
     * @param __md The method descriptor.
     * @param __addr The address the method executes at.
     * @since 2018/02/21
     */
    public CallTraceElement(String __cl, String __mn, String __md, long __addr)
    {
        this(__cl, __mn, __md, __addr, null, -1);
    }
    
    /**
     * Initializes a call trace element.
     *
     * @param __cl The class name.
     * @param __mn The method name.
     * @param __md The method descriptor.
     * @param __addr The address the method executes at.
     * @param __file The file.
     * @param __line The line in the file.
     * @since 2018/04/02
     */
    public CallTraceElement(String __cl, String __mn, String __md, long __addr,
        String __file, int __line)
    {
        this(__cl, __mn, __md, __addr, __file, __line, -1, -1);
    }
    
    /**
     * Initializes a call trace element.
     *
     * @param __cl The class name.
     * @param __mn The method name.
     * @param __md The method descriptor.
     * @param __addr The address the method executes at.
     * @param __file The file.
     * @param __line The line in the file.
     * @param __jbc The Java byte code instruction used.
     * @param __jpc The Java PC address.
     * @since 2019/04/26
     */
    public CallTraceElement(String __cl, String __mn, String __md, long __addr,
        String __file, int __line, int __jbc, int __jpc)
    {
        this(__cl, __mn, __md, __addr, __file, __line, __jbc, __jpc, 0);
    }
    
    /**
     * Initializes a call trace element.
     *
     * @param __cl The class name.
     * @param __mn The method name.
     * @param __md The method descriptor.
     * @param __addr The address the method executes at.
     * @param __file The file.
     * @param __line The line in the file.
     * @param __jbc The Java byte code instruction used.
     * @param __jpc The Java PC address.
     * @param __tid The task ID.
     * @since 2019/10/05
     */
    public CallTraceElement(String __cl, String __mn, String __md, long __addr,
        String __file, int __line, int __jbc, int __jpc, int __tid)
    {
        this(__cl, __mn, __md, __addr, __file, __line, __jbc, __jpc, __tid,
            -1);
    }
    
    /**
     * Initializes a call trace element.
     *
     * @param __cl The class name.
     * @param __mn The method name.
     * @param __md The method descriptor.
     * @param __addr The address the method executes at.
     * @param __file The file.
     * @param __line The line in the file.
     * @param __jbc The Java byte code instruction used.
     * @param __jpc The Java PC address.
     * @param __tid The task ID.
     * @param __nOp The native operation.
     * @since 2021/01/24
     */
    public CallTraceElement(String __cl, String __mn, String __md, long __addr,
        String __file, int __line, int __jbc, int __jpc, int __tid, int __nOp)
    {
        this.className = __cl;
        this.methodName = __mn;
        this.methodType = __md;
        this.address = __addr;
        this.file = __file;
        this.line = __line;
        this.byteCodeOp = __jbc;
        this.byteCodeAddr = __jpc;
        this.taskId = __tid;
        this.nativeOp = __nOp;
    }
    
    /**
     * Returns the address of the element.
     *
     * @return The element address.
     * @since 2018/03/15
     */
    public final long address()
    {
        return this.address;
    }
    
    /**
     * Returns the address of the instruction at the Java byte code position.
     *
     * @return The address of the instruction in Java byte code.
     * @since 2019/04/26
     */
    public final int byteCodeAddress()
    {
        return this.byteCodeAddr;
    }
    
    /**
     * Returns the byte code instruction that was used for this.
     *
     * @return The used byte code instruction or {@code 0xFF} if it is not
     * valid or specified.
     * @since 2019/04/26
     */
    public final int byteCodeInstruction()
    {
        return this.byteCodeOp & 0xFF;
    }
    
    /**
     * Returns the name of the associated class.
     *
     * @return The associated class.
     * @since 2018/03/15
     */
    public final String className()
    {
        return this.className;
    }
    
    /**
     * {@inheritDoc}
     * @since 2018/02/21
     */
    @Override
    public final boolean equals(Object __o)
    {
        if (__o == this)
            return true;
        
        if (this.hashCode() != __o.hashCode())
            return false;
        
        if (!(__o instanceof CallTraceElement))
            return false;
        
        CallTraceElement o = (CallTraceElement)__o;
        return Objects.equals(this.className, o.className) &&
            Objects.equals(this.methodName, o.methodName) &&
            Objects.equals(this.methodType, o.methodType) &&
            Objects.equals(this.file, o.file) &&
            this.address == o.address &&
            this.line == o.line &&
            this.byteCodeOp == o.byteCodeOp &&
            this.byteCodeAddr == o.byteCodeAddr &&
            this.taskId == o.taskId && 
            this.nativeOp == o.nativeOp;
    }
    
    /**
     * Returns the source file.
     *
     * @return The source file.
     * @since 2018/04/02
     */
    public final String file()
    {
        return this.file;
    }
    
    /**
     * {@inheritDoc}
     * @since 2018/02/21
     */
    @Override
    public final int hashCode()
    {
        int rv = this._hash;
        if (rv == 0)
        {
            long address = this.address;
            this._hash = (rv = Objects.hashCode(this.className) ^
                Objects.hashCode(this.methodName) ^
                Objects.hashCode(this.methodType) ^
                Objects.hashCode(this.file) ^
                (int)((address >>> 32) | address) ^
                ~this.line +
                ~this.byteCodeOp +
                ~this.byteCodeAddr +
                ~this.taskId +
                ~this.nativeOp);
        }
        return rv;
    }
    
    /**
     * Returns the source file line.
     *
     * @return The source file line.
     * @since 2018/04/02
     */
    public final int line()
    {
        return this.line;
    }
    
    /**
     * Returns the descriptor of the method.
     *
     * @return The method descriptor.
     * @since 2018/03/15
     */
    public final String methodDescriptor()
    {
        return this.methodType;
    }
    
    /**
     * Returns the name of the method.
     *
     * @return The method name.
     * @since 2018/03/15
     */
    public final String methodName()
    {
        return this.methodName;
    }
    
    /**
     * Returns the native operation.
     * 
     * @return The native operation.
     * @since 2021/01/24
     */
    public final int nativeOp()
    {
        return this.nativeOp;
    }
    
    /**
     * Formats the call trace element but having it only represent the method
     * point without the class information.
     *
     * @return The at line string.
     * @since 2019/05/11
     */
    public final String toAtLineString()
    {
        // Get all fields to determine how to print it pretty
        String methodname = this.methodName,
            methoddescriptor = this.methodType;
        long address = this.address;
        int line = this.line;
        int jInst = this.byteCodeOp & 0xFF;
        int jAddr = this.byteCodeAddr;
        int taskId = this.taskId;
        int nativeOp = this.nativeOp;
        
        // Format it nicely
        StringBuilder sb = new StringBuilder();
        
        // Method name
        sb.append('.');
        sb.append((methodname == null ? "<unknown>" : methodname));
        
        // Method type
        if (methoddescriptor != null)
        {
            sb.append(':');
            sb.append(methoddescriptor);
        }
        
        // Task ID?
        if (taskId != 0)
        {
            sb.append(" T");
            sb.append(taskId);
        }
        
        // Execution address
        if (address != Long.MIN_VALUE)
        {
            sb.append(" @");
            
            sb.append(Long.toString(address, 16).toUpperCase());
            sb.append('h');
        }
        
        // Is there a native operation?
        if (nativeOp >= 0)
        {
            sb.append(" ^");
            sb.append(Integer.toString(nativeOp, 16).toUpperCase());
            sb.append("h/");
            sb.append(Integer.toString(nativeOp, 2).toUpperCase());
            sb.append('b');
        }
        
        // File, Line, and/or Java instruction/address
        boolean hasLine = (line >= 0),
            hasJInst = (jInst >= 0 && jInst < 0xFF),
            hasJAddr = (jAddr >= 0);
        if (hasLine || hasJInst || hasJAddr)
        {
            sb.append(" (");
            
            // Line
            boolean sp = false;
            if ((sp |= hasLine))
            {
                sb.append(':');
                sb.append(line);
            }
            
            // Java instruction info
            if (hasJInst || hasJAddr)
            {
                // Using space?
                if (sp)
                    sb.append(' ');
                
                // Write instruction
                if (hasJInst)
                    sb.append(JavaOpCodeUtils.toString(jInst));
                
                // Write address of Java operation
                if (hasJAddr)
                {
                    sb.append('@');
                    sb.append(jAddr);
                }
            }
            
            sb.append(')');
        }
        
        return sb.toString();
    }
    
    /**
     * Formats the call trace element but having it only represent the class.
     *
     * @return The class header string.
     * @since 2019/05/11
     */
    public final String toClassHeaderString()
    {
        // Get all fields to determine how to print it pretty
        String classname = this.className,
            file = this.file;
        
        // Format it nicely
        StringBuilder sb = new StringBuilder();
        
        // Class name
        sb.append((classname == null ? "<unknown>" :
            classname.replace('/', '.')));
        
        // Is this in a file?
        if (file != null)
        {
            sb.append(" (");
            sb.append(file);
            sb.append(')');
        }
        
        return sb.toString();
    }
    
    /**
     * {@inheritDoc}
     * @since 2018/03/14
     */
    @Override
    public final String toString()
    {
        // Get all fields to determine how to print it pretty
        String classname = this.className,
            methodname = this.methodName,
            methoddescriptor = this.methodType,
            file = this.file;
        long address = this.address;
        int line = this.line;
        int jbcinst = this.byteCodeOp & 0xFF;
        int jbcaddr = this.byteCodeAddr;
        int taskid = this.taskId;
        int nativeOp = this.nativeOp;
        
        // Format it nicely
        StringBuilder sb = new StringBuilder();
        
        sb.append((classname == null ? "<unknown>" : classname));
        sb.append('.');
        sb.append((methodname == null ? "<unknown>" : methodname));
        
        if (methoddescriptor != null)
        {
            sb.append(':');
            sb.append(methoddescriptor);
        }
        
        // Task ID?
        if (taskid != 0)
        {
            sb.append(" T");
            sb.append(taskid);
        }
        
        if (address != Long.MIN_VALUE)
        {
            sb.append(" @");
            
            sb.append(Long.toString(address, 16).toUpperCase());
            sb.append('h');
        }
        
        // Is there a native operation?
        if (nativeOp >= 0)
        {
            sb.append(" ^");
            sb.append(Integer.toString(nativeOp, 16).toUpperCase());
            sb.append("h/");
            sb.append(Integer.toString(nativeOp, 2).toUpperCase());
            sb.append('b');
        }
        
        // File, Line, and/or Java instruction/address
        boolean hasfile = (file != null),
            hasline = (line >= 0),
            hasjbcinst = (jbcinst > 0x00 && jbcinst < 0xFF),
            hasjbcaddr = (jbcaddr >= 0);
        if (hasfile || hasline || hasjbcinst || hasjbcaddr)
        {
            sb.append(" (");
            
            // File
            boolean sp = false;
            if ((sp |= hasfile))
                sb.append(file);
            
            // Line
            if ((sp |= hasline))
            {
                sb.append(':');
                sb.append(line);
            }
            
            // Java instruction info
            if (hasjbcinst || hasjbcaddr)
            {
                // Using space?
                if (sp)
                    sb.append(' ');
                
                // Write instruction
                if (hasjbcinst)
                    sb.append(JavaOpCodeUtils.toString(jbcinst));
                
                // Write address of Java operation
                if (hasjbcaddr)
                {
                    sb.append('@');
                    sb.append(jbcaddr);
                }
            }
            
            sb.append(')');
        }
        
        return sb.toString();
    }
}