Package com.redhat.ceylon.compiler.java.codegen

Source Code of com.redhat.ceylon.compiler.java.codegen.BoxingVisitor

/*
* 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.java.codegen;

import java.util.List;

import com.redhat.ceylon.compiler.typechecker.analyzer.Util;
import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Interface;
import com.redhat.ceylon.compiler.typechecker.model.IntersectionType;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.ProducedReference;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.UnionType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ArithmeticAssignmentOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ArithmeticOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AssignOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseMemberExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Bound;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CharLiteral;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ComparisonOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.EqualityOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Exists;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.FloatLiteral;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IdenticalOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.InOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IndexExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.InvocationExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IsOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LogicalAssignmentOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LogicalOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.NaturalLiteral;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.NegativeOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Nonempty;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.NotOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ParameterizedExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PositionalArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PositiveOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PostfixOperatorExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PowerOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PrefixOperatorExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedMemberExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.StaticMemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.StringLiteral;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.StringTemplate;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.WithinOp;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.sun.tools.javac.tree.JCTree.JCExpression;

public abstract class BoxingVisitor extends Visitor {

    protected abstract boolean isBooleanTrue(Declaration decl);
    protected abstract boolean isBooleanFalse(Declaration decl);
    protected abstract boolean hasErasure(ProducedType type);
    protected abstract boolean hasErasedTypeParameters(ProducedReference producedReference);
    protected abstract boolean willEraseToObject(ProducedType type);
    protected abstract boolean isTypeParameter(ProducedType type);
    protected abstract boolean isRaw(ProducedType type);
    protected abstract boolean needsRawCastForMixinSuperCall(TypeDeclaration declaration, ProducedType type);

    @Override
    public void visit(BaseMemberExpression that) {
        super.visit(that);
        // handle errors gracefully
        if(that.getDeclaration() == null)
            return;
        TypedDeclaration decl = (TypedDeclaration) that.getDeclaration();
        if(CodegenUtil.isUnBoxed(decl)
                // special cases for true/false
                || isBooleanTrue(decl)
                || isBooleanFalse(decl))
            CodegenUtil.markUnBoxed(that);
        if(CodegenUtil.isRaw(decl))
            CodegenUtil.markRaw(that);
        if(CodegenUtil.hasTypeErased(decl))
            CodegenUtil.markTypeErased(that);
        if(CodegenUtil.hasUntrustedType(decl))
            CodegenUtil.markUntrustedType(that);
    }

    @Override
    public void visit(QualifiedMemberExpression that) {
        super.visit(that);
        // handle errors gracefully
        if(that.getDeclaration() == null)
            return;
        if(that.getMemberOperator() instanceof Tree.SafeMemberOp){
            if(CodegenUtil.hasTypeErased((TypedDeclaration) that.getDeclaration()))
                CodegenUtil.markTypeErased(that);
            // we must be boxed, since safe member op "?." returns an optional type
            return;
        }
        if (Decl.isValueTypeDecl(that.getPrimary()) && CodegenUtil.isUnBoxed(that.getPrimary())) {
            // it's unboxed iff it's an unboxable type
            if(Decl.isValueTypeDecl((TypedDeclaration)that.getDeclaration()))
                CodegenUtil.markUnBoxed(that);
            if(CodegenUtil.isRaw((TypedDeclaration) that.getDeclaration()))
                CodegenUtil.markRaw(that);
            if(CodegenUtil.hasTypeErased((TypedDeclaration) that.getDeclaration()))
                CodegenUtil.markTypeErased(that);
        } else {
            propagateFromDeclaration(that, (TypedDeclaration)that.getDeclaration());
        }
        // special case for spread op, because even if the primary is erased (ex: <T> T|String), its application may not
        // be (ex: <String>), and in that case we will generate a proper Sequential<String> which is not raw at all
        if(that.getMemberOperator() instanceof Tree.SpreadOp){
            // find the return element type
            ProducedType elementType = that.getTarget().getType();
            CodegenUtil.markTypeErased(that, hasErasure(elementType));
        }
        if(ExpressionTransformer.isSuperOrSuperOf(that.getPrimary())){
            // if the target is an interface whose type arguments have been turned to raw, make this expression
            // as erased
            ProducedReference target = that.getTarget();
            if(target != null
                    && target.getQualifyingType() != null
                    && target.getQualifyingType().getDeclaration() instanceof Interface){
                if(isRaw(target.getQualifyingType())){
                    CodegenUtil.markTypeErased(that);
                }
                // See note in ClassTransformer.makeDelegateToCompanion for a similar test
                else{
                    TypeDeclaration declaration = target.getQualifyingType().getDeclaration();
                    if(needsRawCastForMixinSuperCall(declaration, target.getType()))
                        CodegenUtil.markTypeErased(that);
                }
            }
        }
        if(that.getPrimary().getTypeModel() != null
                && isRaw(that.getPrimary().getTypeModel())
                && that.getTarget().getDeclaration() instanceof TypedDeclaration
                && CodegenUtil.containsTypeParameter(((TypedDeclaration)that.getTarget().getDeclaration()).getType())){
            CodegenUtil.markTypeErased(that);
        }
    }

    @Override
    public void visit(Expression that) {
        super.visit(that);
        Term term = that.getTerm();
        propagateFromTerm(that, term);
       
        // Special case where a method reference surrounded
        // by an expression will be turned into a Callable
        // which will need to be marked boxed
        if (term instanceof MemberOrTypeExpression) {
            Tree.MemberOrTypeExpression expr = (Tree.MemberOrTypeExpression)term;
            if (expr.getDeclaration() instanceof Method) {
                that.setUnboxed(false);
            }
        }
       
    }
   
    @Override
    public void visit(InvocationExpression that) {
        super.visit(that);
        if (Util.isIndirectInvocation(that)
                && !Decl.isJavaStaticPrimary(that.getPrimary())) {
            // if the Callable is raw the invocation will be erased
            if(that.getPrimary().getTypeModel() != null
                    && isRaw(that.getPrimary().getTypeModel()))
                CodegenUtil.markTypeErased(that);
           
            // These are always boxed
            return;
        }
        if(isByteLiteral(that))
            CodegenUtil.markUnBoxed(that);
        else
            propagateFromTerm(that, that.getPrimary());
       
        // Specifically for method invocations we check if the return type is
        // a type parameter and if so if one of the type arguments is erased,
        // in that case we mark the expression itself as erased as well
        if (that.getPrimary() instanceof StaticMemberOrTypeExpression) {
            StaticMemberOrTypeExpression expr = (StaticMemberOrTypeExpression)that.getPrimary();
            if (expr.getDeclaration() instanceof Method) {
                Method mth = (Method)expr.getDeclaration();
                if (isTypeParameter(mth.getType())
                        && hasErasedTypeParameter(expr.getTarget(), expr.getTypeArguments().getTypeModels())) {
                    CodegenUtil.markTypeErased(that);
                    CodegenUtil.markUntrustedType(that);
                }
            }
        }
    }

    private boolean isByteLiteral(Tree.InvocationExpression ce) {
        // same test as in ExpressionTransformer.checkForByteLiterals
        if(ce.getPrimary() instanceof Tree.BaseTypeExpression
                && ce.getPositionalArgumentList() != null){
            java.util.List<Tree.PositionalArgument> positionalArguments = ce.getPositionalArgumentList().getPositionalArguments();
            if(positionalArguments.size() == 1){
                PositionalArgument argument = positionalArguments.get(0);
                if(argument instanceof Tree.ListedArgument
                        && ((Tree.ListedArgument) argument).getExpression() != null){
                    Term term = ((Tree.ListedArgument)argument).getExpression().getTerm();
                    if(term instanceof Tree.NegativeOp){
                        term = ((Tree.NegativeOp) term).getTerm();
                    }
                    if(term instanceof Tree.NaturalLiteral){
                        Declaration decl = ((Tree.BaseTypeExpression)ce.getPrimary()).getDeclaration();
                        if(decl instanceof Class){
                            String name = decl.getQualifiedNameString();
                            if(name.equals("ceylon.language::Byte")){
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean hasErasedTypeParameter(ProducedReference producedReference, List<ProducedType> typeArguments) {
        if (typeArguments != null){
            for (ProducedType arg : typeArguments) {
                if (hasErasure(arg) /*|| willEraseToSequential(param.getType())*/) {
                    return true;
                }
            }
        }
        return hasErasedTypeParameters(producedReference);
    }
   
    @Override
    public void visit(ParameterizedExpression that) {
        super.visit(that);
        propagateFromTerm(that, that.getPrimary());
    }

    @Override
    public void visit(IndexExpression that) {
        super.visit(that);
        // we need to propagate from the underlying method call (item/span)
        if(that.getPrimary() == null
                || that.getPrimary().getTypeModel() == null)
            return;
        ProducedType lhsModel = that.getPrimary().getTypeModel();
        if(lhsModel.getDeclaration() == null)
            return;
        String methodName = that.getElementOrRange() instanceof Tree.Element ? "get" : "span";
        // find the method from its declaration
        TypedDeclaration member = (TypedDeclaration) lhsModel.getDeclaration().getMember(methodName, null, false);
        if(member == null)
            return;
        propagateFromDeclaration(that, member);
    }

    @Override
    public void visit(NaturalLiteral that) {
        super.visit(that);
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(FloatLiteral that) {
        super.visit(that);
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(StringLiteral that) {
        super.visit(that);
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(CharLiteral that) {
        super.visit(that);
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(StringTemplate that) {
        super.visit(that);
        // for now we always produce an unboxed string in ExpressionTransformer
        CodegenUtil.markUnBoxed(that);
    }
   
    @Override
    public void visit(PositiveOp that) {
        super.visit(that);
        // we are unboxed if our term is
        propagateBoxingFromTerm(that, that.getTerm());
    }

    @Override
    public void visit(NegativeOp that) {
        super.visit(that);
        // we are unboxed if our term is
        propagateBoxingFromTerm(that, that.getTerm());
    }

    @Override
    public void visit(ArithmeticOp that) {
        super.visit(that);
        // can't optimise the ** operator in Java
        if(that instanceof PowerOp) {
            if (Strategy.inlinePowerAsMultiplication((PowerOp)that)) {
                CodegenUtil.markUnBoxed(that);
            }
            return;
        }
        // we are unboxed if both terms are
        if(that.getLeftTerm().getUnboxed()
                && that.getRightTerm().getUnboxed())
            CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(ArithmeticAssignmentOp that) {
        super.visit(that);
        // we are unboxed if both terms are
        if(that.getLeftTerm().getUnboxed()
                && that.getRightTerm().getUnboxed())
            CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(PostfixOperatorExpression that) {
        super.visit(that);
        // we are unboxed if the term is
        propagateBoxingFromTerm(that, that.getTerm());
    }

    @Override
    public void visit(PrefixOperatorExpression that) {
        super.visit(that);
        // we are unboxed if the term is
        propagateBoxingFromTerm(that, that.getTerm());
    }

    @Override
    public void visit(NotOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }
   
    @Override
    public void visit(LogicalOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }
   
    @Override
    public void visit(AssignOp that) {
        super.visit(that);
        propagateFromTerm(that, that.getLeftTerm());
    }

    @Override
    public void visit(LogicalAssignmentOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(EqualityOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(IdenticalOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(ComparisonOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(WithinOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(Bound that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(InOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(IsOp that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(Nonempty that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    @Override
    public void visit(Exists that) {
        super.visit(that);
        // this is not conditional
        CodegenUtil.markUnBoxed(that);
    }

    private void propagateFromDeclaration(Term that, TypedDeclaration decl) {
        if(CodegenUtil.isUnBoxed(decl))
            CodegenUtil.markUnBoxed(that);
        if(CodegenUtil.isRaw(decl))
            CodegenUtil.markRaw(that);
        if(CodegenUtil.hasTypeErased(decl))
            CodegenUtil.markTypeErased(that);
        if(CodegenUtil.hasUntrustedType(decl) || hasTypeParameterWithConstraintsOutsideScope(decl.getType(), that.getScope()))
            CodegenUtil.markUntrustedType(that);
    }

    /**
     * Only for things that can't produce raw/erased types, such as math operators because
     * those are always casted
     */
    private void propagateBoxingFromTerm(Term that, Term term) {
        if(CodegenUtil.isUnBoxed(term))
            CodegenUtil.markUnBoxed(that);
    }
   
    private void propagateFromTerm(Term that, Term term) {
        if(CodegenUtil.isUnBoxed(term))
            CodegenUtil.markUnBoxed(that);
        if(CodegenUtil.isRaw(term))
            CodegenUtil.markRaw(that);
        if(CodegenUtil.hasTypeErased(term))
            CodegenUtil.markTypeErased(that);
        if(CodegenUtil.hasUntrustedType(term))
            CodegenUtil.markUntrustedType(that);
    }

    private boolean hasTypeParameterWithConstraintsOutsideScope(ProducedType type, Scope scope) {
        return hasTypeParameterWithConstraintsOutsideScopeResolved(type.resolveAliases(), scope);
    }
   
    private boolean hasTypeParameterWithConstraintsOutsideScopeResolved(ProducedType type, Scope scope) {
        if(type == null)
            return false;
        TypeDeclaration declaration = type.getDeclaration();
        if(declaration == null)
            return false;
        if(declaration instanceof UnionType){
            UnionType ut = (UnionType) declaration;
            java.util.List<ProducedType> caseTypes = ut.getCaseTypes();
            for(ProducedType pt : caseTypes){
                if(hasTypeParameterWithConstraintsOutsideScopeResolved(pt, scope))
                    return true;
            }
            return false;
        }
        if(declaration instanceof IntersectionType){
            IntersectionType ut = (IntersectionType) declaration;
            java.util.List<ProducedType> satisfiedTypes = ut.getSatisfiedTypes();
            for(ProducedType pt : satisfiedTypes){
                if(hasTypeParameterWithConstraintsOutsideScopeResolved(pt, scope))
                    return true;
            }
            return false;
        }
        if(declaration instanceof TypeParameter){
            // only look at it if it is defined outside our scope
            Scope typeParameterScope = declaration.getContainer();
            while(scope != null){
                if (Decl.equalScopes(scope,  typeParameterScope))
                    return false;
                scope = scope.getContainer();
            }
            TypeParameter tp = (TypeParameter) declaration;
            Boolean nonErasedBounds = tp.hasNonErasedBounds();
            if(nonErasedBounds == null)
                visitTypeParameter(tp);
            return nonErasedBounds != null ? nonErasedBounds.booleanValue() : false;
        }
       
        // now check its type parameters
        for(ProducedType pt : type.getTypeArgumentList()){
            if(hasTypeParameterWithConstraintsOutsideScopeResolved(pt, scope))
                return true;
        }
        // no problem here
        return false;
    }

    private void visitTypeParameter(TypeParameter typeParameter) {
        if(typeParameter.hasNonErasedBounds() != null)
            return;
        for(ProducedType pt : typeParameter.getSatisfiedTypes()){
            if(!willEraseToObject(pt)){
                typeParameter.setNonErasedBounds(true);
                return;
            }
        }
        typeParameter.setNonErasedBounds(false);
        return;
    }
   
    @Override
    public void visit(Tree.TypeLiteral that) {
        super.visit(that);
        if (!that.getWantsDeclaration()) {
            CodegenUtil.markRaw(that);
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.codegen.BoxingVisitor

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.