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

com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.compiler.java.runtime.metamodel;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import com.redhat.ceylon.common.ModuleUtil;
import com.redhat.ceylon.common.NonNull;
import com.redhat.ceylon.compiler.java.Util;
import com.redhat.ceylon.compiler.java.language.BooleanArray;
import com.redhat.ceylon.compiler.java.language.ByteArray;
import com.redhat.ceylon.compiler.java.language.CharArray;
import com.redhat.ceylon.compiler.java.language.DoubleArray;
import com.redhat.ceylon.compiler.java.language.FloatArray;
import com.redhat.ceylon.compiler.java.language.IntArray;
import com.redhat.ceylon.compiler.java.language.InternalMap;
import com.redhat.ceylon.compiler.java.language.LongArray;
import com.redhat.ceylon.compiler.java.language.ObjectArray;
import com.redhat.ceylon.compiler.java.language.ObjectArrayIterable;
import com.redhat.ceylon.compiler.java.language.ShortArray;
import com.redhat.ceylon.compiler.java.metadata.Ceylon;
import com.redhat.ceylon.compiler.java.metadata.Ignore;
import com.redhat.ceylon.compiler.java.metadata.Name;
import com.redhat.ceylon.compiler.java.metadata.Variance;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.CallableConstructorDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ClassDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ClassOrInterfaceDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ClassWithInitializerDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ModuleImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.NestableDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenClassOrInterfaceTypeImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenIntersectionTypeImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenTypeVariableImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenUnionTypeImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.SetterDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ValueConstructorDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ValueDeclarationImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.CallableConstructorImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.ClassImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.ClassOrInterfaceImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.InterfaceImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.IntersectionTypeImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberClassCallableConstructorImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberClassImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberClassValueConstructorImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.UnionTypeImpl;
import com.redhat.ceylon.compiler.java.runtime.metamodel.meta.ValueConstructorImpl;
import com.redhat.ceylon.compiler.java.runtime.model.ReifiedType;
import com.redhat.ceylon.compiler.java.runtime.model.RuntimeModelLoader;
import com.redhat.ceylon.compiler.java.runtime.model.RuntimeModuleManager;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor.Nothing;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.cmr.JDKUtils;
import com.redhat.ceylon.model.cmr.RuntimeResolver;
import com.redhat.ceylon.model.loader.JvmBackendUtil;
import com.redhat.ceylon.model.loader.ModelLoader.DeclarationType;
import com.redhat.ceylon.model.loader.NamingBase;
import com.redhat.ceylon.model.loader.impl.reflect.mirror.ReflectionClass;
import com.redhat.ceylon.model.loader.impl.reflect.mirror.ReflectionMethod;
import com.redhat.ceylon.model.loader.model.AnnotationProxyClass;
import com.redhat.ceylon.model.loader.model.AnnotationProxyMethod;
import com.redhat.ceylon.model.loader.model.FunctionOrValueInterface;
import com.redhat.ceylon.model.loader.model.JavaMethod;
import com.redhat.ceylon.model.loader.model.LazyClass;
import com.redhat.ceylon.model.loader.model.LazyClassAlias;
import com.redhat.ceylon.model.loader.model.LazyElement;
import com.redhat.ceylon.model.loader.model.LazyFunction;
import com.redhat.ceylon.model.loader.model.LazyInterface;
import com.redhat.ceylon.model.loader.model.LazyPackage;
import com.redhat.ceylon.model.loader.model.LazyTypeAlias;
import com.redhat.ceylon.model.loader.model.LazyValue;
import com.redhat.ceylon.model.runtime.CeylonModuleClassLoader;
import com.redhat.ceylon.model.runtime.CeylonModuleClassLoader.ModuleLoadException;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.ModuleImport;
import com.redhat.ceylon.model.typechecker.model.Modules;
import com.redhat.ceylon.model.typechecker.model.NothingType;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Reference;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Setter;
import com.redhat.ceylon.model.typechecker.model.SiteVariance;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.UnknownType;

import ceylon.language.Annotated;
import ceylon.language.Anything;
import ceylon.language.Array;
import ceylon.language.Callable;
import ceylon.language.Iterator;
import ceylon.language.Null;
import ceylon.language.Sequential;
import ceylon.language.empty_;
import ceylon.language.finished_;
import ceylon.language.null_;
import ceylon.language.sequence_;
import ceylon.language.meta.declaration.AnnotatedDeclaration;
import ceylon.language.meta.declaration.CallableConstructorDeclaration;
import ceylon.language.meta.declaration.Module;
import ceylon.language.meta.declaration.NestableDeclaration;
import ceylon.language.meta.declaration.OpenClassOrInterfaceType;
import ceylon.language.meta.declaration.OpenType;
import ceylon.language.meta.declaration.Package;
import ceylon.language.meta.declaration.ValueConstructorDeclaration;
import ceylon.language.meta.model.ClassModel;
import ceylon.language.meta.model.ClassOrInterface;
import ceylon.language.meta.model.Declared;
import ceylon.language.meta.model.FunctionModel;
import ceylon.language.meta.model.Generic;
import ceylon.language.meta.model.IncompatibleTypeException;
import ceylon.language.meta.model.InvocationException;
import ceylon.language.meta.model.Member;
import ceylon.language.meta.model.TypeApplicationException;
import ceylon.language.meta.model.ValueModel;

public class Metamodel {

    private static RuntimeModuleManager moduleManager;
    
    // FIXME: this will need better thinking in terms of memory usage
    private static Map classToDeclaration
    = new HashMap();
    
    private static Map typeCheckModelToRuntimeModel
        = new HashMap();

    private static Map typeCheckPackagesToRuntimeModel
        = new HashMap();

    private static Map typeCheckModulesToRuntimeModel
        = new HashMap();

    private static Map typeDescriptorToProducedType = new WeakHashMap();

    private static final TypeDescriptor TD_ClosedTypeOfAnything
        = TypeDescriptor.klass(ceylon.language.meta.model.Type.class, ceylon.language.Anything.$TypeDescriptor$);
    private static final TypeDescriptor TD_ClosedTypeArgumentElement
        = TypeDescriptor.union(TD_ClosedTypeOfAnything, ceylon.language.meta.declaration.Variance.$TypeDescriptor$);
    private static final TypeDescriptor TD_ClosedTypeArgument
        = TypeDescriptor.tuple(false, false, -1, TD_ClosedTypeOfAnything, ceylon.language.meta.declaration.Variance.$TypeDescriptor$);
    public static final TypeDescriptor TD_OpenTypeArgumentElement
        = TypeDescriptor.union(ceylon.language.meta.declaration.OpenType.$TypeDescriptor$, ceylon.language.meta.declaration.Variance.$TypeDescriptor$);
    public static final TypeDescriptor TD_OpenTypeArgument
        = TypeDescriptor.tuple(false, false, -1, ceylon.language.meta.declaration.OpenType.$TypeDescriptor$, ceylon.language.meta.declaration.Variance.$TypeDescriptor$);
    
    static{
        resetModuleManager();
    }

    public static boolean loadModule(String name, String version, ArtifactResult result, ClassLoader classLoader){
        boolean hasLoaded = moduleManager.loadModule(name, version, result, classLoader);
        // notify any thread waiting for this monitor
        Object lock = getLock();
        synchronized(lock){
            lock.notifyAll();
        }
        return hasLoaded;
    }
    
    public static void resetModuleManager() {
    	resetModuleManager(null);
    }
    
    public static void resetModuleManager(RuntimeResolver runtimeResolver) {
        moduleManager = new RuntimeModuleManager(runtimeResolver);
        moduleManager.initCoreModules(new Modules());
        moduleManager.prepareForTypeChecking();
        classToDeclaration.clear();
        typeCheckModelToRuntimeModel.clear();
        typeCheckModulesToRuntimeModel.clear();
        typeCheckPackagesToRuntimeModel.clear();
        typeDescriptorToProducedType.clear();
    }
    
    // This is only used in tests
    public static RuntimeModuleManager getModuleManager(){
        return moduleManager;
    }

    public static Object getLock(){
        return moduleManager.getModelLoader().getLock();
    }
    
    public static TypeDescriptor getTypeDescriptor(Object instance) {
        if(instance == null)
            return null_.$TypeDescriptor$;
        else if(instance instanceof ReifiedType)
            return((ReifiedType) instance).$getType$();
        else
            return getJavaTypeDescriptor(instance.getClass());
    }
    
    public static TypeDescriptor getIteratedTypeDescriptor(TypeDescriptor td) {
        return getTypeDescriptorForProducedType(
                moduleManager.getModelLoader().getUnit().getIteratedType(
                        getProducedType(td)));
    }
    
    private static TypeDescriptor getJavaArrayTypeDescriptor(Class klass) {
        if(klass == byte[].class)
            return ByteArray.$TypeDescriptor$;
        if(klass == short[].class)
            return ShortArray.$TypeDescriptor$;
        if(klass == int[].class)
            return IntArray.$TypeDescriptor$;
        if(klass == long[].class)
            return LongArray.$TypeDescriptor$;
        if(klass == float[].class)
            return FloatArray.$TypeDescriptor$;
        if(klass == double[].class)
            return DoubleArray.$TypeDescriptor$;
        if(klass == boolean[].class)
            return BooleanArray.$TypeDescriptor$;
        if(klass == char[].class)
            return CharArray.$TypeDescriptor$;
        TypeDescriptor componentType = getJavaTypeDescriptor(klass.getComponentType());
        return TypeDescriptor.klass(ObjectArray.class, componentType);
    }

    private static TypeDescriptor getJavaTypeDescriptor(Class klass) {
        if(klass.isArray())
            return getJavaArrayTypeDescriptor(klass);
        // make sure java.lang.Object doesn't leak in the ceylon metamodel
        // TODO: what about Throwable/j.l.Exception/RuntimeException?
        if(klass == Object.class)
            return ceylon.language.Object.$TypeDescriptor$;
        if(klass.isMemberClass())
            return TypeDescriptor.member(getJavaTypeDescriptor(klass.getEnclosingClass()), TypeDescriptor.klass(klass));
        // FIXME: what about local or anonymous types?
        return TypeDescriptor.klass(klass);
    }
    
    /**
     * Is the given type one that is "completely reifiable"? 
     * That is, does it consist only of class or interface types. 
     * For example:
     * 
     * 
     * 
     * 
     * 
     * 
     * 
Type Result
{@code String} true
{@code j.u.List} false (generic declaration, raw application)
{@code j.u.List}true
{@code j.u.List} false (where X is a type parameter)
*/ private static boolean reifiedGeneric(java.lang.reflect.Type t) { if (t instanceof Class && ((Class)t).getTypeParameters().length == 0) { return true; } else if (t instanceof java.lang.reflect.ParameterizedType) { java.lang.reflect.ParameterizedType pt = (java.lang.reflect.ParameterizedType)t; for (java.lang.reflect.Type t2 : pt.getActualTypeArguments()) { if (!reifiedGeneric(t2)) { return false; } } return true; } else { return false; } } /** * Does the type {@code o} "completely reify" the type {@code tested} * in it's inheritance hierarchy? * @see #reifiedGeneric(java.lang.reflect.Type) */ private static boolean reifiedByInheritance(Class o, Class tested) { int index = 0; // Try to avoid instantiation of ParameterizedTypes and their TypeVariables // by testing the Class instances for equality first. for (Class c : o.getInterfaces()) { if (c.equals(tested) && reifiedGeneric(o.getGenericInterfaces()[index])) { return true; } if (reifiedByInheritance(c, tested)) { return true; } index++; } Class sup = o.getSuperclass(); if (sup != null) { if (sup.equals(tested) && reifiedGeneric(o.getGenericSuperclass())) { return true; } if (reifiedByInheritance(sup, tested)) { return true; } } return false; } /** Implementation of {@code is} operator */ public static boolean isReified(java.lang.Object instance, TypeDescriptor type){ if (instance == null) { return type.containsNull(); } TypeDescriptor instanceType = getTypeDescriptor(instance); if (instanceType == null) { return false; } if (instanceType == type) { return true; } boolean result = type.is(instanceType); if (!result && !(instance instanceof ReifiedType)// we lack reified types && type instanceof TypeDescriptor.Class// we're testing for a generic type && ((TypeDescriptor.Class)type).isGeneric() && ((TypeDescriptor.Class)type).getKlass().isInstance(instance)// the instance is an instance of the base type && !reifiedByInheritance(instance.getClass(), ((TypeDescriptor.Class)type).getKlass())// the type isn't reified by inheritance ) { // throw when asked if an instance of a Java class is // of a generic type and we don't have sufficient information to // answer correctly. // note we do this only if the isSubtypeOf() test fails // that's so that we don't have to worry about type applications // such as like which is true even in the absence // of reified type arguments // throw new ReifiedTypeError("Cannot determine whether " + instance.getClass() + " is a " + type); result = true; //I can do it in Java, so what the hell ;-) } return result; } public static Type getProducedType(Object instance) { TypeDescriptor instanceType = getTypeDescriptor(instance); if(instanceType == null) throw Metamodel.newModelError("Metamodel not yet supported for Java types"); return getProducedType(instanceType); } public static Type getProducedType(TypeDescriptor reifiedType) { Type producedType; synchronized(getLock()){ producedType = typeDescriptorToProducedType.get(reifiedType); if(producedType == null){ producedType = reifiedType.toType(moduleManager); typeDescriptorToProducedType.put(reifiedType, producedType); } } return producedType; } @NonNull public static ceylon.language.meta.model.Type getAppliedMetamodel(TypeDescriptor typeDescriptor) { if(typeDescriptor == null) throw Metamodel.newModelError("Metamodel not yet supported for Java types"); Type pt = getProducedType(typeDescriptor); return getAppliedMetamodel(pt); } public static R getOrCreateMetamodel(com.redhat.ceylon.model.typechecker.model.Declaration declaration){ synchronized(getLock()){ Object ret = typeCheckModelToRuntimeModel.get(declaration); if(ret == null){ // make sure its module is loaded com.redhat.ceylon.model.typechecker.model.Package pkg = getPackage(declaration); com.redhat.ceylon.model.typechecker.model.Module mod = pkg.getModule(); getOrCreateMetamodel(mod); if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Class){ com.redhat.ceylon.model.typechecker.model.Class klass = (com.redhat.ceylon.model.typechecker.model.Class) declaration; ret = klass.hasConstructors() || klass.hasEnumerated() || klass.isAnonymous()? new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ClassWithConstructorsDeclarationImpl(klass) : new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ClassWithInitializerDeclarationImpl(klass); }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Interface){ com.redhat.ceylon.model.typechecker.model.Interface interf = (com.redhat.ceylon.model.typechecker.model.Interface)declaration; ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.InterfaceDeclarationImpl(interf); }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.TypeAlias){ com.redhat.ceylon.model.typechecker.model.TypeAlias alias = (com.redhat.ceylon.model.typechecker.model.TypeAlias)declaration; ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.AliasDeclarationImpl(alias); }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Function){ com.redhat.ceylon.model.typechecker.model.Function method = (com.redhat.ceylon.model.typechecker.model.Function)declaration; if (method.getTypeDeclaration() instanceof com.redhat.ceylon.model.typechecker.model.Constructor) { ret = new CallableConstructorDeclarationImpl(method, (com.redhat.ceylon.model.typechecker.model.Constructor)method.getTypeDeclaration()); } else { ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.FunctionDeclarationImpl(method); } }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Value){ com.redhat.ceylon.model.typechecker.model.Value value = (com.redhat.ceylon.model.typechecker.model.Value)declaration; if (value.getTypeDeclaration() instanceof com.redhat.ceylon.model.typechecker.model.Constructor) { ret = new ValueConstructorDeclarationImpl(value, (com.redhat.ceylon.model.typechecker.model.Constructor)value.getTypeDeclaration()); } else { ret = new ValueDeclarationImpl(value); } }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Setter){ com.redhat.ceylon.model.typechecker.model.Setter value = (com.redhat.ceylon.model.typechecker.model.Setter)declaration; ret = new SetterDeclarationImpl(value); }else if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Constructor){ com.redhat.ceylon.model.typechecker.model.Constructor value = (com.redhat.ceylon.model.typechecker.model.Constructor)declaration; Declaration functionOrValue = value.getContainer().getDirectMember(value.getName(), null, false); if (functionOrValue instanceof com.redhat.ceylon.model.typechecker.model.Function) { ret = new CallableConstructorDeclarationImpl((com.redhat.ceylon.model.typechecker.model.Function)functionOrValue, value); } else if (functionOrValue instanceof com.redhat.ceylon.model.typechecker.model.Value) { ret = new ValueConstructorDeclarationImpl((com.redhat.ceylon.model.typechecker.model.Value)functionOrValue, value); } else if (functionOrValue == null) { ret = new CallableConstructorDeclarationImpl(value, value); }else { throw Metamodel.newModelError("Declaration type not supported yet: "+declaration); } }else{ throw Metamodel.newModelError("Declaration type not supported yet: "+declaration); } typeCheckModelToRuntimeModel.put(declaration, ret); } return (R)ret; } } public static boolean hasTypeParameters(com.redhat.ceylon.model.typechecker.model.TypedDeclaration model) { if(model instanceof com.redhat.ceylon.model.typechecker.model.Generic) return hasTypeParameters((com.redhat.ceylon.model.typechecker.model.Generic)model); if(model.getContainer() instanceof com.redhat.ceylon.model.typechecker.model.ClassOrInterface) return hasTypeParameters((com.redhat.ceylon.model.typechecker.model.ClassOrInterface)model.getContainer()); return false; } public static boolean hasTypeParameters(com.redhat.ceylon.model.typechecker.model.Generic model) { if(!model.getTypeParameters().isEmpty()) return true; Object container = ((com.redhat.ceylon.model.typechecker.model.Declaration)model).getContainer(); if(container instanceof com.redhat.ceylon.model.typechecker.model.ClassOrInterface) return hasTypeParameters((com.redhat.ceylon.model.typechecker.model.ClassOrInterface) container); return false; } public static com.redhat.ceylon.compiler.java.runtime.metamodel.decl.PackageImpl getOrCreateMetamodel(com.redhat.ceylon.model.typechecker.model.Package declaration){ synchronized(getLock()){ com.redhat.ceylon.compiler.java.runtime.metamodel.decl.PackageImpl ret = typeCheckPackagesToRuntimeModel.get(declaration); if(ret == null){ // make sure its module is loaded com.redhat.ceylon.model.typechecker.model.Module mod = declaration.getModule(); getOrCreateMetamodel(mod); ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.PackageImpl(declaration); typeCheckPackagesToRuntimeModel.put(declaration, ret); } return ret; } } public static com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ModuleImpl getOrCreateMetamodel( com.redhat.ceylon.model.typechecker.model.Module declaration){ return getOrCreateMetamodel(null, declaration, null, false /* not optional */); } private static com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ModuleImpl getOrCreateMetamodel( String namespace, com.redhat.ceylon.model.typechecker.model.Module declaration, Set visitedModules, boolean optional){ synchronized(getLock()){ com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ModuleImpl ret = typeCheckModulesToRuntimeModel.get(declaration); if(ret == null){ // make sure it is loaded loadModule(namespace, declaration, visitedModules, optional); if(!declaration.isAvailable()) return null; ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.ModuleImpl(declaration); typeCheckModulesToRuntimeModel.put(declaration, ret); } return ret; } } private static void loadModule( String namespace, com.redhat.ceylon.model.typechecker.model.Module declaration, Set visitedModules, boolean optional) { // don't do if not running JBoss modules if(!isJBossModules()){ return; } // we must use the context module loader, which is the one CeylonModuleLoader for every user module // if we use the language module loader it will be a LocalModuleLoader which doesn't know about the // module repos specified on the command-line. // no loading required for these if(JDKUtils.isJDKModule(declaration.getNameAsString()) || JDKUtils.isOracleJDKModule(declaration.getNameAsString())) return; try { // Stef: this can fail, in particular is the TCCCL is that of a bootstrap module CL since we can't override those // if it does we have to resort to reflection to get around this ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if(contextClassLoader instanceof CeylonModuleClassLoader) { // this will return null for bootstrap modules String modname = ModuleUtil.makeModuleName(namespace, declaration.getNameAsString(), null); CeylonModuleClassLoader newModuleClassLoader = ((CeylonModuleClassLoader) contextClassLoader).loadModule(modname, declaration.getVersion()); // if we can force it loaded, let's do if(newModuleClassLoader != null){ // this can complete in another thread or this thread newModuleClassLoader.registerInMetaModel(); } if(!declaration.isAvailable()){ // perhaps it is being loaded in another thread, wait for it Object lock = getLock(); synchronized(lock){ int tries = RuntimeModelLoader.MAX_JBOSS_MODULES_WAITS; while(!declaration.isAvailable()){ try { lock.wait(RuntimeModelLoader.JBOSS_MODULES_TIMEOUT); } catch (InterruptedException e) { throw Metamodel.newModelError("Interrupted"); } if(tries-- < 0) throw Metamodel.newModelError("JBoss modules failed to make module available: "+declaration.getNameAsString() + "/" + declaration.getVersion()); } } } }// it was loaded via the bootstrap module loader and does not need forcing if(visitedModules == null) visitedModules = new HashSet(); // do not visit this module again visitedModules.add(declaration); // make sure its imports are also loaded for(ModuleImport mi : declaration.getImports()){ com.redhat.ceylon.model.typechecker.model.Module importedModule = mi.getModule(); // don't try loading optional modules if(mi.isOptional()) continue; // make sure we don't run in circles if(importedModule != null && !visitedModules.contains(importedModule)) { String ns = mi.getNamespace() != null ? mi.getNamespace() : namespace; getOrCreateMetamodel(ns, importedModule, visitedModules, mi.isOptional()); } } } catch (ModuleLoadException e) { // it's not an issue if we don't find the default module, it's always created but not always // present. Also not an issue for optional modules. if(!declaration.isDefaultModule() && !optional) throw Metamodel.newModelError(e.toString()); } catch (SecurityException e) { throw Metamodel.newModelError(e.toString()); } catch (IllegalArgumentException e) { throw Metamodel.newModelError(e.toString()); } } private static boolean isJBossModules() { // Metamodel is from ceylon.language which is loaded by the InitialModuleLoader, which can't change its // ClassLoader implementation from that, so it should be safe return Metamodel.class.getClassLoader().getClass().getName().equals("org.jboss.modules.ModuleClassLoader"); } public static ceylon.language.meta.declaration.OpenType getMetamodel(Type pt) { TypeDeclaration declaration = pt.getDeclaration(); if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Class){ return new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenClassTypeImpl(pt); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Constructor){ return new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenClassTypeImpl(pt.getQualifyingType()); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Interface){ return new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.OpenInterfaceTypeImpl(pt); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.TypeParameter){ com.redhat.ceylon.model.typechecker.model.TypeParameter tp = (com.redhat.ceylon.model.typechecker.model.TypeParameter) declaration; return new OpenTypeVariableImpl(tp); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.UnionType){ return new OpenUnionTypeImpl((com.redhat.ceylon.model.typechecker.model.UnionType)declaration); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.IntersectionType){ return new OpenIntersectionTypeImpl((com.redhat.ceylon.model.typechecker.model.IntersectionType)declaration); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.NothingType){ return ceylon.language.meta.declaration.nothingType_.get_(); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.UnknownType){ ((com.redhat.ceylon.model.typechecker.model.UnknownType)declaration).reportErrors(); } throw Metamodel.newModelError("Declaration type not supported yet: "+declaration); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static Sequential getMetamodelSequential(List types) { if(types.isEmpty()) return (Sequential)(Sequential)empty_.get_(); ceylon.language.meta.declaration.OpenType[] ret = new ceylon.language.meta.declaration.OpenType[types.size()]; int i=0; TypeDescriptor td = TypeDescriptor.NothingType; for(Type pt : types){ OpenType mm = Metamodel.getMetamodel(pt); td = TypeDescriptor.union(((ReifiedType)mm).$getType$()); ret[i++] = mm; } return Util.sequentialWrapper(td, ret); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static Sequential> getAppliedMetamodelSequential(List types) { if(types.isEmpty()) return (Sequential>)(Sequential)empty_.get_(); ceylon.language.meta.model.Type[] ret = new ceylon.language.meta.model.Type[types.size()]; int i=0; for(Type pt : types){ ret[i++] = Metamodel.getAppliedMetamodel(pt); } return Util.sequentialWrapper(TypeDescriptor.klass(ceylon.language.meta.model.Type.class, Anything.$TypeDescriptor$), ret); } @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull public static ceylon.language.meta.model.Type getAppliedMetamodel(Type pt) { TypeDeclaration declaration = pt.getDeclaration(); if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Constructor){ com.redhat.ceylon.model.typechecker.model.Constructor ctorDeclaration = (com.redhat.ceylon.model.typechecker.model.Constructor)declaration; Type ctorType = pt; pt = pt.getExtendedType(); declaration = pt.getDeclaration(); TypeDescriptor reifiedArguments; if(!ModelUtil.isEnumeratedConstructor(ctorDeclaration) && !declaration.isAnonymous() && !isLocalType(declaration)) reifiedArguments = Metamodel.getTypeDescriptorForArguments(declaration.getUnit(), (Functional)ctorDeclaration, ctorType); else reifiedArguments = TypeDescriptor.NothingType; TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt); if(declaration.isToplevel() || isLocalType(declaration)) return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.ClassImpl(reifiedType, reifiedArguments, pt, Metamodel.getAppliedMetamodel(pt.getQualifyingType()), null); TypeDescriptor reifiedContainer = getTypeDescriptorForProducedType(pt.getQualifyingType()); return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberClassImpl(reifiedContainer, reifiedType, reifiedArguments, pt); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Class){ // anonymous classes don't have parameter lists TypeDescriptor reifiedArguments; if(!declaration.isAnonymous() && !isLocalType(declaration)) reifiedArguments = Metamodel.getTypeDescriptorForArguments(declaration.getUnit(), (Functional)declaration, pt); else reifiedArguments = TypeDescriptor.NothingType; TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt); if(declaration.isToplevel() || isLocalType(declaration)) return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.ClassImpl(reifiedType, reifiedArguments, pt, null, null); // Workaround for old binaries where static members could have some qualified TDs // but not always. If the qualifying type is missing treat it as a toplevel com.redhat.ceylon.model.typechecker.model.Type qt = pt.getQualifyingType(); if(qt == null && declaration.isStatic()){ qt = ((com.redhat.ceylon.model.typechecker.model.ClassOrInterface)declaration.getContainer()).getType(); } TypeDescriptor reifiedContainer = getTypeDescriptorForProducedType(qt); return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberClassImpl(reifiedContainer, reifiedType, reifiedArguments, pt); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Interface){ TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt); if(declaration.isToplevel() || isLocalType(declaration)) return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.InterfaceImpl(reifiedType, pt, null, null); // Workaround for old binaries where static members could have some qualified TDs // but not always. If the qualifying type is missing treat it as a toplevel com.redhat.ceylon.model.typechecker.model.Type qt = pt.getQualifyingType(); if(qt == null && declaration.isStatic()){ qt = ((com.redhat.ceylon.model.typechecker.model.ClassOrInterface)declaration.getContainer()).getType(); } TypeDescriptor reifiedContainer = getTypeDescriptorForProducedType(qt); return new com.redhat.ceylon.compiler.java.runtime.metamodel.meta.MemberInterfaceImpl(reifiedContainer, reifiedType, pt); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.UnionType){ TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt); return new UnionTypeImpl(reifiedType, (com.redhat.ceylon.model.typechecker.model.UnionType)declaration); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.IntersectionType){ TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt); return new IntersectionTypeImpl(reifiedType, (com.redhat.ceylon.model.typechecker.model.IntersectionType)declaration); } if(declaration instanceof com.redhat.ceylon.model.typechecker.model.NothingType){ return (ceylon.language.meta.model.Type)ceylon.language.meta.model.nothingType_.get_(); } if(declaration instanceof UnknownType){ ((UnknownType) declaration).reportErrors(); } throw Metamodel.newModelError("Declaration type not supported yet: "+declaration); } public static java.lang.Class getJavaClass(com.redhat.ceylon.model.typechecker.model.Module module) { String className = module.getNameAsString() + "." + NamingBase.MODULE_DESCRIPTOR_CLASS_NAME; ReflectionClass classMirror = (ReflectionClass)moduleManager.getModelLoader().lookupClassMirror(module, className); return classMirror.klass; } public static java.lang.Class getJavaClass(com.redhat.ceylon.model.typechecker.model.Package pkg) { String className = ((LazyPackage) pkg).getNameAsString()+ "." + NamingBase.PACKAGE_DESCRIPTOR_CLASS_NAME; ReflectionClass classMirror = (ReflectionClass)moduleManager.getModelLoader().lookupClassMirror(pkg.getModule(), className); return classMirror != null ? classMirror.klass : null; } public static java.lang.Class getJavaClass(com.redhat.ceylon.model.typechecker.model.Declaration declaration) { if(declaration instanceof LazyClass){ ReflectionClass classMirror = (ReflectionClass) ((LazyClass) declaration).classMirror; return classMirror.klass; } if(declaration instanceof LazyInterface){ ReflectionClass classMirror = (ReflectionClass) ((LazyInterface) declaration).classMirror; return classMirror.klass; } if(declaration instanceof LazyFunction){ ReflectionClass classMirror = (ReflectionClass) ((LazyFunction) declaration).classMirror; return classMirror.klass; } if(declaration instanceof LazyValue){ ReflectionClass classMirror = (ReflectionClass) ((LazyValue) declaration).classMirror; return classMirror.klass; } if (declaration instanceof LazyClassAlias) { ReflectionClass classMirror = (ReflectionClass) ((LazyClassAlias) declaration).classMirror; return classMirror.klass; } if (declaration instanceof LazyTypeAlias) { ReflectionClass classMirror = (ReflectionClass) ((LazyTypeAlias) declaration).classMirror; return classMirror.klass; } if(declaration instanceof AnnotationProxyClass){ return getJavaClass(((AnnotationProxyClass) declaration).iface); } if(declaration.getContainer() instanceof com.redhat.ceylon.model.typechecker.model.Declaration){ return getJavaClass((com.redhat.ceylon.model.typechecker.model.Declaration)declaration.getContainer()); } throw Metamodel.newModelError("Unsupported declaration type: " + declaration + " of type "+declaration.getClass()); } public static java.lang.reflect.Constructor getJavaConstructor(com.redhat.ceylon.model.typechecker.model.Constructor declaration) { return getJavaConstructor((com.redhat.ceylon.model.typechecker.model.Class)declaration.getContainer(), declaration.getName()); } public static java.lang.reflect.Constructor getJavaConstructor(com.redhat.ceylon.model.typechecker.model.Class clazz, String ctorName) { Constructor[] ctors = getJavaClass(clazz).getDeclaredConstructors(); for (java.lang.reflect.Constructor ctor : ctors) { if (ctor.isAnnotationPresent(Ignore.class)) { continue; } Name name = ctor.getAnnotation(Name.class); String n1 = name == null ? "" : name.value(); String n2 = ctorName == null ? "" : ctorName; if (n1.equals(n2)) { return ctor; } } return null; } public static java.lang.reflect.Method getJavaInstantiator(com.redhat.ceylon.model.typechecker.model.Constructor declaration, String methodName) { com.redhat.ceylon.model.typechecker.model.Class cls = (com.redhat.ceylon.model.typechecker.model.Class)declaration.getContainer(); Class terJavaCls = getJavaClass((Declaration)cls.getContainer()); java.lang.reflect.Method[] methods = terJavaCls.getDeclaredMethods(); // the instantiator method is @Ignore'd and not @Name'd // So we search for the method: // * with the instantiator name // * with or without a constructor class name parameter and // * with the longest signature // This really is horrible. int numReified = cls.getTypeParameters().size(); boolean defaultCtor = isDefaultConstructor(declaration); java.lang.reflect.Method longestSig = null; for (java.lang.reflect.Method method : methods) { if (method.getName().equals(methodName)) { Class[] sig = method.getParameterTypes(); if (longestSig == null || longestSig.getParameterTypes().length < sig.length) { Class possibleCtorNameCls; if (sig.length > numReified) { possibleCtorNameCls = sig[numReified]; } else { possibleCtorNameCls = null; } boolean ctorNameParam = possibleCtorNameCls != null && possibleCtorNameCls.getAnnotation(Ignore.class) != null; //&& possibleCtorNameCls.getAnnotation(Name.class) != null //&& declaration.equals(possibleCtorNameCls.getAnnotation(Name.class).value()); if (defaultCtor && !ctorNameParam) { longestSig = method; } else if (!defaultCtor && ctorNameParam){ longestSig = method; } } } } if (longestSig == null) { throw Metamodel.newModelError("Unsupported declaration type: " + declaration); } else { return longestSig; } } /** * Return all the Java constructors for the given named Ceylon constructor, * including the overloaded ones. */ public static List> getJavaConstructors( com.redhat.ceylon.model.typechecker.model.Constructor declaration) { Class javaClass = getJavaClass((com.redhat.ceylon.model.typechecker.model.Class)declaration.getContainer()); ArrayList> result = new ArrayList>(); // find the appropriate ultimate constructor java.lang.reflect.Constructor ultimate = getJavaConstructor(declaration); result.add(ultimate); List parameters = declaration.getFirstParameterList().getParameters(); Class[] javapl = ultimate.getParameterTypes(); // find all the overloads of the ultimate that we expect, // according to the parameter list for (int ii = parameters.size()-1, jj=javapl.length; ii >= 0; ii--, jj--) { Parameter p = parameters.get(ii); if (p.isDefaulted() || (p.isSequenced() && !p.isAtLeastOne())) { Class[] sig = Arrays.copyOfRange(javapl, 0, jj-1); try { Constructor overloaded = javaClass.getDeclaredConstructor(sig); result.add(overloaded); } catch (NoSuchMethodException e) { throw Metamodel.newModelError("Could not find overloaded constructor with signature " + Arrays.toString(sig), e); } } } return result; } public static List getJavaInstantiators( com.redhat.ceylon.model.typechecker.model.Constructor declaration) { com.redhat.ceylon.model.typechecker.model.Class classModel = (com.redhat.ceylon.model.typechecker.model.Class)declaration.getContainer(); // Class javaClass = getJavaClass(classModel); Class outerJavaClass = getJavaClass((Declaration)classModel.getContainer()); // java.lang.reflect.Method[] ctors = outerJavaClass.getDeclaredMethods(); ArrayList result = new ArrayList(); // find the appropriate ultimate constructor String methodName = classModel.getName() + "$new$"; java.lang.reflect.Method ultimate = getJavaInstantiator(declaration, methodName); result.add(ultimate); List parameters = declaration.getFirstParameterList().getParameters(); Class[] javapl = ultimate.getParameterTypes(); // find all the overloads of the ultimate that we expect, // according to the parameter list for (int ii = parameters.size()-1, jj=javapl.length; ii >= 0; ii--, jj--) { Parameter p = parameters.get(ii); if (p.isDefaulted() || (p.isSequenced() && !p.isAtLeastOne())) { Class[] sig = Arrays.copyOfRange(javapl, 0, jj-1); try { java.lang.reflect.Method overloaded = outerJavaClass.getDeclaredMethod(methodName, sig); result.add(overloaded); } catch (NoSuchMethodException e) { throw Metamodel.newModelError("Could not find overloaded constructor with signature " + Arrays.toString(sig), e); } } } return result; } public static boolean isDefaultConstructor(com.redhat.ceylon.model.typechecker.model.Constructor declaration) { return declaration.getName() == null || declaration.getName().isEmpty(); } public static java.lang.reflect.AnnotatedElement getJavaAnnotatedElement(com.redhat.ceylon.model.typechecker.model.Function declaration) { if(declaration instanceof JavaMethod || declaration instanceof LazyFunction) return getJavaMethod(declaration); if(declaration instanceof AnnotationProxyMethod){ return getJavaClass(((AnnotationProxyMethod)declaration).proxyClass.iface); } throw Metamodel.newModelError("Unsupported declaration type: " + declaration); } public static java.lang.reflect.Method getJavaMethod(com.redhat.ceylon.model.typechecker.model.Function declaration) { if(declaration instanceof JavaMethod){ ReflectionMethod methodMirror = (ReflectionMethod) ((JavaMethod) declaration).mirror; return (java.lang.reflect.Method) methodMirror.method; } if(declaration instanceof LazyFunction){ ReflectionMethod methodMirror = (ReflectionMethod) ((LazyFunction) declaration).getMethodMirror(); return (java.lang.reflect.Method) methodMirror.method; } throw Metamodel.newModelError("Unsupported declaration type: " + declaration); } public static TypeDescriptor getTypeDescriptorForProducedType(com.redhat.ceylon.model.typechecker.model.Type type) { TypeDeclaration declaration = type.getDeclaration(); if(type.isNothing()){ return TypeDescriptor.NothingType; } if(type.isUnion()){ TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getCaseTypes()); return TypeDescriptor.union(tdArgs); } if(type.isIntersection()){ TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getSatisfiedTypes()); return TypeDescriptor.intersection(tdArgs); } if(declaration instanceof LazyClass){ ReflectionClass classMirror = (ReflectionClass) ((LazyClass) declaration).classMirror; TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getTypeArgumentList()); TypeDescriptor ret = TypeDescriptor.klass(classMirror.klass, tdArgs); if(type.getQualifyingType() != null) return TypeDescriptor.member(getTypeDescriptorForProducedType(type.getQualifyingType()), ret); return ret; } if(declaration instanceof LazyInterface){ ReflectionClass classMirror = (ReflectionClass) ((LazyInterface) declaration).classMirror; TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getTypeArgumentList()); TypeDescriptor ret = TypeDescriptor.klass(classMirror.klass, tdArgs); if(type.getQualifyingType() != null) return TypeDescriptor.member(getTypeDescriptorForProducedType(type.getQualifyingType()), ret); return ret; } if(declaration instanceof FunctionOrValueInterface){ TypedDeclaration underlyingDeclaration = ((FunctionOrValueInterface) declaration).getUnderlyingDeclaration(); TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getTypeArgumentList()); TypeDescriptor ret; if(underlyingDeclaration.isToplevel()){ ReflectionClass classMirror; // FIXME: this is not really true, but reflects what's in TypeDescriptor.functionOrValue where we do not // make any different, but this should not matter since we only care about container functions and their // type arguments if(underlyingDeclaration instanceof Setter) underlyingDeclaration = ((Setter) underlyingDeclaration).getGetter(); if(underlyingDeclaration instanceof LazyValue) classMirror = (ReflectionClass) ((LazyValue) underlyingDeclaration).classMirror; else if(underlyingDeclaration instanceof LazyFunction) classMirror = (ReflectionClass) ((LazyFunction) underlyingDeclaration).classMirror; else throw Metamodel.newModelError("Unsupported underlying declaration type: " + underlyingDeclaration); ret = TypeDescriptor.functionOrValue(classMirror.klass, tdArgs); }else ret = TypeDescriptor.functionOrValue(underlyingDeclaration.getPrefixedName(), tdArgs); if(type.getQualifyingType() != null) return TypeDescriptor.member(getTypeDescriptorForProducedType(type.getQualifyingType()), ret); return ret; } if(declaration instanceof UnknownType){ ((UnknownType) declaration).reportErrors(); } throw Metamodel.newModelError("Unsupported declaration type: " + (declaration == null ? "null" : declaration.getClass())); } public static TypeDescriptor[] getTypeDescriptors(Sequential> types) { Iterator> iterator = types.iterator(); Object it; TypeDescriptor[] ret = new TypeDescriptor[(int) types.getSize()]; int i=0; while((it = iterator.next()) != finished_.get_()){ ceylon.language.meta.model.Type annotationType = (ceylon.language.meta.model.Type)it; ret[i++] = getTypeDescriptor(annotationType); } return ret; } @SuppressWarnings("rawtypes") public static TypeDescriptor getTypeDescriptor(ceylon.language.meta.model.Type appliedType) { if(appliedType instanceof ClassImpl){ return ((ClassImpl) appliedType).$reifiedType; } if(appliedType instanceof InterfaceImpl){ return ((InterfaceImpl) appliedType).$reifiedType; } if(appliedType instanceof UnionTypeImpl){ return ((UnionTypeImpl) appliedType).$reifiedUnion; } if(appliedType instanceof IntersectionTypeImpl){ return ((IntersectionTypeImpl) appliedType).$reifiedIntersection; } if(appliedType == ceylon.language.meta.model.nothingType_.get_()) return TypeDescriptor.NothingType; throw Metamodel.newModelError("Unsupported type: " + appliedType); } private static TypeDescriptor[] getTypeDescriptorsForProducedTypes(List args) { TypeDescriptor[] tdArgs = new TypeDescriptor[args.size()]; for(int i=0;i pt) { if(pt instanceof ClassOrInterfaceImpl) return ((ClassOrInterfaceImpl)pt).producedType; if(pt instanceof UnionTypeImpl) return ((UnionTypeImpl)pt).model; if(pt instanceof IntersectionTypeImpl) return ((IntersectionTypeImpl)pt).model; if(pt instanceof ceylon.language.meta.model.nothingType_) return new NothingType(moduleManager.getModelLoader().getUnit()).getType(); throw Metamodel.newModelError("Unsupported applied produced type: " + pt); } public static com.redhat.ceylon.model.typechecker.model.Package getPackage(com.redhat.ceylon.model.typechecker.model.Declaration declaration) { Scope scope = declaration.getContainer(); while(scope != null && scope instanceof com.redhat.ceylon.model.typechecker.model.Package == false) scope = scope.getContainer(); if(scope == null) throw Metamodel.newModelError("Declaration with no package: "+declaration); return (com.redhat.ceylon.model.typechecker.model.Package)scope; } public static java.util.List getProducedTypes(Sequential> types) { Iterator iterator = types.iterator(); Object it; List producedTypes = new LinkedList(); while((it = iterator.next()) != finished_.get_()){ ceylon.language.meta.model.Type pt = (ceylon.language.meta.model.Type) it; com.redhat.ceylon.model.typechecker.model.Type modelPt = Metamodel.getModel(pt); producedTypes.add(modelPt); } return producedTypes; } /** * returns the java.lang.Class of the given the Ceylon metamodel of * an annotation class. */ public static Class getReflectedAnnotationClass( ClassOrInterface annotationType) { ClassOrInterfaceDeclarationImpl freeClass; if (annotationType instanceof ClassOrInterfaceImpl) { freeClass = (ClassOrInterfaceDeclarationImpl)((ClassOrInterfaceImpl)annotationType).getDeclaration(); } else { freeClass = (ClassOrInterfaceDeclarationImpl)annotationType; } final Class refAnnotationClass = getJavaClass(freeClass.declaration); return refAnnotationClass; } private static void addAnnotation( Annotated annotated, ArrayList ceylonAnnotations, java.lang.annotation.Annotation jAnnotation, Predicates.Predicate pred) { Class jAnnotationType = jAnnotation.annotationType(); if (pred != null && pred instanceof Predicates.AnnotationPredicate && !((Predicates.AnnotationPredicate)pred).shouldInstantiate(jAnnotationType)) { return; } if (jAnnotationType.getAnnotation(Ceylon.class) == null) { // It's a Java annotation addProxyCeylonAnnotation(annotated, ceylonAnnotations, jAnnotation); return; } if (jAnnotationType.getName().endsWith("$annotations$")) { java.lang.annotation.Annotation[] jAnnotations; try { Method method = jAnnotationType.getMethod("value"); method.setAccessible(true); jAnnotations = (java.lang.annotation.Annotation[])method.invoke(jAnnotation); } catch (RuntimeException e) { throw e; } catch (Exception e) {/* aka ReflectiveOperationException */ throw Metamodel.newModelError("While unwrapping a sequenced annotation of element " + annotated, e); } for (java.lang.annotation.Annotation wrapped : jAnnotations) { addAnnotation(annotated, ceylonAnnotations, wrapped, pred); } } else { // Find the annotation class java.lang.Class annotationClass = getAnnotationClass(jAnnotationType, annotated); // Invoke it with the jAnnotation as the only argument try { Constructor constructor = annotationClass.getDeclaredConstructor(jAnnotationType); constructor.setAccessible(true); A cAnnotation = constructor.newInstance(jAnnotation); if (pred.accept(cAnnotation)) { ceylonAnnotations.add(cAnnotation); } } catch (RuntimeException e) { throw e; } catch (Exception e) {/* aka ReflectiveOperationException */ throw Metamodel.newModelError("While reflectively instantiating " + annotationClass + " on element " + annotated, e); } } } /** * Gets the {@code java.lang.Class} of the Ceylon annotation class, * given the {@code java.lang.Class} of a Java annotation type */ protected static Class getAnnotationClass( Class jAnnotationType, Annotated annotated) { String annotationName = jAnnotationType.getName(); if (!annotationName.endsWith("$annotation$")) { throw Metamodel.newModelError("Annotation has invalid name: "+annotationName); } String className = annotationName.substring(0, annotationName.length() - "$annotation$".length()); java.lang.Class annotationClass; try { annotationClass = (Class)Class.forName(className, false, jAnnotationType.getClassLoader()); } catch (ClassNotFoundException e) { throw Metamodel.newModelError("Unable to find annotation class " + className + " for annotation type " + annotationName + " on element "+ annotated, e); } return annotationClass; } protected static Class getJavaAnnotationClass( Class ceylonAnnotationClass) { if(((Class)ceylonAnnotationClass) == ceylon.language.Annotation.class || ((Class)ceylonAnnotationClass) == ceylon.language.ConstrainedAnnotation.class) return (Class) java.lang.annotation.Annotation.class; String suffix; if (ceylon.language.SequencedAnnotation.class.isAssignableFrom(ceylonAnnotationClass)) { suffix = "$annotations$"; } else { suffix = "$annotation$"; } String classname = ceylonAnnotationClass.getName() + suffix; try { return (Class)Class.forName(classname , false, ceylonAnnotationClass.getClassLoader()); } catch (ClassNotFoundException e) { throw Metamodel.newModelError("Unable to find annotation class " + classname + " for annotation type " + ceylonAnnotationClass, e); } } private static void addProxyCeylonAnnotation( Annotated annotated, ArrayList ceylonAnnotations, java.lang.annotation.Annotation jAnnotation) { Class jAnnotationType = jAnnotation.annotationType(); // we add java.lang.Deprecated on top of our own annotation, so ignore it if(jAnnotationType == java.lang.Deprecated.class) return; // ignore all our metadata annotations if(jAnnotationType.getName().startsWith("com.redhat.ceylon.compiler.java.metadata.")) return; // seriously, wtf? ((ArrayList)ceylonAnnotations).add(jAnnotation); } public static Sequential annotations( TypeDescriptor $reifiedValues, Annotated annotated) { // TODO If the annotated is not a valid target for the annotationType // we can return empty immediately Predicates.Predicate predicate = Predicates.isAnnotationOfType($reifiedValues); return annotations($reifiedValues, annotated, predicate); } @SuppressWarnings("unchecked") public static Sequential annotations(TypeDescriptor $reifiedValues, Annotated annotated, Predicates.Predicate predicate) { java.lang.annotation.Annotation[] jAnnotations = ((AnnotationBearing)annotated).$getJavaAnnotations$(); if (jAnnotations == null) { throw Metamodel.newModelError("Unable to find java.lang.reflect.AnnotatedElement for " + annotated); } // TODO Fix initial size estimate when query for OptionalAnnotation ArrayList ceylonAnnotations = new ArrayList(jAnnotations.length); for (java.lang.annotation.Annotation jAnnotation: jAnnotations) { addAnnotation(annotated, ceylonAnnotations, jAnnotation, predicate); } java.lang.annotation.Annotation[] array = ceylonAnnotations.toArray(new java.lang.annotation.Annotation[0]); return new ObjectArrayIterable($reifiedValues, (A[]) array).sequence(); } public static String getJavaMethodName(Functional method) { // FIXME: introduce a damn interface for getRealName() if(method instanceof JavaMethod) return ((JavaMethod)method).getRealName(); else if(method instanceof LazyFunction){ return ((LazyFunction)method).getRealMethodName(); }else throw Metamodel.newModelError("Function declaration type not supported yet: "+method); } public static int getFirstDefaultedParameter(List parameters) { int i = 0; for(Parameter param : parameters){ if(param.isDefaulted() || (param.isSequenced() && !param.isAtLeastOne())){ return i; } i++; } return -1; } public static int getVariadicParameter(List parameters) { int i = 0; for(Parameter param : parameters){ if(param.isSequenced()){ return i; } i++; } return -1; } public static Sequential getModuleList() { // FIXME: this probably needs synchronisation to avoid new modules loaded during traversal Set modules = moduleManager.getModules().getListOfModules(); com.redhat.ceylon.model.typechecker.model.Module[] view = new com.redhat.ceylon.model.typechecker.model.Module[modules.size()]; modules.toArray(view); ceylon.language.meta.declaration.Module[] array = new ceylon.language.meta.declaration.Module[view.length]; int i=0; for(com.redhat.ceylon.model.typechecker.model.Module module : view){ ModuleImpl mod = getOrCreateMetamodel(null, module, null, true); // optional means don't throw if it's not available // skip unavailable modules if(mod != null) array[i++] = mod; } ObjectArrayIterable iterable = new ObjectArrayIterable(ceylon.language.meta.declaration.Module.$TypeDescriptor$, array); return iterable.take(i).sequence(); } /** * Used by c.l.meta.modules.find, which accepts null */ public static ceylon.language.meta.declaration.Module findLoadedModule(String name, String version) { // FIXME: this probably needs synchronisation to avoid new modules loaded during traversal com.redhat.ceylon.model.typechecker.model.Module module = moduleManager.findLoadedModule(name, version); // consider it optional to get null rather than exception return module != null ? getOrCreateMetamodel(null, module, null, true) : null; } /** * Used by c.l.meta.modules.find, which accepts null */ public static Module getDefaultModule() { com.redhat.ceylon.model.typechecker.model.Module module = moduleManager.getModules().getDefaultModule(); // consider it optional to get null rather than exception return module != null ? getOrCreateMetamodel(null, module, null, true) : null; } public static List getParameterProducedTypes(List parameters, Reference producedReference) { List parameterProducedTypes = new ArrayList(parameters.size()); for(Parameter parameter : parameters){ Type ft = producedReference.getTypedParameter(parameter).getFullType(); parameterProducedTypes.add(ft); } return parameterProducedTypes; } public static boolean isCeylon(com.redhat.ceylon.model.typechecker.model.ClassOrInterface declaration){ return JvmBackendUtil.isCeylon(declaration); } public static TypeDescriptor getTypeDescriptorForArguments(com.redhat.ceylon.model.typechecker.model.Unit unit, com.redhat.ceylon.model.typechecker.model.Functional decl, Reference producedReference) { if(!decl.getParameterLists().isEmpty()){ List parameters = decl.getFirstParameterList().getParameters(); com.redhat.ceylon.model.typechecker.model.Type tupleType = unit.getParameterTypesAsTupleType(parameters, producedReference); return Metamodel.getTypeDescriptorForProducedType(tupleType); }else{ return TypeDescriptor.NothingType; } } public static Type getProducedTypeForArguments(com.redhat.ceylon.model.typechecker.model.Unit unit, com.redhat.ceylon.model.typechecker.model.Functional decl, Reference producedReference) { if(!decl.getParameterLists().isEmpty()){ List parameters = decl.getFirstParameterList().getParameters(); return unit.getParameterTypesAsTupleType(parameters, producedReference); }else{ return new NothingType(unit).getType(); } } /** * This is also used by generated code in the JVM compiler, for type declaration literals. * In theory this can only be used for ClassOrInterface or TypeAlias. */ public static ceylon.language.meta.declaration.NestableDeclaration getOrCreateMetamodel(java.lang.Class klass){ synchronized(getLock()){ ceylon.language.meta.declaration.NestableDeclaration result = classToDeclaration.get(klass); if (result == null) { // FIXME: is this really enough? String typeName = klass.getName(); com.redhat.ceylon.model.typechecker.model.Module module = moduleManager.findModuleForClass(klass); com.redhat.ceylon.model.typechecker.model.TypeDeclaration decl = (com.redhat.ceylon.model.typechecker.model.TypeDeclaration) moduleManager.getModelLoader().getDeclaration(module, typeName, DeclarationType.TYPE); result = (ceylon.language.meta.declaration.NestableDeclaration) getOrCreateMetamodel(decl); classToDeclaration.put(klass, result); } return result; } } public static TypeDescriptor getTypeDescriptorForFunction(Reference appliedFunction) { return getTypeDescriptorForProducedType(getFunctionReturnType(appliedFunction)); } public static Type getFunctionReturnType(Reference appliedFunction) { // pull the return type out of the Callable Type fullType = appliedFunction.getFullType(); return fullType.getTypeArgumentList().get(0); } public static com.redhat.ceylon.model.typechecker.model.Parameter getParameterFromTypedDeclaration(com.redhat.ceylon.model.typechecker.model.TypedDeclaration declaration) { if(declaration instanceof com.redhat.ceylon.model.typechecker.model.FunctionOrValue) return ((com.redhat.ceylon.model.typechecker.model.FunctionOrValue) declaration).getInitializerParameter(); return null; } /** * Called when an annotation class is instantiated via an annotation * constructor or annotation callsite to convert the String representation * of a Declaration literal back into the corresponding Declaration. */ @SuppressWarnings("unchecked") public static T parseMetamodelReference(String ref/*, java.lang.Class klass*/) { DeclarationParser parser = new DeclarationParser(); return (T)parser.ref(ref); } /** * Called when an annotation class is instantiated via an annotation * constructor or annotation callsite to convert an array of String representations * of Declaration literals back into a Sequential of Declarations. */ @SuppressWarnings("unchecked") public static Sequential parseMetamodelReferences(TypeDescriptor $reifiedElement, String[] refs) { DeclarationParser parser = new DeclarationParser(); ceylon.language.meta.declaration.Declaration[] array = new ceylon.language.meta.declaration.Declaration[refs.length]; for (int ii = 0; ii < refs.length; ii++) { array[ii] = (T)parser.ref(refs[ii]); } return Util.sequentialWrapper($reifiedElement, (T[])array); } @SuppressWarnings("unchecked") public static T parseEnumerationReference(java.lang.Class klass) { ClassOrInterfaceDeclarationImpl decl = (ClassOrInterfaceDeclarationImpl)getOrCreateMetamodel(klass); String getterName = NamingBase.getGetterName(decl.declaration); try { java.lang.reflect.Method method = klass.getDeclaredMethod(getterName); method.setAccessible(true); return (T)method.invoke(null); } catch (RuntimeException e) { throw e; } catch (Exception e) {/* aka ReflectiveOperationException */ throw Metamodel.newModelError(e.toString()); } } @SuppressWarnings("unchecked") public static Sequential parseEnumerationReferences(TypeDescriptor $reifiedElement, java.lang.Class[] refs) { Object[] array = new Object[refs.length]; for (int ii = 0; ii < refs.length; ii++) { array[ii] = parseEnumerationReference(refs[ii]); } return new ObjectArrayIterable($reifiedElement, (T[]) array).sequence(); } public static Sequential getTypeParameters(com.redhat.ceylon.model.typechecker.model.Generic declaration) { List typeParameters = declaration.getTypeParameters(); ceylon.language.meta.declaration.TypeParameter[] typeParametersArray = new ceylon.language.meta.declaration.TypeParameter[typeParameters.size()]; int i=0; for(com.redhat.ceylon.model.typechecker.model.TypeParameter tp : typeParameters){ typeParametersArray[i++] = new com.redhat.ceylon.compiler.java.runtime.metamodel.decl.TypeParameterImpl(tp); } return Util.sequentialWrapper(ceylon.language.meta.declaration.TypeParameter.$TypeDescriptor$, typeParametersArray); } @SuppressWarnings("hiding") public static DeclarationType findDeclarationByName(Sequential declarations, String name) { Iterator iterator = declarations.iterator(); Object it; while((it = iterator.next()) != finished_.get_()){ @SuppressWarnings("unchecked") DeclarationType tp = (DeclarationType) it; if(tp.getName().equals(name)) return tp; } return null; } public static AnnotatedDeclaration getContainer(Declaration declaration) { Scope container = declaration.getContainer(); if(container instanceof com.redhat.ceylon.model.typechecker.model.Declaration) return Metamodel.getOrCreateMetamodel((com.redhat.ceylon.model.typechecker.model.Declaration)container); if(container instanceof com.redhat.ceylon.model.typechecker.model.Package) return Metamodel.getOrCreateMetamodel((com.redhat.ceylon.model.typechecker.model.Package)container); // FIXME: can that happen? throw Metamodel.newModelError("Illegal container type: "+container); } public static boolean isLocalType(com.redhat.ceylon.model.typechecker.model.TypeDeclaration decl) { return ((LazyElement)decl).isLocal(); } public static ceylon.language.Map> getTypeArguments(ceylon.language.meta.declaration.GenericDeclaration declaration, Reference appliedFunction) { java.util.Map> typeArguments = new LinkedHashMap>(); Iterator typeParameters = declaration.getTypeParameterDeclarations().iterator(); Object it; java.util.Map ptArguments = appliedFunction.getTypeArguments(); while((it = typeParameters.next()) != finished_.get_()){ com.redhat.ceylon.compiler.java.runtime.metamodel.decl.TypeParameterImpl tp = (com.redhat.ceylon.compiler.java.runtime.metamodel.decl.TypeParameterImpl) it; com.redhat.ceylon.model.typechecker.model.TypeParameter tpDecl = (com.redhat.ceylon.model.typechecker.model.TypeParameter) tp.declaration; com.redhat.ceylon.model.typechecker.model.Type ptArg = ptArguments.get(tpDecl); ceylon.language.meta.model.Type ptArgWrapped = Metamodel.getAppliedMetamodel(ptArg); typeArguments.put(tp, ptArgWrapped); } return new InternalMap>(ceylon.language.meta.declaration.TypeParameter.$TypeDescriptor$, TD_ClosedTypeOfAnything, typeArguments); } public static ceylon.language.Map> getTypeArgumentWithVariances(ceylon.language.meta.declaration.GenericDeclaration declaration, Reference appliedFunction) { java.util.Map> typeArguments = new LinkedHashMap>(); Iterator typeParameters = declaration.getTypeParameterDeclarations().iterator(); Object it; java.util.Map ptArguments = appliedFunction.getTypeArguments(); Map varianceOverrides = appliedFunction instanceof com.redhat.ceylon.model.typechecker.model.Type ? ((com.redhat.ceylon.model.typechecker.model.Type)appliedFunction).getVarianceOverrides() : null; while((it = typeParameters.next()) != finished_.get_()){ com.redhat.ceylon.compiler.java.runtime.metamodel.decl.TypeParameterImpl tp = (com.redhat.ceylon.compiler.java.runtime.metamodel.decl.TypeParameterImpl) it; com.redhat.ceylon.model.typechecker.model.TypeParameter tpDecl = (com.redhat.ceylon.model.typechecker.model.TypeParameter) tp.declaration; com.redhat.ceylon.model.typechecker.model.Type ptArg = ptArguments.get(tpDecl); ceylon.language.meta.model.Type ptArgWrapped = Metamodel.getAppliedMetamodel(ptArg); ceylon.language.meta.declaration.Variance variance = modelVarianceToMetaModel(varianceOverrides, tpDecl); ceylon.language.Sequence tuple = ceylon.language.Tuple.instance(TD_ClosedTypeArgumentElement, new Object[]{ptArgWrapped, variance}); typeArguments.put(tp, tuple); } return new InternalMap>(ceylon.language.meta.declaration.TypeParameter.$TypeDescriptor$, TD_ClosedTypeArgument, typeArguments); } public static ceylon.language.meta.declaration.Variance modelVarianceToMetaModel(Map varianceOverrides, TypeParameter tpDecl) { if(varianceOverrides == null) return getModelVariance(tpDecl); SiteVariance useSiteVariance = varianceOverrides.get(tpDecl); if(useSiteVariance == null) return ceylon.language.meta.declaration.invariant_.get_(); switch(useSiteVariance){ case IN: return ceylon.language.meta.declaration.contravariant_.get_(); case OUT: return ceylon.language.meta.declaration.covariant_.get_(); } // should not happen return ceylon.language.meta.declaration.invariant_.get_(); } public static String toTypeString(ceylon.language.meta.declaration.NestableDeclaration declaration, ceylon.language.Map> typeArguments){ StringBuffer string = new StringBuffer(); string.append(declaration.getName()); if(declaration instanceof ceylon.language.meta.declaration.GenericDeclaration) addTypeArguments(string, (ceylon.language.meta.declaration.GenericDeclaration)declaration, typeArguments); java.lang.Object container = declaration.getContainer(); while(container != null){ if(container instanceof Package) return ((Package)container).getName() + "::" + string; StringBuffer string2 = new StringBuffer(((NestableDeclaration)container).getName()); if(container instanceof ceylon.language.meta.declaration.GenericDeclaration) addTypeArguments(string2, (ceylon.language.meta.declaration.GenericDeclaration)container, typeArguments); string2.append("."); string.insert(0, string2.toString()); container = ((NestableDeclaration)container).getContainer(); } return string.toString(); } /** * @param Either a Type (appending for a Model) or an * OpenType (appending for a Declaration) */ private static void addTypeArguments(StringBuffer string, ceylon.language.meta.declaration.GenericDeclaration declaration, ceylon.language.Map> typeArguments) { if(!declaration.getTypeParameterDeclarations().getEmpty()){ string.append("<"); Iterator iterator = declaration.getTypeParameterDeclarations().iterator(); Object it; boolean once = true; while((it = iterator.next()) != finished_.get_()){ if(once) once = false; else string.append(","); ceylon.language.meta.declaration.TypeParameter tpDecl = Util.assertExists((ceylon.language.meta.declaration.TypeParameter) it); ceylon.language.Sequence tuple = typeArguments != null ? typeArguments.get(tpDecl) : null; Object val = tuple.getFromFirst(0); ceylon.language.meta.declaration.Variance variance = (ceylon.language.meta.declaration.Variance)tuple.getFromFirst(1); if(variance == ceylon.language.meta.declaration.contravariant_.get_()) string.append("in "); else if(variance == ceylon.language.meta.declaration.covariant_.get_()) string.append("out "); if (val instanceof ceylon.language.meta.model.Type) { string.append(val); } else if (val instanceof ceylon.language.meta.declaration.OpenTypeVariable) { string.append(((ceylon.language.meta.declaration.OpenTypeVariable)val).getDeclaration().getQualifiedName()); } else if (val instanceof ceylon.language.meta.declaration.OpenClassOrInterfaceType) { string.append(((ceylon.language.meta.declaration.OpenClassOrInterfaceType)val).getDeclaration().getQualifiedName()); } else if (val instanceof ceylon.language.meta.declaration.OpenType) { string.append(val); } else { string.append("##Missing##"); } } string.append(">"); } } public static String toTypeString(ceylon.language.meta.model.Model model){ StringBuffer string = new StringBuffer(); ceylon.language.meta.model.Type container = model.getContainer(); if (model instanceof CallableConstructorImpl) { string.append(((CallableConstructorImpl)model).appliedClass.toString()).append("."); } else if (model instanceof ValueConstructorImpl) { string.append(((ValueConstructorImpl)model).clazz.toString()).append("."); } else if (model instanceof MemberClassCallableConstructorImpl) { string.append(((MemberClassCallableConstructorImpl)model).clazz.toString()).append("."); } else if (model instanceof MemberClassValueConstructorImpl) { string.append(((MemberClassValueConstructorImpl)model).clazz.toString()).append("."); } else if(container == null){ string.append(model.getDeclaration().getContainingPackage().getName()).append("::"); } else if(container instanceof ceylon.language.meta.model.ClassOrInterface){ string.append(container.toString()).append("."); } else { string.append("<").append(container.toString()).append(">."); } string.append(model.getDeclaration().getName()); if(model instanceof ceylon.language.meta.model.Generic) addTypeArguments(string, (ceylon.language.meta.declaration.GenericDeclaration) model.getDeclaration(), ((ceylon.language.meta.model.Generic)model).getTypeArgumentWithVariances()); return string.toString(); } public static void checkTypeArguments(Type qualifyingType, Declaration declaration, List typeArguments) { if(declaration instanceof com.redhat.ceylon.model.typechecker.model.Generic){ List typeParameters = ((com.redhat.ceylon.model.typechecker.model.Generic) declaration).getTypeParameters(); if(typeParameters.size() < typeArguments.size()) throw new TypeApplicationException("Too many type arguments provided: "+typeArguments.size()+", but only accepts "+typeParameters.size()); int min = 0; for (TypeParameter tp: typeParameters) { if (!tp.isDefaulted()) min++; } if(typeArguments.size() < min){ String requires = (min == typeParameters.size()) ? "exactly" : "at least"; throw new TypeApplicationException("Not enough type arguments provided: "+typeArguments.size()+", but requires "+requires+" "+min); } for(int i=0;i type) { Type b = Metamodel.getModel(type); return a.isSupertypeOf(b); } public static boolean isSubTypeOf(Type a, ceylon.language.meta.model.Type type) { Type b = Metamodel.getModel(type); return a.isSubtypeOf(b); } public static boolean isExactly(Type a, ceylon.language.meta.model.Type type) { Type b = Metamodel.getModel(type); return a.isExactly(b); } public static ceylon.language.meta.model.Type union( ceylon.language.meta.model.Type typeX, ceylon.language.meta.model.Type typeY) { Type x = Metamodel.getModel(typeX); Type y = Metamodel.getModel(typeY); Type unionType = com.redhat.ceylon.model.typechecker.model.ModelUtil.unionType(x, y, moduleManager.getModelLoader().getUnit()); return getAppliedMetamodel(unionType); } public static ceylon.language.meta.model.Type intersection( ceylon.language.meta.model.Type typeX, ceylon.language.meta.model.Type typeY) { Type x = Metamodel.getModel(typeX); Type y = Metamodel.getModel(typeY); Type intersectionType = com.redhat.ceylon.model.typechecker.model.ModelUtil.intersectionType(x, y, moduleManager.getModelLoader().getUnit()); return getAppliedMetamodel(intersectionType); } public static void checkReifiedTypeArgument(String methodName, String className, Variance variance, Type appliedType, TypeDescriptor $reifiedType) { Type expectedReifiedType = Metamodel.getProducedType($reifiedType); boolean check = checkReifiedTypeArgument(variance, appliedType, expectedReifiedType); if(!check){ String appliedTypeString = appliedType.asString(); String expectedReifiedTypeString = expectedReifiedType.asString(); String appliedString = className.replace("$1", appliedTypeString); String expectedString = className.replace("$1", expectedReifiedTypeString); throw new IncompatibleTypeException("Incompatible type: actual type of applied declaration is "+appliedString +" is not compatible with expected type: "+expectedString+". Try passing the type argument explicitly with: " +methodName+"<"+appliedTypeString+">()"); } } public static void checkReifiedTypeArgument(String methodName, String className, Variance variance1, Type appliedType1, TypeDescriptor $reifiedType1, Variance variance2, Type appliedType2, TypeDescriptor $reifiedType2) { Type expectedReifiedType1 = Metamodel.getProducedType($reifiedType1); Type expectedReifiedType2 = Metamodel.getProducedType($reifiedType2); boolean check1 = checkReifiedTypeArgument(variance1, appliedType1, expectedReifiedType1); boolean check2 = checkReifiedTypeArgument(variance2, appliedType2, expectedReifiedType2); if(!check1 || !check2){ String appliedTypeString1 = appliedType1.asString(); String expectedReifiedTypeString1 = expectedReifiedType1.asString(); String appliedTypeString2 = appliedType2.asString(); String expectedReifiedTypeString2 = expectedReifiedType2.asString(); String appliedString = className.replace("$1", appliedTypeString1).replace("$2", appliedTypeString2); String expectedString = className.replace("$1", expectedReifiedTypeString1).replace("$2", expectedReifiedTypeString2); throw new IncompatibleTypeException("Incompatible type: actual type of applied declaration is "+appliedString +" is not compatible with expected type: "+expectedString+". Try passing the type argument explicitly with: " +methodName+"<"+appliedTypeString1+","+appliedTypeString2+">()"); } } public static void checkReifiedTypeArgument(String methodName, String className, Variance variance1, Type appliedType1, TypeDescriptor $reifiedType1, Variance variance2, Type appliedType2, TypeDescriptor $reifiedType2, Variance variance3, Type appliedType3, TypeDescriptor $reifiedType3) { Type expectedReifiedType1 = Metamodel.getProducedType($reifiedType1); Type expectedReifiedType2 = Metamodel.getProducedType($reifiedType2); Type expectedReifiedType3 = Metamodel.getProducedType($reifiedType3); boolean check1 = checkReifiedTypeArgument(variance1, appliedType1, expectedReifiedType1); boolean check2 = checkReifiedTypeArgument(variance2, appliedType2, expectedReifiedType2); boolean check3 = checkReifiedTypeArgument(variance3, appliedType3, expectedReifiedType3); if(!check1 || !check2 || !check3){ String appliedTypeString1 = appliedType1.asString(); String expectedReifiedTypeString1 = expectedReifiedType1.asString(); String appliedTypeString2 = appliedType2.asString(); String expectedReifiedTypeString2 = expectedReifiedType2.asString(); String appliedTypeString3 = appliedType3.asString(); String expectedReifiedTypeString3 = expectedReifiedType3.asString(); String appliedString = className.replace("$1", appliedTypeString1).replace("$2", appliedTypeString2).replace("$3", appliedTypeString3); String expectedString = className.replace("$1", expectedReifiedTypeString1).replace("$2", expectedReifiedTypeString2).replace("$3", expectedReifiedTypeString3); throw new IncompatibleTypeException("Incompatible type: actual type of applied declaration is "+appliedString +" is not compatible with expected type: "+expectedString+". Try passing the type argument explicitly with: " +methodName+"<"+appliedTypeString1+","+appliedTypeString2+","+appliedTypeString3+">()"); } } private static boolean checkReifiedTypeArgument(Variance variance, Type appliedType, Type expectedReifiedType) { switch(variance){ case IN: return appliedType.isSupertypeOf(expectedReifiedType); case OUT: return appliedType.isSubtypeOf(expectedReifiedType); case NONE: return appliedType.isExactly(expectedReifiedType); default: throw Metamodel.newModelError("Invalid variance: "+variance); } } public static void checkQualifyingType(Type qualifyingType, Declaration declaration) { Scope container = declaration.getContainer(); if(container instanceof TypeDeclaration == false) throw new IncompatibleTypeException("Declaration container is not a type: "+container); TypeDeclaration typeDecl = (TypeDeclaration) container; Type supertype = qualifyingType.getSupertype(typeDecl); if(supertype == null) throw new IncompatibleTypeException("Invalid container type: "+qualifyingType+" is not a subtype of "+typeDecl); } public static Return namedApply(Callable function, DefaultValueProvider defaultValueProvider, com.redhat.ceylon.model.typechecker.model.Functional declaration, ceylon.language.Iterable,? extends java.lang.Object> arguments, List parameterProducedTypes){ // FIXME: throw for Java declarations java.util.Map argumentMap = collectArguments(arguments); java.util.List parameters = declaration.getFirstParameterList().getParameters(); // store the values in an array Array values = new Array(Anything.$TypeDescriptor$, parameters.size(), (java.lang.Object) null); int parameterIndex = 0; for(Parameter parameter : parameters){ // get the parameter value and remove it so we can keep track of those we used java.lang.Object value; if(argumentMap.containsKey(parameter.getName())){ value = argumentMap.remove(parameter.getName()); // we have a value: check the type Type argumentType = Metamodel.getProducedType(value); Type parameterType = parameterProducedTypes.get(parameterIndex); if(!argumentType.isSubtypeOf(parameterType)) throw new ceylon.language.meta.model.IncompatibleTypeException("Invalid argument "+parameter.getName()+", expected type "+parameterType+" but got "+argumentType); }else{ // make sure it has a default value if(!parameter.isDefaulted()) throw new InvocationException("Missing value for non-defaulted parameter "+parameter.getName()); // we need to fetch the default value value = defaultValueProvider.getDefaultParameterValue(parameter, values, parameterIndex); argumentMap.remove(parameter.getName()); } values.set(parameterIndex++, value); } // do we have extra unknown/unused parameters left? if(!argumentMap.isEmpty()){ for(String name : argumentMap.keySet()){ throw new InvocationException("No such parameter "+name); } } // FIXME: don't we need to spread any variadic param? // now do a regular invocation Sequential argumentSequence = values.sequence(); // we can trust any variadic or pseudo-variadic since we checked parameter by parameter (no spreading possible) return Util.apply(function, argumentSequence, null); } private static Map collectArguments(ceylon.language.Iterable,? extends Object> arguments) { java.util.Map args = new java.util.HashMap<>(); ceylon.language.Iterator> iterator = arguments.iterator(); java.lang.Object elem; while((elem = iterator.next()) != finished_.get_()){ @SuppressWarnings("unchecked") ceylon.language.Entry entry = (ceylon.language.Entry)elem; args.put(entry.getKey().toString(), entry.getItem()); } return args; } public static Return apply(Callable function, Sequential arguments, List parameterProducedTypes, int firstDefaulted, int variadicIndex){ int argumentCount = Util.toInt(arguments.getSize()); int parameters = parameterProducedTypes.size(); // check minimum if(firstDefaulted == -1){ if(argumentCount < parameters) throw new InvocationException("Not enough arguments to function. Expected "+parameters+" but got only "+argumentCount); }else if(argumentCount < firstDefaulted) throw new InvocationException("Not enough arguments to function. Expected at least "+firstDefaulted+" but got only "+argumentCount); // check maximum if(variadicIndex == -1){ if(argumentCount > parameters) throw new InvocationException("To many arguments to function. Expected at most "+parameters+" but got "+argumentCount); }// if we're variadic we accept any number // now check their types Iterator it = arguments.iterator(); Object arg; int i = 0; Type variadicElement = null; if(variadicIndex != -1) // it must be a Sequential variadicElement = parameterProducedTypes.get(variadicIndex).getTypeArgumentList().get(0); while((arg = it.next()) != finished_.get_()){ Type parameterType = variadicIndex == -1 || i < variadicIndex ? // normal param parameterProducedTypes.get(i) // variadic param : variadicElement; Type argumentType = Metamodel.getProducedType(arg); if(!argumentType.isSubtypeOf(parameterType)) throw new IncompatibleTypeException("Invalid argument "+i+", expected type "+parameterType+" but got "+argumentType); i++; } // they are all good, let's call it TypeDescriptor variadicElementType = variadicElement != null ? Metamodel.getTypeDescriptorForProducedType(variadicElement) : null; return Util.apply(function, arguments, variadicElementType); } public static K bind(ceylon.language.meta.model.Qualified member, Type containerType, Object container){ if (container == null && (!(member instanceof Declared && (((Declared)member).getDeclaration() instanceof NestableDeclaration) && ((NestableDeclaration)((Declared)member).getDeclaration()).getStatic()))) { throw new IncompatibleTypeException("Invalid container "+container+", expected type "+containerType+" but got ceylon.language::Null"); } if(container != null) { Type argumentType = Metamodel.getProducedType(container); if(!argumentType.isSubtypeOf(containerType)) throw new IncompatibleTypeException("Invalid container "+container+", expected type "+containerType+" but got "+argumentType); } return member.$call$(container); } public static int hashCode(NestableDeclarationImpl decl, String type) { int result = 1; java.lang.Object container = decl.getContainer(); result = 37 * result + type.hashCode(); result = 37 * result + (container == null ? 0 : container.hashCode()); result = 37 * result + (decl.getQualifier() == null ? 0 : decl.getQualifier().hashCode()); result = 37 * result + decl.getName().hashCode(); return result; } public static boolean equalsForSameType(NestableDeclarationImpl a, NestableDeclarationImpl b) { if(!Util.eq(a.getContainer(), b.getContainer())) return false; if(!Util.eq(a.getQualifier(), b.getQualifier())) return false; return a.getName().equals(b.getName()); } public static RuntimeException newModelError(String string) { return newModelError(string, null); } public static RuntimeException newModelError(String string, Throwable cause) { // we throw in rethrow in order not to have to declare the error everywhere Util.rethrow(new ModelError(string, cause)); // we don't return any thing since we throw return null; } /** * Used by the generated JVM compiler code */ public static ceylon.language.meta.declaration.Module checkModule(ceylon.language.meta.declaration.Module module, String name, String version){ if(module == null){ String spec; if(version == null) spec = name; else spec = name + "/" + version; if(moduleManager.getModelLoader().isDynamicMetamodel()) return getOrCreateMetamodel(getModuleManager().getModules().getDefaultModule()); throw new ceylon.language.AssertionError("Module "+spec+" is not available"); } return module; } public static Object getCompanionInstance(java.lang.Object instance, com.redhat.ceylon.model.typechecker.model.Interface iface) { if (instance == null) { return null; } try { java.lang.reflect.Method implAccessor = instance.getClass().getMethod("$" + iface.getQualifiedNameString().replace('.', '$').replace("::", "$") + "$impl"); implAccessor.setAccessible(true); return implAccessor.invoke(instance); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } public static ceylon.language.Sequential> getTypeArgumentList(Generic generic) { Object sequence = sequence_.sequence( TypeDescriptor.klass(ceylon.language.meta.model.Type.class, Anything.$TypeDescriptor$), Null.$TypeDescriptor$, generic.getTypeArguments().getItems()); return (Sequential)(sequence != null ? sequence : empty_.get_()); } public static ceylon.language.Sequential getTypeArgumentList(OpenClassOrInterfaceType generic) { Object sequence = sequence_.sequence( OpenType.$TypeDescriptor$, Null.$TypeDescriptor$, generic.getTypeArguments().getItems()); return (Sequential)(sequence != null ? sequence : empty_.get_()); } public static ceylon.language.Sequential> getTypeArgumentWithVarianceList(Generic generic) { Object sequence = sequence_.sequence( TD_ClosedTypeArgument, Null.$TypeDescriptor$, generic.getTypeArgumentWithVariances().getItems()); return (Sequential)(sequence != null ? sequence : empty_.get_()); } public static ceylon.language.Sequential> getTypeArgumentWithVarianceList(OpenClassOrInterfaceType generic) { Object sequence = sequence_.sequence( TD_OpenTypeArgument, Null.$TypeDescriptor$, generic.getTypeArgumentWithVariances().getItems()); return (Sequential)(sequence != null ? sequence : empty_.get_()); } /** * Delegated to by all the instances of {@code Annotated.annotated()} */ public static boolean isAnnotated(TypeDescriptor reifed$AnnotationType, AnnotationBearing annotated) { Class klass = reifed$AnnotationType instanceof TypeDescriptor.Class ? ((TypeDescriptor.Class)reifed$AnnotationType).getKlass() : null; if (klass == ceylon.language.Annotation.class) { // annotated with anything at all (Celyon or Java) Annotation[] ja = annotated.$getJavaAnnotations$(); return ja != null && ja.length > 0; } else if (klass == ceylon.language.OptionalAnnotation.class || klass == ceylon.language.SequencedAnnotation.class || klass == ceylon.language.ConstrainedAnnotation.class) { TypeDescriptor typeArgument = ((TypeDescriptor.Class)reifed$AnnotationType).getTypeArgument(0); if (typeArgument.equals(TypeDescriptor.NothingType)) { Annotation[] ja = annotated.$getJavaAnnotations$(); if (ja != null) { for (Annotation j : ja) { if (j.annotationType().isAnnotationPresent(Ceylon.class) && (klass == ceylon.language.ConstrainedAnnotation.class || j.annotationType().getName().endsWith( klass == ceylon.language.OptionalAnnotation.class ? "$annotation$" : "$annotations$"))) { return true; } } } return false; } else { klass = ((TypeDescriptor.Class)typeArgument).getKlass(); return annotated.$isAnnotated$(getJavaAnnotationClass((Class)klass)); } } else { return annotated.$isAnnotated$(getJavaAnnotationClass((Class)klass)); } } public static ceylon.language.meta.declaration.Variance getModelVariance(TypeParameter declaration) { if(declaration.isInvariant()) return ceylon.language.meta.declaration.invariant_.get_(); if(declaration.isCovariant()) return ceylon.language.meta.declaration.covariant_.get_(); if(declaration.isContravariant()) return ceylon.language.meta.declaration.contravariant_.get_(); throw Metamodel.newModelError("Underlying declaration is neither invariant, covariant nor contravariant"); } public static boolean hasAllAnnotations(AnnotatedDeclaration decl, TypeDescriptor[] annotationTypeDescriptors) { for(TypeDescriptor annotationTypeDescriptor : annotationTypeDescriptors){ if(decl.annotations(annotationTypeDescriptor).getEmpty()){ // skip this declaration return false; } } return true; } public static Sequential getConstructors( ClassOrInterfaceImpl cls, boolean justShared, boolean callableConstructors, TypeDescriptor $reified$Arguments, ceylon.language.Sequential> annotations) { ArrayList ctors = new ArrayList<>(); com.redhat.ceylon.model.typechecker.model.Type reifiedArguments = $reified$Arguments == null ? null : Metamodel.getProducedType($reified$Arguments); TypeDescriptor[] annotationTypeDescriptors = Metamodel.getTypeDescriptors(annotations); if (cls.declaration instanceof ClassWithInitializerDeclarationImpl) { Reference producedReference = cls.declaration.declaration.appliedReference(cls.producedType, Collections.emptyList()); com.redhat.ceylon.model.typechecker.model.Type argumentsType = Metamodel.getProducedTypeForArguments( cls.declaration.declaration.getUnit(), (Functional) cls.declaration.declaration, producedReference); if(reifiedArguments == null || reifiedArguments.isSubtypeOf(argumentsType)) { if(hasAllAnnotations(((ClassWithInitializerDeclarationImpl)cls.declaration).getDefaultConstructor(), annotationTypeDescriptors)) { // TODO test for arguments too //ctors.add(new AppliedInitializer((AppliedClass)cls)); ctors.add(((ClassModel)cls).getDefaultConstructor()); } } } else { for (ceylon.language.meta.declaration.Declaration d : ((ClassDeclarationImpl)cls.declaration).constructors()) { Declaration dd = null; AnnotatedDeclaration annotated; if (d instanceof CallableConstructorDeclarationImpl && callableConstructors) { dd = ((CallableConstructorDeclarationImpl)d).declaration; annotated = (CallableConstructorDeclaration)d; } else if (d instanceof ValueConstructorDeclarationImpl && !callableConstructors) { dd = ((ValueConstructorDeclarationImpl)d).declaration; annotated = (ValueConstructorDeclaration)d; } else { continue; } // ATM this is an AND WRT annotation types: all must be present if(!hasAllAnnotations(annotated, annotationTypeDescriptors)) continue; if (dd instanceof Functional && reifiedArguments != null) { // CallableConstructor need a check on the Reference producedReference = dd.appliedReference(cls.producedType, Collections.emptyList()); com.redhat.ceylon.model.typechecker.model.Type argumentsType = Metamodel.getProducedTypeForArguments( dd.getUnit(), (Functional) dd, producedReference); if(!reifiedArguments.isSubtypeOf(argumentsType)) continue; } // ATM this is an AND WRT annotation types: all must be present if(!Metamodel.hasAllAnnotations((AnnotatedDeclaration)d, annotationTypeDescriptors)) continue; if (dd instanceof Functional && reifiedArguments != null) { // CallableConstructor need a check on the Reference producedReference = dd.appliedReference(cls.producedType, Collections.emptyList()); com.redhat.ceylon.model.typechecker.model.Type argumentsType = Metamodel.getProducedTypeForArguments( dd.getUnit(), (Functional) dd, producedReference); if(!reifiedArguments.isSubtypeOf(argumentsType)) continue; } if (!justShared || (d instanceof NestableDeclaration && ((NestableDeclaration)d).getShared())) { Object ctor; if (cls instanceof ClassImpl) { ctor = ((ClassImpl)cls).getDeclaredConstructor(TypeDescriptor.NothingType, d.getName()); } else {//if (cls instanceof AppliedMemberClass) { ctor = ((MemberClassImpl)cls).getDeclaredConstructor(TypeDescriptor.NothingType, d.getName()); } ctors.add(ctor); } } } Object[] array = ctors.toArray(new Object[ctors.size()]); ObjectArrayIterable iterable = new ObjectArrayIterable( TypeDescriptor.union( TypeDescriptor.klass(FunctionModel.class, cls.$reifiedType, TypeDescriptor.NothingType), TypeDescriptor.klass(ValueModel.class, cls.$reifiedType, TypeDescriptor.NothingType)), (Object[]) array); return (ceylon.language.Sequential) iterable.sequence(); } /** * Return the java.lang.Class for the given reified type, or null if * the given type cannot be represented as a java.lang.Class. Only * types which simplify to class or interface types can be converted. */ public static java.lang.Class getJavaClass(TypeDescriptor $reifiedT) { if ($reifiedT instanceof TypeDescriptor.Intersection) { return (java.lang.Class) getJavaClass(((TypeDescriptor.Intersection)$reifiedT) .toSimpleType(getModuleManager())); } else if ($reifiedT instanceof TypeDescriptor.Union) { return (java.lang.Class) getJavaClass(((TypeDescriptor.Union)$reifiedT) .toSimpleType(getModuleManager())); } if ($reifiedT instanceof TypeDescriptor.Class) { TypeDescriptor.Class klass = (TypeDescriptor.Class) $reifiedT; // this is already erased return (java.lang.Class) klass.getArrayElementClass(); } else if ($reifiedT instanceof TypeDescriptor.Member) { TypeDescriptor.Member member = (TypeDescriptor.Member) $reifiedT; TypeDescriptor m = member.getMember(); if (m instanceof TypeDescriptor.Class) { TypeDescriptor.Member.Class klass = (TypeDescriptor.Class) m; return (java.lang.Class) klass.getKlass(); } } return null; } private static java.lang.Class getJavaClass(Type pt) { TypeDeclaration declaration = pt.getDeclaration(); if(declaration instanceof com.redhat.ceylon.model.typechecker.model.ClassOrInterface){ return getJavaClass(TypeDescriptor.klass(getJavaClass(declaration))); } return null; } }