VariableManipulator.java

package com.mackenziehigh.autumn.lang.compiler.compilers;

import com.google.common.base.Preconditions;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import com.mackenziehigh.autumn.resources.Finished;
import org.objectweb.asm.tree.InsnList;

/**
 * An instance of this class is used to generate bytecode that manipulates local-variables.
 *
 * @author Mackenzie High
 */
@Finished("2014/07/12")
final class VariableManipulator
{
    /**
     * This object manages the allocation of local variables.
     */
    private final VariableAllocator allocator;

    /**
     * This is the list of bytecode instructions that is being generated by the compiler.
     */
    private final InsnList code;

    /**
     * Sole Constructor.
     *
     * @param allocator manages the allocation of local variables.
     * @param code is the code being generated.
     */
    public VariableManipulator(final VariableAllocator allocator,
                               final InsnList code)
    {
        Preconditions.checkNotNull(allocator);
        Preconditions.checkNotNull(code);

        this.allocator = allocator;
        this.code = code;
    }

    /**
     * This method retrieves the object that manages the allocation of local variables.
     *
     * @return the allocator of local variables.
     */
    public VariableAllocator allocator()
    {
        return allocator;
    }

    /**
     * This method generates the bytecode necessary to initialize the scope.
     */
    public void initScope()
    {
        // For each variable declared in the scope, assign it a default value.
        for (String name : allocator.getVariables())
        {
            // If the variable is a parameter, skip it.
            // Parameter's do not need a default value.
            if (allocator.isParameter(name))
            {
                continue;
            }

            // Load the default value of the variable onto the operand-stack.
            // The default value varies based on the type of the variable.
            code.add(Utils.ldcDefault(allocator.typeOf(name)));

            // Assign the default value to the variable itself.
            store(name);
        }
    }

    /**
     * This method generates bytecode that loads a variable onto the operand-stack.
     *
     * @param name is the name of the variable to load.
     */
    public void load(final String name)
    {
        Preconditions.checkNotNull(name);

        // Get the type of the variable.
        final IVariableType type = allocator.typeOf(name);

        // Get the address where the variable is stored in the stack-frame.
        final int address = allocator.addressOf(name);

        // Generate bytecode that loads the variable onto the operand-stack.
        code.add(Utils.selectLoadVarInsn(type, address));
    }

    /**
     * This method generates bytecode that assigns a value to a variable.
     *
     * @param name is the name of the variable that is being assigned a value.
     */
    public void store(final String name)
    {
        Preconditions.checkNotNull(name);

        // Get the type of the variable.
        final IVariableType type = allocator.typeOf(name);

        // Get the address where the variable is stored in the stack-frame.
        final int address = allocator.addressOf(name);

        // Generate bytecode that pops a value off of the operand-stack into a variable.
        code.add(Utils.selectStoreVarInsn(type, address));
    }
}