summaryrefslogblamecommitdiff
path: root/sources/scalac/atree/ATreeFromSTree.java
blob: cf62f951faacb02babb82057bddf539f42f01456 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                          

                           

                         
 
                   
                       
                             
                                
                                 
                            

                                
                         
                        












                                                                              





                                                                   






                                                                              



















                                                  






                                                                              



















                                                                              
                                                     





                                                     

































                                                                              
                                                                      




                                                    


                                                                              




                                                                               
                                                     



































                                                                               





























































                                                                             
                      

                                                   

                                              









                                                                          
                                                                    

                                                          
         



                                                                               












                                                                              






                                                                              
                                      
                       
 
                                       
                                                                  
                                                                                                                   

                                                                            
 
                      
                                                                         
 








                                                            
                                               


                                        


                                                                              



                                              
                                      


                                       






                                                                                              








                                                                              
















































































































































































































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

// $Id$

package scalac.atree;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import scalac.Unit;
import scalac.ast.Tree;
import scalac.ast.Tree.Ident;
import scalac.ast.Tree.Template;
import scalac.symtab.Definitions;
import scalac.symtab.Symbol;
import scalac.symtab.TermSymbol;
import scalac.symtab.Type;
import scalac.util.Debug;
import scalac.util.Name;

/** This class translates syntax trees into attributed trees. */
public class ATreeFromSTree {

    //########################################################################
    // Private Fields

    /** The global definitions */
    private final Definitions definitions;

    /** The attributed tree factory */
    private final ATreeFactory make;

    /** A mapping from primitive classes to initialization state */
    private final Map/*<Symbol,Boolean>*/ states;

    /** A mapping from primitive methods to generators */
    private final Map/*<Symbol,Generator>*/ generators;

    //########################################################################
    // Public Constructors

    /** Initializes this instance. */
    public ATreeFromSTree(Definitions definitions) {
        this.definitions = definitions;
        this.make = new ATreeFactory();
        this.states = new HashMap();
        this.generators = new HashMap();
        Symbol[] classes = {
            definitions.ANY_CLASS,
            definitions.JAVA_OBJECT_CLASS,
            definitions.JAVA_STRING_CLASS,
            definitions.JAVA_THROWABLE_CLASS,
            definitions.ARRAY_CLASS,
            definitions.UNIT_CLASS,
            definitions.BOOLEAN_CLASS,
            definitions.BYTE_CLASS,
            definitions.SHORT_CLASS,
            definitions.CHAR_CLASS,
            definitions.INT_CLASS,
            definitions.LONG_CLASS,
            definitions.FLOAT_CLASS,
            definitions.DOUBLE_CLASS,
        };
        for (int i = 0; i < classes.length; i++)
            states.put(classes[i], Boolean.FALSE);
    }

    //########################################################################
    // Public Methods - Translating units

    /** Translates the unit's body and stores the result in it. */
    public void translate(Unit unit) {
        template(unit.repository = new ARepository(), unit.body);
    }

    //########################################################################
    // Private Methods - Translating templates

    /** Translates the templates and adds them to the repository. */
    private void template(ARepository repository, Tree[] trees) {
        for (int i = 0; i < trees.length; i++) template(repository, trees[i]);
    }

    /** Translates the template and adds it to the repository. */
    private void template(ARepository repository, Tree tree) {
        switch (tree) {

        case Empty:
            return;

        case ClassDef(_, _, _, _, _, Template(_, Tree[] body)):
            AClass clasz = new AClass(tree.symbol());
            // !!! add static field to global modules
            repository.addClass(clasz);
            member(clasz, body);
            return;

        case PackageDef(_, Template(_, Tree[] body)):
            template(repository, body);
            return;

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    //########################################################################
    // Private Methods - Translating members

    /** Translates the members and adds them to the class. */
    private void member(AClass clasz, Tree[] trees) {
        for (int i = 0; i < trees.length; i++) member(clasz, trees[i]);
    }

    /** Translates the member and adds it to the class. */
    private void member(AClass clasz, Tree tree) {
        switch (tree) {

        case Empty:
            return;

        case ClassDef(_, _, _, _, _, _):
            template(clasz, tree);
            return;

        case ValDef(_, _, _, Tree rhs):
            AField field = new AField(tree.symbol(), false);
            clasz.addField(field);
            return;

        case DefDef(_, _, _, _, _, Tree rhs):
            AMethod method = new AMethod(tree.symbol(), false);
            clasz.addMethod(method);
            if (!method.isAbstract()) method.setCode(expression(rhs));
            return;

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    //########################################################################
    // Private Methods - Translating statements

     /** Translates the statements. */
    private ACode[] statement(List locals, Tree[] trees, int start, int count){
        List codes = new ArrayList();
        for (int i = start; i < start + count; i++) {
            ACode code = statement(locals, trees[i]);
            if (code != ACode.Void) codes.add(code);
        }
        return (ACode[])codes.toArray(new ACode[codes.size()]);
    }

    /** Translates the statement. */
    private ACode statement(List locals, Tree tree) {
        switch (tree) {

        case Empty:
            return make.Void;

        case ValDef(_, _, _, Tree rhs):
            Symbol symbol = tree.symbol();
            locals.add(symbol);
            ALocation location = ALocation.Local(symbol, false);
            return make.Store(tree, location, expression(rhs));

        default:
            return ACode.Drop(expression(tree), tree.type());
        }
    }

    //########################################################################
    // Private Methods - Translating expressions

    /** Translates the expressions. */
    private ACode[] expression(Tree[] trees) {
        ACode[] codes = new ACode[trees.length];
        for (int i = 0; i < codes.length; i++) codes[i] = expression(trees[i]);
        return codes;
    }

    /** Translates the expression. */
    private ACode expression(Tree tree) {
        switch (tree) {

        case LabelDef(_, Ident[] idents, Tree rhs):
            Symbol[] locals = Tree.symbolOf(idents);
            return make.Label(tree, tree.symbol(), locals, expression(rhs));

        case Block(Tree[] statements):
            if (statements.length == 0) return make.Void;
            int statement_count = statements.length - 1;
            List locals = new ArrayList();
            ACode[] codes = statement(locals,statements,0, statement_count);
            ACode value = expression(statements[statement_count]);
            if (locals.size() == 0 && codes.length == 0) return value;
            Symbol[] symbols =
                (Symbol[])locals.toArray(new Symbol[locals.size()]);
            return make.Block(tree, symbols, codes, value);

        case Assign(Tree lhs, Tree rhs):
            return make.Block(tree, Symbol.EMPTY_ARRAY, new ACode[] {
                make.Store(tree, location(lhs), expression(rhs))},
                make.Void);

        case If(Tree cond, Tree thenp, Tree elsep):
            ACode test = expression(cond);
            return make.If(tree, test, expression(thenp), expression(elsep));

        case Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise):
            int[][] tagss = new int[tags.length][];
            for (int i = 0; i < tagss.length; i++)
                tagss[i] = new int[] {tags[i]};
            ACode[] codes = new ACode[bodies.length + 1];
            for (int i = 0; i < bodies.length; i++)
                codes[i] = expression(bodies[i]);
            codes[tags.length] = expression(otherwise);
            return make.Switch(tree, expression(test), tagss, codes);

        case Return(Tree value):
            return make.Return(tree, tree.symbol(), expression(value));

        case Throw(Tree value):
            return make.Throw(tree, expression(value));

        case New(Template(Tree[] bases, _)):
            switch (bases[0]) {
            case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs):
                return apply(tree, method(fun), targs, vargs);
            case Apply(Tree fun, Tree[] vargs):
                return apply(tree, method(fun), Tree.EMPTY_ARRAY, vargs);
            default:
                throw Debug.abort("illegal case", bases[0]);
            }

        case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs):
            return apply(tree, fun, targs, vargs);
        case Apply(Tree fun, Tree[] vargs):
            return apply(tree, fun, Tree.EMPTY_ARRAY, vargs);

        case Super(_, _):
        case This(_):
            return make.This(tree, tree.symbol());

        case Select(_, _):
        case Ident(_):
            return make.Load(tree, location(tree));

        case Literal(AConstant value):
            return make.Constant(tree, value);

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    /** Translates the application. */
    private ACode apply(Tree tree, Tree fun, Tree[] targs, Tree[] vargs) {
        switch (fun) {
        case Ident(_):
            return make.Goto(tree, fun.symbol(), expression(vargs));
        default:
            return apply(tree, method(fun), targs, vargs);
        }
    }

    /** Translates the application. */
    private ACode apply(Tree tree, AFunction function,Tree[]targs,Tree[]vargs){
        Type[] types = Tree.typeOf(targs);
        switch (function) {
        case Method(ACode object, Symbol method, AInvokeStyle style):
            if (!style.isDynamic()) break;
            Symbol clasz = method.owner();
            Object state = states.get(clasz);
            if (state == null) break;
            if (state != Boolean.TRUE) addGeneratorsOf(clasz);
            Object generator = generators.get(method);
            if (generator == null) break;
            return generate((Generator)generator, tree, object, types, vargs);
        }
        return make.Apply(tree, function, types, expression(vargs));
    }

    //########################################################################
    // Private Methods - Translating functions

    /** Translates the method. */
    private AFunction method(Tree tree) {
        Symbol symbol = tree.symbol();
        switch (tree) {

        case Select(Tree qualifier, _):
            if (symbol.isJava() && symbol.owner().isModuleClass())
                return AFunction.Method(make.Void, symbol, AInvokeStyle.StaticClass); // !!! qualifier is ignored !
            ACode object = expression(qualifier);
            return AFunction.Method(object, symbol, invokeStyle(qualifier));

        case Ident(_):
            return AFunction.Method(make.Void, symbol, AInvokeStyle.New);

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    /** Returns the InvokeStyle to use for the qualifier. */
    private AInvokeStyle invokeStyle(Tree qualifier) {
        switch (qualifier) {
        case Super(_, _):
            return AInvokeStyle.StaticInstance;
        default:
            return AInvokeStyle.Dynamic;
        }
    }

    //########################################################################
    // Private Methods - Translating locations

    /** Translates the location. */
    private ALocation location(Tree tree) {
        Symbol symbol = tree.symbol();
        switch (tree) {

        case Select(Tree qualifier, _):
            if (symbol.isModule())
                return ALocation.Module(symbol); // !!! qualifier is ignored !
            if (symbol.isJava() && symbol.owner().isModuleClass())
                return ALocation.Field(make.Void, symbol, true); // !!! qualifier is ignored !
            return ALocation.Field(expression(qualifier), symbol, false);

        case Ident(_):
            if (symbol.isModule()) return ALocation.Module(symbol);
            return ALocation.Local(symbol, symbol.isParameter());

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    //########################################################################
    // Private Methods - Translating constants

    /** Translates the constant. */
    private AConstant constant(Object value) {
        if (value instanceof Boolean  ) return make.BOOLEAN((Boolean  )value);
        if (value instanceof Byte     ) return make.BYTE   (((Byte    )value));
        if (value instanceof Short    ) return make.SHORT  ((Short    )value);
        if (value instanceof Character) return make.CHAR   ((Character)value);
        if (value instanceof Integer  ) return make.INT    ((Integer  )value);
        if (value instanceof Long     ) return make.LONG   ((Long     )value);
        if (value instanceof Float    ) return make.FLOAT  ((Float    )value);
        if (value instanceof Double   ) return make.DOUBLE ((Double   )value);
        if (value instanceof String   ) return make.STRING ((String   )value);
        throw Debug.abort("illegal constant", value +" -- "+ value.getClass());
    }

    //########################################################################
    // Private Methods - Generating code for primitive methods

    /** Applies generator to given object and arguments. */
    private ACode generate(Generator generator, Tree tree, ACode object,
        Type[] targs, Tree[] vargs)
    {
        switch (generator) {

        case ANYID:
            assert targs.length == 0 && vargs.length == 1: tree;
            return make.EQ(tree, ATypeKind.REF, object, expression(vargs[0]));

        case ANYEQ:
            Symbol lf = newLocal(tree, definitions.ANY_TYPE());
            Symbol rg = newLocal(tree, definitions.ANY_TYPE());
            return make.Block(tree,
                new Symbol[] {lf, rg},
                new ACode[] {
                    store(tree, lf, object),
                    store(tree, rg, expression(vargs[0]))},
                make.If(tree,
                    make.EQ(tree, ATypeKind.REF, load(tree, lf)),
                    make.EQ(tree, ATypeKind.REF, load(tree, rg)),
                    make.Apply(tree,
                        AFunction.Method(
                            load(tree, lf),
                            definitions.ANY_EQUALS,
                            AInvokeStyle.Dynamic),
                        Type.EMPTY_ARRAY,
                        new ACode[] {load(tree, rg)})));

        case ANYNE:
            Symbol lf = newLocal(tree, definitions.ANY_TYPE());
            Symbol rg = newLocal(tree, definitions.ANY_TYPE());
            return make.Block(tree,
                new Symbol[] {lf, rg},
                new ACode[] {
                    store(tree, lf, object),
                    store(tree, rg, expression(vargs[0]))},
                make.If(tree,
                    make.EQ(tree, ATypeKind.REF, load(tree, lf)),
                    make.NE(tree, ATypeKind.REF, load(tree, rg)),
                    make.NOT(tree,
                        ATypeKind.BOOL,
                        make.Apply(tree,
                            AFunction.Method(
                                load(tree, lf),
                                definitions.ANY_EQUALS,
                                AInvokeStyle.Dynamic),
                            Type.EMPTY_ARRAY,
                            new ACode[] {load(tree, rg)}))));

        case ISAS(boolean cast):
            assert targs.length == 1 && vargs.length == 0: tree;
            return make.IsAs(tree, object, targs[0], cast);

        case SYNCHRONIZED:
            assert targs.length == 1 && vargs.length == 1: tree;
            return make.Synchronized(tree, object, expression(vargs[0]));

        case THROW:
            assert targs.length == 0 && vargs.length == 0: tree;
            return make.Throw(tree, object);

        case CONCAT(ATypeKind prefix):
            assert targs.length == 0 && vargs.length == 1: tree;
            ATypeKind suffix = kind(vargs[0].type());
            ACode argument = expression(vargs[0]);
            return make.CONCAT(tree, prefix, suffix, object, argument);

        default:
            throw Debug.abort("unknown case", generator);
        }
    }

    /** Generates a load operation with given variable. */
    private ACode load(Tree tree, Symbol local) {
        assert local.owner().isNone(): Debug.show(local);
        return make.Load(tree, ALocation.Local(local, false));
    }

    /** Generates a store operation with given variable and value. */
    private ACode store(Tree tree, Symbol local, ACode value) {
        assert local.owner().isNone(): Debug.show(local);
        return make.Store(tree, ALocation.Local(local, false), value);
    }

    /** Creates a variable with tree's position and given type. */
    private Symbol newLocal(Tree tree, Type type) {
        Symbol owner = Symbol.NONE; // !!!
        Name name = Name.fromString("local"); // !!!
        return new TermSymbol(tree.pos, name, owner, 0).setType(type);
    }

    /** Returns the type kind of given type. */
    private ATypeKind kind(Type type) {
        switch (type) {
        case SingleType(_, _):
        case ConstantType(_, _):
            return kind(type.singleDeref());
        case TypeRef(_, Symbol clasz, _):
            if (clasz == definitions.BOOLEAN_CLASS) return ATypeKind.BOOL;
            if (clasz == definitions.BYTE_CLASS) return ATypeKind.I1;
            if (clasz == definitions.SHORT_CLASS) return ATypeKind.I2;
            if (clasz == definitions.CHAR_CLASS) return ATypeKind.U2;
            if (clasz == definitions.INT_CLASS) return ATypeKind.I4;
            if (clasz == definitions.LONG_CLASS) return ATypeKind.I8;
            if (clasz == definitions.FLOAT_CLASS) return ATypeKind.R4;
            if (clasz == definitions.DOUBLE_CLASS) return ATypeKind.R8;
            if (clasz == definitions.JAVA_STRING_CLASS) return ATypeKind.STR;
            return ATypeKind.REF;
        default:
            return ATypeKind.REF;
        }
    }

    //########################################################################
    // Private Methods - Collecting primitive methods

    /** Associates generators to primitive methods of given class. */
    private void addGeneratorsOf(Symbol clasz) {
        if (clasz == definitions.ANY_CLASS) {
            addGenerator(definitions.ANY_EQ, Generator.ANYID);
            addGenerator(definitions.ANY_EQEQ, Generator.ANYEQ);
            addGenerator(definitions.ANY_BANGEQ, Generator.ANYNE);
            addGenerator(definitions.ANY_IS, Generator.ISAS(false));
            addGenerator(definitions.ANY_AS, Generator.ISAS(true));
        }
        if (clasz == definitions.JAVA_OBJECT_CLASS) {
            addGenerator(definitions.ANYREF_SYNCHRONIZED, Generator.SYNCHRONIZED);
        }
        if (clasz == definitions.JAVA_STRING_CLASS) {
            addGenerator(definitions.JAVA_STRING_PLUS, Generator.CONCAT(ATypeKind.STR));
        }
        if (clasz == definitions.JAVA_THROWABLE_CLASS) {
            addGenerator(definitions.JAVA_THROWABLE_THROW, Generator.THROW);
        }
        if (clasz == definitions.ARRAY_CLASS) {
            // !!! addAll(defs.ARRAY_CLASS, Names.length, Primitive.LENGTH, 1);
            // !!! addAll(defs.ARRAY_CLASS, Names.apply, Primitive.APPLY, 2);
            // !!! addAll(defs.ARRAY_CLASS, Names.update, Primitive.UPDATE, 1);
        }
        if (clasz == definitions.UNIT_CLASS) {
            // !!!
        }
        if (clasz == definitions.BOOLEAN_CLASS) {
            // !!!
        }
        if (clasz == definitions.BYTE_CLASS) {
            // !!!
        }
        if (clasz == definitions.SHORT_CLASS) {
            // !!!
        }
        if (clasz == definitions.CHAR_CLASS) {
            // !!!
        }
        if (clasz == definitions.INT_CLASS) {
            // !!!
        }
        if (clasz == definitions.LONG_CLASS) {
            // !!!
        }
        if (clasz == definitions.FLOAT_CLASS) {
            // !!!
        }
        if (clasz == definitions.DOUBLE_CLASS) {
            // !!!
        }
        states.put(clasz, Boolean.TRUE);
    }

    /** Associates given generator to given primitive method. */
    private void addGenerator(Symbol method, Generator generator) {
        generators.put(method, generator);
    }

    //########################################################################
    // Private Class - Code generators

    /** Code generators for primitive methods. */
    private static class Generator {
        case ANYID;
        case ANYEQ;
        case ANYNE;
        case ISAS(boolean cast);
        case SYNCHRONIZED;
        case THROW;
        case CONCAT(ATypeKind prefix);
    }

    //########################################################################
}