/*
 * This file is part of the Polyglot extensible compiler framework.
 *
 * Copyright (c) 2000-2006 Polyglot project group, Cornell University
 * 
 */

package pao.visit;

import pao.extension.PaoExt;
import pao.types.PaoTypeSystem;
import polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.frontend.Job;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.util.Position;
import polyglot.visit.AscriptionVisitor;
import polyglot.visit.NodeVisitor;

/**
 * Visitor that inserts boxing and unboxing code into the AST. 
 * <code>PaoBoxer</code> accomplishes this task by first inserting explicit
 * casts into the AST, where primitive values need to be treated as objects,
 * and then uses the {@link PaoExt#rewrite(PaoTypeSystem, NodeFactory) PaoExt.rewrite(PaoTypeSystem, NodeFactory)}
 * method to rewrite these explicit casts into appropriate boxing and
 * unboxing code.
 */
public class PaoBoxer extends AscriptionVisitor {
    public PaoBoxer(Job job, TypeSystem ts, NodeFactory nf) {
        super(job, ts, nf);
    }

    /**
     * Inserts an explicit cast if the type of expressions <code>e</code> is
     * a primitive type, and the type <code>toType</code> is a reference type.
     * For example, <code>Integer i = 42</code> will be modified to
     * <code>Integer i = (Object)42</code>. The <code>leaveCall</code> method
     * will ensure that these explicit casts are rewritten to the correct
     * boxing and unboxing code.
     */
    @Override
    public Expr ascribe(Expr e, Type toType) {
        Type fromType = e.type();

        if (toType == null) {
            return e;
        }

        Position p = e.position();

        // Insert a cast.  Translation of the cast will insert the
        // correct boxing/unboxing code.
        if (toType.isReference() && fromType.isPrimitive()) {
            return nf.Cast(p, nf.CanonicalTypeNode(p, ts.Object()), e);
        }

        return e;
    }

    /**
     * Calls the
     * {@link PaoExt#rewrite(PaoTypeSystem, NodeFactory) PaoExt.rewrite(PaoTypeSystem, NodeFactory)}
     * method to rewrite the explicit casts inserted by the 
     * <code>ascribe</code> method into correct boxing and unboxing code.
     */
    @Override
    public Node leaveCall(Node old, Node n, NodeVisitor v)
            throws SemanticException {
        n = super.leaveCall(old, n, v);

        if (n.ext() instanceof PaoExt) {
            return ((PaoExt) n.ext()).rewrite((PaoTypeSystem) typeSystem(),
                                              nodeFactory());
        }

        return n;
    }
}
