All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.ceylon.model.loader.JvmBackendUtil Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.model.loader;

import static com.redhat.ceylon.model.typechecker.model.ModelUtil.getSignature;

import java.util.List;
import java.util.Map;

import com.redhat.ceylon.common.BooleanUtil;
import com.redhat.ceylon.model.loader.mirror.AnnotatedMirror;
import com.redhat.ceylon.model.loader.mirror.AnnotationMirror;
import com.redhat.ceylon.model.loader.mirror.ClassMirror;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassAlias;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Specification;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;

public class JvmBackendUtil {
    public static boolean isInitialLowerCase(String name) {
        return !name.isEmpty() && isLowerCase(name.codePointAt(0));
    }

    public static boolean isLowerCase(int codepoint) {
        return Character.isLowerCase(codepoint) || codepoint == '_';
    }

    public static String getName(List parts){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parts.size(); i++) {
            sb.append(parts.get(i));
            if (i < parts.size() - 1) {
                sb.append('.');
            }
        }
        return sb.toString();
    }

    public static String getMirrorName(AnnotatedMirror mirror) {
        String name;
        AnnotationMirror annot = mirror.getAnnotation(AbstractModelLoader.CEYLON_NAME_ANNOTATION);
        if (annot != null) {
            name = (String)annot.getValue();
        } else {
            name = mirror.getName();
            name = name.isEmpty() ? name : NamingBase.stripLeadingDollar(name);
            if (mirror instanceof ClassMirror
                    && JvmBackendUtil.isInitialLowerCase(name)
                    && name.endsWith("_")
                    && mirror.getAnnotation(AbstractModelLoader.CEYLON_CEYLON_ANNOTATION) != null) {
                name = name.substring(0, name.length()-1);
            }
        }
        return name;
    }

    public static boolean isSubPackage(String moduleName, String pkgName) {
        return pkgName.equals(moduleName)
                || pkgName.startsWith(moduleName+".");
    }

    /**
     * Removes the given character from the given string. More efficient than using String.replace
     * which uses regexes.
     */
    public static String removeChar(char c, String string) {
        int nextChar = string.indexOf(c);
        if(nextChar == -1)
            return string;
        int start = 0;
        StringBuilder ret = new StringBuilder(string.length()-1);// we remove at least one
        while(nextChar != -1){
            ret.append(string, start, nextChar);
            start = nextChar+1;
            nextChar = string.indexOf(c, start);
        }
        // don't forget the end part
        ret.append(string, start, string.length());
        return ret.toString();
    }

    public static String strip(String name, boolean isCeylon, boolean isShared) {
        String stripped = NamingBase.stripLeadingDollar(name);
        String privSuffix = NamingBase.Suffix.$priv$.name();
        if(isCeylon && !isShared && name.endsWith(privSuffix))
            return stripped.substring(0, stripped.length() - privSuffix.length());
        return stripped;
    }

    /**
     * Determines whether the declaration is a non-transient non-parameter value 
     * (not a getter)
     * @param decl The declaration
     * @return true if the declaration is a value
     */
    public static boolean isValue(Declaration decl) {
        return (decl instanceof Value)
                && !((Value)decl).isParameter()
                && !((Value)decl).isTransient();
    }

    /**
     * Determines whether the declaration's is a method
     * @param decl The declaration
     * @return true if the declaration is a method
     */
    public static boolean isMethod(Declaration decl) {
        return (decl instanceof Function)
                && !((Function)decl).isParameter();
    }

    public static boolean isCeylon(TypeDeclaration declaration) {
        return ModelUtil.isCeylonDeclaration(declaration);
    }

    public static Declaration getTopmostRefinedDeclaration(Declaration decl){
        return getTopmostRefinedDeclaration(decl, null);
    }

    public static Declaration getTopmostRefinedDeclaration(Declaration decl, Map methodOverrides){
        if (decl instanceof FunctionOrValue
                && ((FunctionOrValue)decl).isParameter()
                && decl.getContainer() instanceof Class) {
            // Parameters in a refined class are not considered refinements themselves
            // We have in find the refined attribute
            Class c = (Class)decl.getContainer();
            boolean isAlias = c.isAlias();
            boolean isActual = c.isActual();
            // aliases and actual classes actually do refine their extended type parameters so the same rules apply wrt
            // boxing and stuff
            if (isAlias || isActual) {
                Functional ctor = null;
                int index = c.getParameterList().getParameters().indexOf(findParamForDecl(((TypedDeclaration)decl)));
                // ATM we only consider aliases if we're looking at aliases, and same for actual, not mixing the two.
                // Note(Stef): not entirely sure about that one, what about aliases of actual classes?
                while ((isAlias && c.isAlias())
                        || (isActual && c.isActual())) {
                    ctor = (isAlias && c.isAlias()) ? (Functional)((ClassAlias)c).getConstructor() : c;
                    Type et = c.getExtendedType();
                    c = et!=null && et.isClass() ? (Class)et.getDeclaration() : null;
                    // handle compile errors
                    if(c == null)
                        return null;
                }
                if (isActual) {
                    ctor = c;
                }
                // be safe
                if(ctor == null 
                        || ctor.getParameterLists() == null
                        || ctor.getParameterLists().isEmpty()
                        || ctor.getFirstParameterList() == null
                        || ctor.getFirstParameterList().getParameters() == null
                        || ctor.getFirstParameterList().getParameters().size() <= index)
                    return null;
                decl = ctor.getFirstParameterList().getParameters().get(index).getModel();
            }
            if (decl.isShared()) {
                Declaration refinedDecl = c.getRefinedMember(decl.getName(), getSignature(decl), false);//?? ellipsis=false??
                if(refinedDecl != null && !ModelUtil.equal(refinedDecl, decl)) {
                    return getTopmostRefinedDeclaration(refinedDecl, methodOverrides);
                }
            }
            return decl;
        } else if(decl instanceof FunctionOrValue
                && ((FunctionOrValue)decl).isParameter() // a parameter
                && ((decl.getContainer() instanceof Function && !(((Function)decl.getContainer()).isParameter())) // that's not parameter of a functional parameter 
                        || decl.getContainer() instanceof Specification // or is a parameter in a specification
                        || (decl.getContainer() instanceof Function  
                            && ((Function)decl.getContainer()).isParameter() 
                            && createMethod((Function)decl.getContainer())))) {// or is a class functional parameter
            // Parameters in a refined method are not considered refinements themselves
            // so we have to look up the corresponding parameter in the container's refined declaration
            Functional func = (Functional)getParameterized((FunctionOrValue)decl);
            if(func == null)
                return decl;
            Declaration kk = getTopmostRefinedDeclaration((Declaration)func, methodOverrides);
            // error recovery
            if(kk instanceof Functional == false)
                return decl;
            Functional refinedFunc = (Functional) kk;
            // shortcut if the functional doesn't override anything
            if (ModelUtil.equal((Declaration)refinedFunc, (Declaration)func)) {
                return decl;
            }
            if (func.getParameterLists().size() != refinedFunc.getParameterLists().size()) {
                // invalid input
                return decl;
            }
            for (int ii = 0; ii < func.getParameterLists().size(); ii++) {
                if (func.getParameterLists().get(ii).getParameters().size() != refinedFunc.getParameterLists().get(ii).getParameters().size()) {
                    // invalid input
                    return decl;
                }
                // find the index of the parameter in the declaration
                int index = 0;
                for (Parameter px : func.getParameterLists().get(ii).getParameters()) {
                    if (px.getModel() == null || px.getModel().equals(decl)) {
                        // And return the corresponding parameter from the refined declaration
                        return refinedFunc.getParameterLists().get(ii).getParameters().get(index).getModel();
                    }
                    index++;
                }
                continue;
            }
        }else if(methodOverrides != null
                && decl instanceof Function
                && ModelUtil.equal(decl.getRefinedDeclaration(), decl)
                && decl.getContainer() instanceof Specification
                && ((Specification)decl.getContainer()).getDeclaration() instanceof Function
                && ((Function) ((Specification)decl.getContainer()).getDeclaration()).isShortcutRefinement()
                // we do all the previous ones first because they are likely to filter out false positives cheaper than the
                // hash lookup we do next to make sure it is really one of those cases
                && methodOverrides.containsKey(decl)){
            // special case for class X() extends T(){ m = function() => e; } which we inline
            decl = methodOverrides.get(decl);
        }
        Declaration refinedDecl = decl.getRefinedDeclaration();
        if(refinedDecl != null && !ModelUtil.equal(refinedDecl, decl))
            return getTopmostRefinedDeclaration(refinedDecl);
        return decl;
    }

    public static Parameter findParamForDecl(TypedDeclaration decl) {
        String attrName = decl.getName();
        return findParamForDecl(attrName, decl);
    }
    
    public static Parameter findParamForDecl(String attrName, TypedDeclaration decl) {
        Parameter result = null;
        if (decl.getContainer() instanceof Functional) {
            Functional f = (Functional)decl.getContainer();
            result = f.getParameter(attrName);
        }
        return result;
    }

    public static Declaration getParameterized(FunctionOrValue methodOrValue) {
        if (!methodOrValue.isParameter()) {
            return null;
        }
        Scope scope = methodOrValue.getContainer();
        if (scope instanceof Specification) {
            return ((Specification)scope).getDeclaration();
        } else if (scope instanceof Declaration) {
            return (Declaration)scope;
        } 
        return null;
    }

    public static boolean createMethod(FunctionOrValue model) {
        return model instanceof Function
                && model.isParameter()
                && model.isClassMember()
                && (model.isShared() || model.isCaptured());
    }

    public static boolean supportsReified(Declaration declaration){
        if(declaration instanceof ClassOrInterface){
            // Java constructors don't support reified type arguments
            return isCeylon((TypeDeclaration) declaration);
        }else if(declaration instanceof Function){
            if (((Function)declaration).isParameter()) {
                // those can never be parameterised
                return false;
            }
            if(declaration.isToplevel())
                return true;
            // Java methods don't support reified type arguments
            Function m = (Function) getTopmostRefinedDeclaration(declaration);
            // See what its container is
            ClassOrInterface container = ModelUtil.getClassOrInterfaceContainer(m);
            // a method which is not a toplevel and is not a class method, must be a method within method and
            // that must be Ceylon so it supports it
            if(container == null)
                return true;
            return supportsReified(container);
        }else if(declaration instanceof Constructor){
            // Java constructors don't support reified type arguments
            return isCeylon((Constructor) declaration);
        }else{
            return false;
        }
    }
    
    public static boolean isCompanionClassNeeded(TypeDeclaration decl) {
        return decl instanceof Interface 
                && BooleanUtil.isNotFalse(((Interface)decl).isCompanionClassNeeded());
    }

    /**
     * Determines whether the given attribute should be accessed and assigned 
     * via a {@code VariableBox}
     */
    public static boolean isBoxedVariable(TypedDeclaration attr) {
        return ModelUtil.isNonTransientValue(attr)
                && ModelUtil.isLocalNotInitializer(attr)
                && ((attr.isVariable() && attr.isCaptured())
                        // self-captured objects must also be boxed like variables
                        || attr.isSelfCaptured());
    }

    private static String getArrayName(TypeDeclaration decl) {
        if(decl instanceof Class == false)
            return null;
        return decl.getQualifiedNameString();
    }
    
    public static boolean isJavaArray(TypeDeclaration decl) {
        String name = getArrayName(decl);
        return "java.lang::ObjectArray".equals(name)
                || "java.lang::ByteArray".equals(name)
                || "java.lang::ShortArray".equals(name)
                || "java.lang::IntArray".equals(name)
                || "java.lang::LongArray".equals(name)
                || "java.lang::FloatArray".equals(name)
                || "java.lang::DoubleArray".equals(name)
                || "java.lang::BooleanArray".equals(name)
                || "java.lang::CharArray".equals(name);
    }
    
    public static boolean isJavaBooleanArray(TypeDeclaration decl) {
        return "java.lang::BooleanArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaByteArray(TypeDeclaration decl) {
        return "java.lang::ByteArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaShortArray(TypeDeclaration decl) {
        return "java.lang::ShortArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaIntArray(TypeDeclaration decl) {
        return "java.lang::IntArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaLongArray(TypeDeclaration decl) {
        return "java.lang::LongArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaFloatArray(TypeDeclaration decl) {
        return "java.lang::FloatArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaDoubleArray(TypeDeclaration decl) {
        return "java.lang::DoubleArray".equals(getArrayName(decl));
    }
    
    public static boolean isJavaCharArray(TypeDeclaration decl) {
        return "java.lang::CharArray".equals(getArrayName(decl));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy