SquirrelJME/SquirrelJME

View on GitHub
modules/tool-classfile/src/main/java/net/multiphasicapps/classfile/JavaType.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 net.multiphasicapps.classfile;

import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Objects;

/**
 * This represents a type which may be used on the stack or within local
 * variables within the virtual machine.
 *
 * @since 2017/07/26
 */
public final class JavaType
{
    /** The top of a long. */
    public static final JavaType TOP_LONG =
        new JavaType(1);
    
    /** The top of a double. */
    public static final JavaType TOP_DOUBLE =
        new JavaType(2);
    
    /** Top of an undefined type. */
    public static final JavaType TOP_UNDEFINED =
        new JavaType(3);
    
    /** Nothing. */
    public static final JavaType NOTHING =
        new JavaType(4);
    
    /** Integer. */
    public static final JavaType INTEGER =
        new JavaType(new FieldDescriptor("I"));
    
    /** Long. */
    public static final JavaType LONG =
        new JavaType(new FieldDescriptor("J"));
    
    /** Float. */
    public static final JavaType FLOAT =
        new JavaType(new FieldDescriptor("F"));
    
    /** Double. */
    public static final JavaType DOUBLE =
        new JavaType(new FieldDescriptor("D"));
    
    /** Plain object. */
    public static final JavaType OBJECT =
        new JavaType(new FieldDescriptor("Ljava/lang/Object;"));
    
    /** Throwable. */
    public static final JavaType THROWABLE =
        new JavaType(new FieldDescriptor("Ljava/lang/Throwable;"));
    
    /** The type this refers to. */
    protected final FieldDescriptor type;
    
    /** Internal property. */
    private final int _internal;
    
    /** String representation of this table. */
    private Reference<String> _string;
    
    /**
     * Initializes the stack map type.
     *
     * @param __cn The name of the field.
     * @throws NullPointerException On null arguments.
     * @since 2017/07/28
     */
    public JavaType(ClassName __cn)
        throws NullPointerException
    {
        this(new FieldDescriptor((__cn.isArray() ? __cn.toString() :
            "L" + __cn + ";")));
    }
    
    /**
     * Initializes the stack map type.
     *
     * @param __f The field type.
     * @throws NullPointerException On null arguments.
     * @since 2017/07/28
     */
    public JavaType(FieldDescriptor __f)
        throws IllegalStateException, NullPointerException
    {
        // Check
        if (__f == null)
            throw new NullPointerException("NARG");
        
        // Promote to integer since the VM does not have a representation for
        // types smaller than int
        PrimitiveType p = __f.primitiveType();
        if (p != null && p.promotesToInt())
            __f = new FieldDescriptor("I");
        
        // Set
        this._internal = 0;
        this.type = __f;
    }
    
    /**
     * Used internally to create special non-comparable types.
     *
     * @param __i Internal identifier.
     * @throws RuntimeException If the internal value is zero.
     * @since 2017/07/28
     */
    private JavaType(int __i)
        throws RuntimeException
    {
        // Check
        if (__i == 0)
            throw Debugging.oops();
        
        this._internal = __i;
        this.type = null;
    }
    
    /**
     * Returns the class name of the type.
     *
     * @return The type class name.
     * @since 2019/05/24
     */
    public final ClassName className()
    {
        FieldDescriptor type = this.type;
        if (type == null)
            return null;
        return type.className();
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/07/28
     */
    @Override
    public boolean equals(Object __o)
    {
        // Check
        if (!(__o instanceof JavaType))
            return false;
        
        JavaType o = (JavaType)__o;
        return this._internal == o._internal &&
            Objects.equals(this.type, o.type);
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/07/28
     */
    @Override
    public int hashCode()
    {
        return this._internal ^ Objects.hashCode(this.type);
    }
    
    /**
     * Is this an array type?
     *
     * @return True if this is an array.
     * @since 2019/05/25
     */
    public final boolean isArray()
    {
        FieldDescriptor type = this.type;
        return (type != null && type.isObject() && type.isArray());
    }
    
    /**
     * Is this an object type?
     *
     * @return {@code true} if this is an object type.
     * @since 2017/08/13
     */
    public boolean isObject()
    {
        FieldDescriptor type = this.type;
        return (type != null && type.isObject());
    }
    
    /**
     * Is this a primitive type?
     *
     * @return {@code true} if this is a primitive type.
     * @since 2017/08/13
     */
    public boolean isPrimitive()
    {
        FieldDescriptor type = this.type;
        return (type != null && type.isPrimitive());
    }
    
    /**
     * Is this the nothing type?
     *
     * @return If this is the nothing type.
     * @since 2017/10/20
     */
    public boolean isNothing()
    {
        return this.equals(JavaType.NOTHING);
    }
    
    /**
     * Is this a top type?
     *
     * @return If this is a top type or not.
     * @since 2017/09/16
     */
    public boolean isTop()
    {
        return this.equals(JavaType.TOP_LONG) ||
            this.equals(JavaType.TOP_DOUBLE) ||
            this.equals(JavaType.TOP_UNDEFINED);
    }
    
    /**
     * Is this a wide type?
     *
     * @return {@code true} if the type is a wide type.
     * @since 2017/07/28
     */
    public boolean isWide()
    {
        FieldDescriptor type = this.type;
        if (type == null)
            return false;
        PrimitiveType pt = type.primitiveType();
        return pt != null && pt.isWide();
    }
    
    /**
     * Returns the simple storage type of this type.
     * 
     * @return The simple storage type.
     * @since 2023/07/03
     */
    public final SimpleStorageType simpleStorageType()
    {
        return this.type.simpleStorageType();
    }
    
    /**
     * Returns the type that is used for the top type.
     *
     * @return The associated top type used for this type or {@code null} if
     * there is none.
     * @since 2017/07/28
     */
    public JavaType topType()
    {
        PrimitiveType pt = this.type.primitiveType();
        if (pt == null)
            return null;
        
        switch (pt)
        {
            case LONG:        return JavaType.TOP_LONG;
            case DOUBLE:    return JavaType.TOP_DOUBLE;
            default:
                return null;
        }
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/07/28
     */
    @Override
    public String toString()
    {
        // Unknown
        Reference<String> ref = this._string;
        String rv;
        
        // Cache?
        if (ref == null || null == (rv = ref.get()))
        {
            // Fixed type
            if (this.equals(JavaType.TOP_LONG))
                rv = "top-long";
            else if (this.equals(JavaType.TOP_DOUBLE))
                rv = "top-double";
            else if (this.equals(JavaType.TOP_UNDEFINED))
                rv = "top-undefined";
            else if (this.equals(JavaType.NOTHING))
                rv = "nothing";
            
            // Other
            else
                rv = this.type.toString();
            
            // Cache
            this._string = new WeakReference<>(rv);
        }
        
        return rv;
    }
    
    /**
     * Returns the type for this type.
     *
     * @return The type used.
     * @since 2017/09/19
     */
    public FieldDescriptor type()
    {
        return this.type;
    }
    
    /**
     * Returns the width of the type.
     *
     * @return The width of the type.
     * @since 2017/09/03
     */
    public int width()
    {
        return (this.isWide() ? 2 : 1);
    }
}