SquirrelJME/SquirrelJME

View on GitHub
modules/aot-nanocoat/src/main/java/cc/squirreljme/jvm/aot/nanocoat/common/JvmPrimitiveType.java

Summary

Maintainability
A
1 hr
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: 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.jvm.aot.nanocoat.common;

import cc.squirreljme.c.CMathOperator;
import java.util.Arrays;
import java.util.List;
import net.multiphasicapps.classfile.ClassName;
import net.multiphasicapps.classfile.FieldDescriptor;
import net.multiphasicapps.classfile.JavaType;
import net.multiphasicapps.classfile.MethodDescriptor;
import net.multiphasicapps.classfile.MethodName;
import net.multiphasicapps.classfile.MethodReference;
import net.multiphasicapps.classfile.PrimitiveType;
import net.multiphasicapps.collections.UnmodifiableList;

/**
 * Primitive types within the virtual machine.
 *
 * @since 2023/07/16
 */
public enum JvmPrimitiveType
{
    /** Integer. */
    INTEGER,
    
    /** Long. */
    LONG,
    
    /** Float. */
    FLOAT,
    
    /** Double. */
    DOUBLE,
    
    /** Object. */
    OBJECT,
    
    /** Boolean or byte. */
    BOOLEAN_OR_BYTE,
    
    /** Short. */
    SHORT,
    
    /** Character. */
    CHARACTER,
    
    /* End. */
    ;
    
    /** The Java Types available. */
    public static final List<JvmPrimitiveType> JAVA_TYPES =
        UnmodifiableList.of(Arrays.asList(JvmPrimitiveType.INTEGER,
            JvmPrimitiveType.LONG,
            JvmPrimitiveType.FLOAT,
            JvmPrimitiveType.DOUBLE,
            JvmPrimitiveType.OBJECT));
    
    /** The maximum number of Java Types. */
    public static final int NUM_JAVA_TYPES =
        JvmPrimitiveType.JAVA_TYPES.size();
    
    /**
     * Returns the descriptor to use for this type.
     * 
     * @return The descriptor to use for this type.
     * @since 2023/07/16
     */
    public FieldDescriptor descriptor()
    {
        switch (this)
        {
            case INTEGER:
                return JavaType.INTEGER.type();
                
            case LONG:
                return JavaType.LONG.type();
            
            case FLOAT:
                return JavaType.FLOAT.type();
            
            case DOUBLE:
                return JavaType.DOUBLE.type();
                
                /* {@squirreljme.error NCa3 Has no valid descriptor.} */
            default:
                throw new IllegalArgumentException("NCa3");
        }
    }
    
    /**
     * Returns the Java primitive type. 
     *
     * @return The Java primitive type.
     * @since 2023/08/09
     */
    public JvmPrimitiveType javaType()
    {
        switch (this)
        {
            case BOOLEAN_OR_BYTE:
            case SHORT:
            case CHARACTER:
                return JvmPrimitiveType.INTEGER;
        }
        
        return this;
    }
    
    /**
     * Returns the reference to a soft comparison.
     *
     * @param __op The compare operation.
     * @return The reference to the resultant method.
     * @throws NullPointerException On null arguments.
     * @since 2023/07/16
     */
    public MethodReference softCompare(JvmCompareOp __op)
        throws NullPointerException
    {
        if (__op == null)
            throw new NullPointerException("NARG");
        
        // Which class do we use?
        ClassName useClass = this.softMathClass();
        
        // Name of the operation?
        String methodName;
        switch (__op)
        {
            case CMP:
                methodName = "cmp";
                break;
                
            case CMPG:
                methodName = "cmpg";
                break;
                
            case CMPL:
                methodName = "cmpl";
                break;
                
                /* {@squirreljme.error NCa7 Not valid for software math.} */
            default:
                throw new IllegalArgumentException("NCa7");
        }
        
        // Build
        FieldDescriptor self = this.descriptor();
        return new MethodReference(useClass,
            new MethodName(methodName),
            MethodDescriptor.ofArguments(self, self, FieldDescriptor.INTEGER),
            false);
    }
    
    /**
     * Returns the method to use for software comparison.
     *
     * @param __to The target type.
     * @return The reference to convert the type.
     * @throws NullPointerException On null arguments.
     * @since 2023/07/16
     */
    public MethodReference softConvert(JvmPrimitiveType __to)
        throws NullPointerException
    {
        if (__to == null)
            throw new NullPointerException("NARG");
        
        // Which class do we use?
        ClassName useClass = this.softMathClass();
        
        // Which method to call?
        String methodName;
        switch (__to)
        {
            case INTEGER:
                methodName = "toInteger";
                break;
                
            case LONG:
                methodName = "toLong";
                break;
            
            case FLOAT:
                methodName = "toFloat";
                break;
            
            case DOUBLE:
                methodName = "toDouble";
                break;
                
                /* {@squirreljme.error NCad Not valid for software compare.} */
            default:
                throw new IllegalArgumentException("NCad");
        }
        
        // Build
        return new MethodReference(useClass,
            new MethodName(methodName),
            MethodDescriptor.ofArguments(__to.descriptor(), this.descriptor()),
            false);
    }
    
    /**
     * Determines the method to use for software math operations.
     * 
     * @param __mathOp The math operator.
     * @return The reference to the method that performs software math for
     * this operation and type.
     * @throws NullPointerException On null arguments.
     * @since 2023/07/16
     */
    public MethodReference softMath(CMathOperator __mathOp)
        throws NullPointerException
    {
        if (__mathOp == null)
            throw new NullPointerException("NARG");
        
        // Which class do we use?
        ClassName useClass = this.softMathClass();
        
        // Name of the operation?
        String methodName;
        switch (__mathOp)
        {
            case ADD:
                methodName = "add";
                break;
                
            case SUBTRACT:
                methodName = "sub";
                break;
                
            case MULTIPLY:
                methodName = "mul";
                break;
                
            case DIVIDE:
                methodName = "div";
                break;
                
            case REMAINDER:
                methodName = "rem";
                break;
                
            case AND:
                methodName = "and";
                break;
                
            case OR:
                methodName = "or";
                break;
                
            case XOR:
                methodName = "xor";
                break;
                
            case SHIFT_LEFT:
                methodName = "shl";
                break;
                
            case SHIFT_RIGHT:
                methodName = "shr";
                break;
                
                /* {@squirreljme.error NCa2 Not valid for software math.} */
            default:
                throw new IllegalArgumentException("NCa2");
        }
        
        // Determine the type of method used
        FieldDescriptor self = this.descriptor();
        MethodDescriptor type;
        switch (methodName)
        {
            case "shl":
            case "shr":
            case "ushr":
                type = MethodDescriptor.ofArguments(
                    self,
                    self, FieldDescriptor.INTEGER);
                break;
                
            default:
                type = MethodDescriptor.ofArguments(
                    FieldDescriptor.INTEGER,
                    self, self);
                break;
        }
        
        // Build
        return new MethodReference(useClass,
            new MethodName(methodName),
            type,
            false);
    }
    
    /**
     * Returns the class to use for software math.
     * 
     * @return The class name to use for the software math.
     * @since 2023/07/16
     */
    public ClassName softMathClass()
    {
        String name;
        switch (this)
        {
            case INTEGER:
                name = "cc/squirreljme/jvm/SoftInteger";
                break;
                
            case LONG:
                name = "cc/squirreljme/jvm/SoftLong";
                break;
                
            case FLOAT:
                name = "cc/squirreljme/jvm/SoftFloat";
                break;
            
            case DOUBLE:
                name = "cc/squirreljme/jvm/SoftDouble";
                break;
                
                /* {@squirreljme.error NCa1 Not valid for software math.} */
            default:
                throw new IllegalArgumentException("NCa1");
        }
        
        return new ClassName(name);
    }
    
    /**
     * Returns the method used for software negative.
     *
     * @return The method used for software negative.
     * @since 2023/07/16
     */
    public MethodReference softNegative()
    {
        // Build
        FieldDescriptor self = this.descriptor();
        return new MethodReference(this.softMathClass(),
            new MethodName("neg"),
            MethodDescriptor.ofArguments(self, self),
            false);
    }
    
    /**
     * Determines the method to use for soft shifting. 
     *
     * @param __op The operation to use.
     * @return The reference to the shifted method.
     * @throws NullPointerException On null arguments.
     * @since 2023/07/16
     */
    public MethodReference softShift(JvmShiftOp __op)
        throws NullPointerException
    {
        if (__op == null)
            throw new NullPointerException("NARG");
        
        // Which class do we use?
        ClassName useClass = this.softMathClass();
        
        // Name of the operation?
        String methodName;
        switch (__op)
        {
            case SIGNED_SHIFT_LEFT:
                methodName = "shl";
                break;
                
            case SIGNED_SHIFT_RIGHT:
                methodName = "shr";
                break;
                
            case UNSIGNED_SHIFT_RIGHT:
                methodName = "ushr";
                break;
                
                /* {@squirreljme.error NCa2 Not valid for software math.} */
            default:
                throw new IllegalArgumentException("NCa2");
        }
        
        // Build
        FieldDescriptor self = this.descriptor();
        return new MethodReference(useClass,
            new MethodName(methodName),
            MethodDescriptor.ofArguments(self,
                self, FieldDescriptor.INTEGER),
            false);
    }
    
    /**
     * Returns the primitive type associated with the given field.
     *
     * @param __fieldType The field type.
     * @return The primitive type that is used.
     * @throws IllegalArgumentException If the type is not valid.
     * @throws NullPointerException On null arguments.
     * @since 2023/08/09
     */
    public static JvmPrimitiveType of(FieldDescriptor __fieldType)
        throws IllegalArgumentException, NullPointerException
    {
        if (__fieldType == null)
            throw new NullPointerException("NARG");
        
        if (__fieldType.isPrimitive())
            return JvmPrimitiveType.of(__fieldType.primitiveType());
        return JvmPrimitiveType.OBJECT;
    }
    
    /**
     * Returns the primitive type associated with the primitive type.
     *
     * @param __primitiveType The primitive type.
     * @return The primitive type that is used.
     * @throws IllegalArgumentException If the type is not valid.
     * @throws NullPointerException On null arguments.
     * @since 2023/08/09
     */
    private static JvmPrimitiveType of(PrimitiveType __primitiveType)
        throws IllegalArgumentException, NullPointerException
    {
        if (__primitiveType == null)
            throw new NullPointerException("NARG");
        
        switch (__primitiveType)
        {
            case BYTE:
            case BOOLEAN:
                return JvmPrimitiveType.BOOLEAN_OR_BYTE;
                
            case SHORT:
                return JvmPrimitiveType.SHORT;
                
            case CHARACTER:
                return JvmPrimitiveType.CHARACTER;
                
            case INTEGER:
                return JvmPrimitiveType.INTEGER;
                
            case LONG:
                return JvmPrimitiveType.LONG;
                
            case FLOAT:
                return JvmPrimitiveType.FLOAT;
                
            case DOUBLE:
                return JvmPrimitiveType.DOUBLE;
        }
        
        throw new IllegalArgumentException("IARG " + __primitiveType);
    }
    
    /**
     * Maps the Class file Java Type to the primitive type used by NanoCoat
     *
     * @param __type The type to map.
     * @return The resultant primitive type.
     * @throws NullPointerException On null arguments.
     * @since 2023/08/10
     */
    public static JvmPrimitiveType of(JavaType __type)
        throws NullPointerException
    {
        if (__type == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error NC02 Cannot map nothing or top type.}. */ 
        if (__type.isNothing() || __type.isTop())
            throw new IllegalArgumentException("NC02");
        
        // Basic types
        if (JavaType.INTEGER.equals(__type))
            return JvmPrimitiveType.INTEGER;
        else if (JavaType.LONG.equals(__type))
            return JvmPrimitiveType.LONG;
        else if (JavaType.FLOAT.equals(__type))
            return JvmPrimitiveType.FLOAT;
        else if (JavaType.DOUBLE.equals(__type))
            return JvmPrimitiveType.DOUBLE;
        
        // Assume object otherwise
        return JvmPrimitiveType.OBJECT;
    }
}