Package com.redhat.ceylon.compiler.loader.model

Source Code of com.redhat.ceylon.compiler.loader.model.LazyPackage

/*
* 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.loader.model;

import static com.redhat.ceylon.compiler.typechecker.model.Util.lookupMember;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.redhat.ceylon.compiler.java.codegen.AnnotationArgument;
import com.redhat.ceylon.compiler.java.codegen.AnnotationConstructorParameter;
import com.redhat.ceylon.compiler.java.codegen.AnnotationInvocation;
import com.redhat.ceylon.compiler.java.codegen.Decl;
import com.redhat.ceylon.compiler.java.codegen.ParameterAnnotationTerm;
import com.redhat.ceylon.compiler.java.util.Util;
import com.redhat.ceylon.compiler.loader.AbstractModelLoader;
import com.redhat.ceylon.compiler.loader.ModelLoader.DeclarationType;
import com.redhat.ceylon.compiler.loader.mirror.ClassMirror;
import com.redhat.ceylon.compiler.typechecker.model.Annotation;
import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ParameterList;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.Unit;
import com.redhat.ceylon.compiler.typechecker.model.Value;

/**
* Represents a lazy Package declaration.
*
* @author Stéphane Épardaud <stef@epardaud.fr>
*/
public class LazyPackage extends Package {
   
    private AbstractModelLoader modelLoader;
    private List<Declaration> compiledDeclarations = new ArrayList<Declaration>(3);
    private Set<Unit> lazyUnits = new HashSet<Unit>();
    private Map<String,Declaration> cache = new HashMap<String,Declaration>();
   
    public LazyPackage(AbstractModelLoader modelLoader){
        this.modelLoader = modelLoader;
    }
   
    @Override
    public Declaration getMember(String name, List<ProducedType> signature, boolean ellipsis) {
        // FIXME: what use is this method in the type checker?
        return getDirectMember(name, signature, ellipsis);
    }
   
    // FIXME: redo this method better: https://github.com/ceylon/ceylon-spec/issues/90
    @Override
    public Declaration getDirectMember(String name, List<ProducedType> signature, boolean ellipsis) {
//        System.err.println("getMember "+name+" "+signature+" "+ellipsis);
        boolean canCache = (signature == null && !ellipsis);
        if(canCache){
            if(cache.containsKey(name)) {
                Declaration cachedDeclaration = cache.get(name);
                if (cachedDeclaration != null || ! modelLoader.searchAgain(this, name)) {
                    return cachedDeclaration;
                }

            }
        }
        Declaration ret = getDirectMemberMemoised(name, signature, ellipsis);
        if(canCache){
            cache.put(name, ret);
        }
        return ret;
    }
   
    private Declaration getDirectMemberMemoised(String name, List<ProducedType> signature, boolean ellipsis) {
        synchronized(modelLoader.getLock()){

            String pkgName = getQualifiedNameString();
            Module module = getModule();
           
            // we need its package ready first
            modelLoader.loadPackage(module, pkgName, false);

            // make sure we iterate over a copy of compiledDeclarations, to avoid lazy loading to modify it and
            // cause a ConcurrentModificationException: https://github.com/ceylon/ceylon-compiler/issues/399
            Declaration d = lookupMember(compiledDeclarations, name, signature, ellipsis);
            if (d != null) {
                return d;
            }

            String className = getQualifiedName(pkgName, name);
            ClassMirror classSymbol = modelLoader.lookupClassMirror(module, className);

            // only get it from the classpath if we're not compiling it, unless
            // it happens to be a java source
            if(classSymbol != null && (!classSymbol.isLoadedFromSource() || classSymbol.isJavaSource())) {
                d = modelLoader.convertToDeclaration(module, className, DeclarationType.VALUE);
                if (d instanceof Class) {
                    Class c = (Class) d;
                    if (c.isAbstraction() && signature != null) {
                        ArrayList<Declaration> list = new ArrayList<Declaration>(c.getOverloads());
                        list.add(c);
                        return lookupMember(list, name, signature, ellipsis);
                    }
                }
                return d;
            }
            d = getDirectMemberFromSource(name);
           
            if (d == null
                    && Character.isLowerCase(name.charAt(0))
                    && Character.isUpperCase(Character.toUpperCase(name.charAt(0)))) {
                // Might be trying to get an annotation constructor for a Java annotation type
                // So try to find the annotation type
                String annotationName = Character.toUpperCase(name.charAt(0)) + (name.length() > 1 ? name.substring(1) : "");
                Declaration possibleAnnotationType = getDirectMember(annotationName, signature, ellipsis);
                if (possibleAnnotationType != null
                        && possibleAnnotationType instanceof LazyInterface
                        && ((LazyInterface)possibleAnnotationType).isAnnotationType()) {
                    // addMember() will have added a Method if we found an annotation type
                    // so now we can look for the constructor again
                    d = lookupMember(compiledDeclarations, name, signature, ellipsis);
                }
            }
            return d;
        }
    }

    private Declaration getDirectMemberFromSource(String name) {
        for (Declaration d: super.getMembers()) {
            if (com.redhat.ceylon.compiler.typechecker.model.Util.isResolvable(d) /* && d.isShared() */
            && com.redhat.ceylon.compiler.typechecker.model.Util.isNamed(name, d)) {
                return d;
            }
        }
        return null;
    }

    public String getQualifiedName(final String pkgName, String name) {
        // no need to quote the name itself as java keywords are lower-cased and we append a _ to every
        // lower-case toplevel so they can never be java keywords
        String className = pkgName.isEmpty() ? name : Util.quoteJavaKeywords(pkgName) + "." + name;
        return className;
    }
   
    // FIXME: This is only here for wildcard imports, and we should be able to make it lazy like the rest
    // with a bit of work in the typechecker
    // FIXME: redo this method better: https://github.com/ceylon/ceylon-spec/issues/90
    @Override
    public List<Declaration> getMembers() {
        synchronized(modelLoader.getLock()){
            // make sure the package is loaded
            modelLoader.loadPackage(getModule(), getQualifiedNameString(), true);
            List<Declaration> sourceDeclarations = super.getMembers();
            LinkedList<Declaration> ret = new LinkedList<Declaration>();
            ret.addAll(sourceDeclarations);
            ret.addAll(compiledDeclarations);
            return ret;
        }
    }

    @Override
    public void addMember(Declaration declaration) {
        super.addMember(declaration);
        flushCache(declaration);
    }
   
    private void flushCache(Declaration declaration) {
        cache.remove(declaration.getName());
    }

    public void addCompiledMember(Declaration d) {
        synchronized(modelLoader.getLock()){
            flushCache(d);
            compiledDeclarations.add(d);
            if (d instanceof LazyInterface
                    && !((LazyInterface)d).isCeylon()
                    && ((LazyInterface)d).isAnnotationType()) {
                makeInteropAnnotation((LazyInterface)d);
               
            }
            if ((d instanceof LazyClass ||
                    d instanceof LazyInterface&& d.getUnit().getFilename() != null) {
                lazyUnits.add(d.getUnit());
            }
        }
    }

    /**
     * Adds extra members to the package for annotation interop.
     * For a Java declaration {@code @interface Annotation} we generate
     * a model corresponding to:
     * <pre>
     *   annotation class Annotation$Proxy(...) satisfies Annotation {
     *       // a `shared` class parameter for each method of Annotation
     *   }
     *   annotation JavaAnnotation javaAnnotation(...) => JavaAnnotation$Proxy(...);
     * </pre>
     * @param iface
     */
    private void makeInteropAnnotation(LazyInterface iface) {
        Class klass = new AnnotationProxyClass(iface);
        klass.setContainer(this);
        klass.setScope(this);
        klass.setName(iface.getName()+"$Proxy");
        klass.setShared(iface.isShared());
        klass.setAnnotation(true);
        Annotation annotationAnnotation = new Annotation();
        annotationAnnotation.setName("annotation");
        klass.getAnnotations().add(annotationAnnotation);
        klass.getSatisfiedTypes().add(iface.getType());
        klass.setUnit(iface.getUnit());
        ParameterList classpl = new ParameterList();
        klass.addParameterList(classpl);
       
        Method ctor = new AnnotationProxyMethod();
        ctor.setContainer(this);
        klass.setScope(this);
        ctor.setAnnotation(true);
        ctor.setName(iface.getName().substring(0, 1).toLowerCase() + iface.getName().substring(1));
        ctor.setShared(iface.isShared());
        Annotation annotationAnnotation2 = new Annotation();
        annotationAnnotation2.setName("annotation");
        ctor.getAnnotations().add(annotationAnnotation2);
        ctor.setType(((TypeDeclaration)iface).getType());
        ctor.setUnit(iface.getUnit());
       
        ParameterList ctorpl = new ParameterList();
        ctor.addParameterList(ctorpl);
       
        AnnotationInvocation ai = new AnnotationInvocation();
        ai.setConstructorDeclaration(ctor);
        ai.setPrimary(klass);
        ai.setInterop(true);
        ctor.setAnnotationConstructor(ai);
        List<AnnotationArgument> annotationArgs = new ArrayList<AnnotationArgument>();
        for (Declaration member : iface.getMembers()) {
            boolean isValue = member.getName().equals("value");
            if (member instanceof JavaMethod) {
                JavaMethod m = (JavaMethod)member;
               
                ParameterAnnotationTerm term = new ParameterAnnotationTerm();
                AnnotationArgument argument = new AnnotationArgument();
                argument.setTerm(term);
                {
                    Parameter klassParam = new Parameter();
                    Value value = new Value();
                    klassParam.setModel(value);
                    value.setInitializerParameter(klassParam);
                    klassParam.setDeclaration(klass);
                    value.setContainer(klass);
                    value.setScope(klass);
                    value.setName(member.getName());
                    klassParam.setName(member.getName());
                    value.setType(annotationParameterType(iface.getUnit(), m));
                    value.setUnboxed(true);
                    value.setUnit(iface.getUnit());
                    if(isValue)
                        classpl.getParameters().add(0, klassParam);
                    else
                        classpl.getParameters().add(klassParam);
                    argument.setParameter(klassParam);
                    klass.addMember(value);
                }
                {
                    Parameter ctorParam = new Parameter();
                    Value value = new Value();
                    ctorParam.setModel(value);
                    value.setInitializerParameter(ctorParam);
                    ctorParam.setDeclaration(ctor);
                    value.setContainer(klass);
                    value.setScope(klass);
                    ctorParam.setDefaulted(m.isDefaultedAnnotation());
                    value.setName(member.getName());
                    ctorParam.setName(member.getName());
                    value.setType(annotationParameterType(iface.getUnit(), m));
                    value.setUnboxed(true);
                    value.setUnit(iface.getUnit());
                    if(isValue)
                        ctorpl.getParameters().add(0, ctorParam);
                    else
                        ctorpl.getParameters().add(ctorParam);
                    term.setSourceParameter(ctorParam);
                    ctor.addMember(value);
                   
                    AnnotationConstructorParameter acp = new AnnotationConstructorParameter();
                    acp.setParameter(ctorParam);
                    if(isValue)
                        ai.getConstructorParameters().add(0, acp);
                    else
                        ai.getConstructorParameters().add(acp);
                }
                annotationArgs.add(argument);
            }
        }
        ai.getAnnotationArguments().addAll(annotationArgs);
        compiledDeclarations.add(klass);
        compiledDeclarations.add(ctor);
    }

    private ProducedType annotationParameterType(Unit unit, JavaMethod m) {
        ProducedType type = m.getType();
        if (Decl.isJavaArray(type.getDeclaration())) {
            String name = type.getDeclaration().getQualifiedNameString();
            final ProducedType elementType;
            String underlyingType = null;
            if(name.equals("java.lang::ObjectArray")){
                ProducedType eType = type.getTypeArgumentList().get(0);
                String elementTypeName = eType.getDeclaration().getQualifiedNameString();
                if ("java.lang::String".equals(elementTypeName)) {
                    elementType = unit.getStringDeclaration().getType();
                } else if ("java.lang::Class".equals(elementTypeName)
                        || "java.lang.Class".equals(eType.getUnderlyingType())) {
                    // Two cases because the types
                    // Class[] and Class<?>[] are treated differently by
                    // AbstractModelLoader.obtainType()
                   
                    // TODO Replace with metamodel ClassOrInterface type
                    // once we have support for metamodel references
                    elementType = unit.getAnythingDeclaration().getType();
                    underlyingType = "java.lang.Class";
                } else {
                    elementType = eType;  
                }
                // TODO Enum elements
            } else if(name.equals("java.lang::LongArray")) {
                elementType = unit.getIntegerDeclaration().getType();
            } else if (name.equals("java.lang::ByteArray")) {
                elementType = unit.getByteDeclaration().getType();
            } else if (name.equals("java.lang::ShortArray")) {
                elementType = unit.getIntegerDeclaration().getType();
                underlyingType = "short";
            } else if (name.equals("java.lang::IntArray")){
                elementType = unit.getIntegerDeclaration().getType();
                underlyingType = "int";
            } else if(name.equals("java.lang::BooleanArray")){
                elementType = unit.getBooleanDeclaration().getType();
            } else if(name.equals("java.lang::CharArray")){
                elementType = unit.getCharacterDeclaration().getType();
                underlyingType = "char";
            } else if(name.equals("java.lang::DoubleArray")) {
                elementType = unit.getFloatDeclaration().getType();
            } else if (name.equals("java.lang::FloatArray")){
                elementType = unit.getFloatDeclaration().getType();
                underlyingType = "float";
            } else {
                throw new RuntimeException();
            }
            elementType.setUnderlyingType(underlyingType);
            ProducedType iterableType = unit.getIterableType(elementType);
            return iterableType;
        } else if ("java.lang::Class".equals(type.getDeclaration().getQualifiedNameString())) {
            // TODO Replace with metamodel ClassOrInterface type
            // once we have support for metamodel references
            return unit.getAnythingDeclaration().getType();
        } else {
            return type;
        }
    }
   
    @Override
    public Iterable<Unit> getUnits() {
        synchronized(modelLoader.getLock()){
            Iterable<Unit> sourceUnits = super.getUnits();
            LinkedList<Unit> ret = new LinkedList<Unit>();
            for (Unit unit : sourceUnits) {
                ret.add(unit);
            }
            ret.addAll(lazyUnits);
            return ret;
        }
    }

    @Override
    public void removeUnit(Unit unit) {
        synchronized(modelLoader.getLock()){
            for (Declaration d : unit.getDeclarations()) {
                flushCache(d);
                if (d instanceof TypeDeclaration) {
                    ((TypeDeclaration)d).clearProducedTypeCache();
                }
            }
            if (unit.getFilename().endsWith(".class") || unit.getFilename().endsWith(".java")) {
                lazyUnits.remove(unit);
                for (Declaration d : unit.getDeclarations()) {
                    compiledDeclarations.remove(d);
                    // TODO : remove the declaration from the declaration map in AbstractModelLoader
                }
                modelLoader.removeDeclarations(unit.getDeclarations());
            } else {           
                super.removeUnit(unit);
            }
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.loader.model.LazyPackage

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.