Package com.redhat.ceylon.compiler.typechecker.analyzer

Source Code of com.redhat.ceylon.compiler.typechecker.analyzer.LocalDeclarationVisitor

/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*/
package com.redhat.ceylon.compiler.typechecker.analyzer;

import java.util.HashMap;
import java.util.Map;

import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.tree.NaturalVisitor;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/**
* Visitor which attributes qualifiers to local types, such that they will have a unique qualifier
* in their closest declaration container which is reified at runtime.
*
* For example:
*
* void m() {
*   if(true){
*     class Local(){}
*   }else{
*     class Local(){}
*   }
* }
*
* Then at runtime, since the conditional scope is gone, they will both be named Local and have
* the same method container, so equality would break. With this visitor the first one will have
* a qualifier of 1, and the second of 2, which we test in equals/hashCode, so we fix runtime
* equality for local types.
*
* @author Stéphane Épardaud <stef@epardaud.fr>
*/
public class LocalDeclarationVisitor extends Visitor implements NaturalVisitor {

    private Map<String,Integer> localNames;
    private String prefix;
   
    private void visitLocalDecl(Tree.Declaration that) {
        Declaration model = that.getDeclarationModel();
        visitLocalDeclarationModel(model);
    }

    private void visitLocalDecl(Tree.ObjectArgument that) {
        Declaration model = that.getDeclarationModel();
        visitLocalDeclarationModel(model);
    }

    private void visitLocalDeclarationModel(Declaration model) {
        if(model != null
                && !model.isToplevel()
                && !model.isMember()
                && !(model instanceof Method && model.isParameter())
                && localNames!=null){
            Integer counter = localNames.get(model.getName());
            if(counter == null)
                counter = 1;
            else
                counter = counter + 1;
            localNames.put(model.getName(), counter);
            String qualifier;
            if(prefix != null)
                qualifier = prefix + counter.toString();
            else
                qualifier = counter.toString();
            model.setQualifier(qualifier);
        }
    }

    @Override
    public void visit(Tree.TypeAliasDeclaration that) {
        // type aliases don't introduce new scopes
        visitLocalDecl(that);
        super.visit(that);
    }

    @Override
    public void visit(Tree.ClassOrInterface that) {
        ClassOrInterface model = that.getDeclarationModel();
        visitLocalDecl(that);

        Map<String,Integer> oldLocalNames = null;
        if(model != null && !model.isAlias()){
            oldLocalNames = localNames;
            localNames = new HashMap<String,Integer>();
        }
        super.visit(that);
        if(model != null && !model.isAlias()){
            localNames = oldLocalNames;
        }
    }
   
    @Override
    public void visit(Tree.ObjectDefinition that) {
        visitLocalDecl(that);
        // use the same qualifier for the object type
        if(that.getAnonymousClass() != null
                && that.getDeclarationModel() != null) {
            that.getAnonymousClass().setQualifier(that.getDeclarationModel().getQualifier());
        }

        Map<String,Integer> oldLocalNames = localNames;
        localNames = new HashMap<String,Integer>();

        super.visit(that);
       
        localNames = oldLocalNames;
    }

    @Override
    public void visit(Tree.ObjectArgument that) {
        visitLocalDecl(that);
        // use the same qualifier for the object type
        if(that.getAnonymousClass() != null
                && that.getDeclarationModel() != null) {
            that.getAnonymousClass().setQualifier(that.getDeclarationModel().getQualifier());
        }

        Map<String,Integer> oldLocalNames = localNames;
        localNames = new HashMap<String,Integer>();

        super.visit(that);
       
        localNames = oldLocalNames;
    }

    @Override
    public void visit(Tree.AnyMethod that) {
        visitLocalDecl(that);

        Map<String,Integer> oldLocalNames = localNames;
        localNames = new HashMap<String,Integer>();

        super.visit(that);
       
        localNames = oldLocalNames;
    }

    @Override
    public void visit(Tree.AttributeGetterDefinition that) {
        visitLocalDecl(that);

        Map<String,Integer> oldLocalNames = localNames;
        localNames = new HashMap<String,Integer>();

        super.visit(that);
       
        localNames = oldLocalNames;
    }

    @Override
    public void visit(Tree.AttributeSetterDefinition that) {
        // setters use the same prefix as the getter with a $setter$ prefix
       
        Map<String,Integer> oldLocalNames = localNames;
        localNames = new HashMap<String,Integer>();

        super.visit(that);
       
        localNames = oldLocalNames;
    }

    @Override
    public void visit(Tree.AttributeDeclaration that) {
        Value model = that.getDeclarationModel();
        /*
         * we need a prefix to local type qualifiers for values in toplevel attributes
         * so that we can make a difference between qualifiers for:
         *
         * Anything() toplevel1 = void(){ class Local(){} }
         * Anything() toplevel2 = void(){ class Local(){} }
         *
         * Since both local types have the same name and the same package container at runtime,
         * and there's no proper ordering in a package, so we use 1toplevel1$ as a prefix. It
         * starts with a number because there's heuristics in the runtime model that local types
         * must start with a number.
         */
        if(model != null && model.isToplevel()){
            Map<String,Integer> oldLocalNames = localNames;
            String oldPrefix = prefix;
            localNames = new HashMap<String,Integer>();
            prefix = "1"+model.getName()+"$";

            super.visit(that);
       
            localNames = oldLocalNames;
            prefix = oldPrefix;
        }else{
            super.visit(that);
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.typechecker.analyzer.LocalDeclarationVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.