summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/AddConstructors.java
blob: 160839c6d87a84a8d751260a79ab7bceddda3f65 (plain) (tree)

















































                                                                              
                                                                               









                                                  


                                                      










                                                                 

                                                                              

     



                                                                                 

                                                               
                                        



                                                                               






                                                                        


                                                  
 









                                                                 


                                             

                                      






                                                               

                                                                          


                                           




                                                                  

                                                       
                                                           









                                                                            
             






                                                                                 
                                                   




                                                                                      

                                                                           












                                                                             

                                                            
                                                     

                                                                

                                         











                                                                            

                                                                          
















                                                                                  
                         
 


                                     
                          
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
**                                                                      **
\*                                                                      */

// $Id$

package scalac.transformer;

import scalac.Global;
import scalac.util.Name;

import scalac.ast.Tree;
import Tree.*;
import scalac.ast.Transformer;

import scalac.symtab.Type;
import scalac.symtab.Symbol;
import scalac.symtab.TermSymbol;
import scalac.symtab.Scope;
import scalac.symtab.Kinds;
import scalac.symtab.Modifiers;

import java.util.HashMap;
import java.util.HashSet;
import java.util.ArrayList;

import scalac.util.Debug;

/**
 * 1. For each class add a single method (DefDef) designated as
 * constructor whose parameters are the class parameters. The
 * constructor contains:
 *   - call to the constructor of the supertype with the appropriate arguments
 *   - initialize the fields corresponding to the class ValDefs
 *   - the bodies of the class level expressions;
 *     they are also removed from the ClassDef body
 *
 * 2. The right hand sides of the ValDefs are left empty
 *
 * 3. Substitute the appropriate constructor symbol into the
 * 'new' expressions
 *
 * @author Nikolay Mihaylov
 * @version 1.2
*/

public class AddConstructors extends Transformer {
    public final static Name CTOR_N = Name.fromString("<init>").toConstrName();

    // True iff we generate code for INT backend.
    protected final boolean forINT;
    // True iff we generate code for JVM backend.
    protected final boolean forJVM;
    // True iff we generate code for MSIL backend.
    protected final boolean forMSIL;

    final HashMap constructors;

    public AddConstructors(Global global,
			   AddConstructorsPhase descr,
			   HashMap constructors) {
	super(global, descr);
        this.constructors = constructors;
        this.forINT = global.target == global.TARGET_INT;
        this.forJVM = global.target == global.TARGET_JVM;
        this.forMSIL = global.target == global.TARGET_MSIL;
    }

    /** return new constructor symbol if it isn't already defined
     */
    Symbol getConstructor(Symbol classConstr) {
	return getConstructor(classConstr,
			      (((Type.MethodType)classConstr.info()).vparams),
			      classConstr.primaryConstructorClass());
    }

    Symbol getConstructor(Symbol classConstr, Symbol[] paramSyms, Symbol owner) {
  	assert classConstr.isConstructor() :
  	    "Class constructor expected: " + Debug.show(classConstr);

	Symbol constr = (Symbol) constructors.get(classConstr);
	if (constr == null) {
	    assert !owner.isInterface();
            int flags = forJVM
                ? classConstr.flags & (Modifiers.PRIVATE | Modifiers.PROTECTED)
                : classConstr.flags;
	    constr =
                new TermSymbol(classConstr.pos, CTOR_N, owner, flags);

	    Type constrType = Type.MethodType
		(paramSyms, forJVM ?
		 global.definitions.UNIT_TYPE : owner.type()).erasure();

	    constr.setInfo(constrType.erasure());
	    constructors.put(classConstr, constr);
	    constructors.put(constr, constr);
	}

	return constr;
    }

    /** process the tree
     */
    public Tree transform(Tree tree) {
	Symbol treeSym = tree.symbol();
	switch (tree) {
	case ClassDef(_, _, _, ValDef[][] vparams, _, //:
		      Template(Tree[] baseClasses, Tree[] body)):

	    assert treeSym.name.isTypeName();

	    if (treeSym.isInterface())
		return tree;

	    Symbol[] paramSyms = new Symbol[vparams[0].length];
	    for (int i = 0; i < paramSyms.length; i++)
		paramSyms[i] = vparams[0][i].symbol();

	    ArrayList constrBody = new ArrayList();
	    ArrayList classBody = new ArrayList();
	    Symbol constrSym =
		getConstructor(treeSym.constructor(), paramSyms, treeSym);
	    Scope classScope = new Scope();
	    classScope.enter(constrSym);

	    assert constrSym.owner() == treeSym :
		"Wrong owner of the constructor: \n\tfound: " +
		Debug.show(constrSym.owner()) + "\n\texpected: " +
		Debug.show(treeSym);

	    // inline the call to the super constructor
            Type superType = treeSym.parents()[0];
            if ( !forINT || !superType.symbol().isJava()) {
		switch (baseClasses[0]) {
		case Apply(Tree fun, Tree[] args):
		    int pos = baseClasses[0].pos;
		    Tree superConstr = gen.Select
			(gen.Super(pos, superType),
			 getConstructor(fun.symbol()));
		    constrBody.add(gen.Apply(superConstr, transform(args)));
		    break;
		default: assert false;
		}
	    }

	    // for every ValDef move the initialization code into the constructor
	    for (int i = 0; i < body.length; i++) {
		Tree t = body[i];
		if (t.definesSymbol()) {
		    Symbol sym = t.symbol();
		    switch (t) {
		    case ValDef(_, _, _, Tree rhs):
			if (rhs != Tree.Empty) {
			    // !!!FIXME: revert to this.whatever when the bug is fixed
			    //Tree ident = gen.Select(gen.This(t.pos, treeSym), sym);
			    Tree ident = gen.Ident(sym);

			    constrBody.add
				(gen.Assign(t.pos, ident, transform(rhs)));
			    t = gen.ValDef(sym, Tree.Empty);
			}
			break;
		    }
		    classBody.add(transform(t));
		    classScope.enter(sym);
		} else
		    // move every class-level expression into the constructor
		    constrBody.add(transform(t));
	    }

	    // add result expression consistent with the
	    // result type of the constructor
            if (! forJVM)
                constrBody.add(gen.This(tree.pos, treeSym));
            Tree constrTree = constrBody.size() > 1 ?
                gen.Block((Tree[])constrBody.
			  toArray(new Tree[constrBody.size()])):
                (Tree) constrBody.get(0);

	    classBody.add(gen.DefDef(tree.pos, constrSym, constrTree));

	    // strip off the class constructor from parameters
	    switch (treeSym.constructor().info()) {
	    case MethodType(_, Type result):
		treeSym.constructor().
		    updateInfo(Type.MethodType(Symbol.EMPTY_ARRAY, result));
		break;
	    default : assert false;
	    }

	    Type classType =
		Type.compoundType(treeSym.parents(), classScope, treeSym);
	    Symbol classSym = treeSym.updateInfo(classType);
	    Tree[] newBody = (Tree[]) classBody.toArray(Tree.EMPTY_ARRAY);
	    return gen.ClassDef(classSym, newBody);

	// Substitute the constructor into the 'new' expressions
	case New(Template(Tree[] baseClasses, _)):
	    Tree base = baseClasses[0];
	    switch (base) {
	    case Apply(Tree fun, Tree[] args):
		return gen.New(copy.Apply
			       (base,
				gen.Ident(base.pos, getConstructor(fun.symbol())),
				transform(args)));
	    default:
		assert false;
	    }
	    break;

	} // switch(tree)

	return super.transform(tree);
    } // transform()

} // class AddConstructors