Package com.redhat.ceylon.compiler.java.runtime.metamodel

Source Code of com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel

package com.redhat.ceylon.compiler.java.runtime.metamodel;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
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 org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;

import ceylon.language.Annotated;
import ceylon.language.Anything;
import ceylon.language.Array;
import ceylon.language.Callable;
import ceylon.language.ConstrainedAnnotation;
import ceylon.language.Iterator;
import ceylon.language.Sequential;
import ceylon.language.empty_;
import ceylon.language.finished_;
import ceylon.language.null_;
import ceylon.language.meta.declaration.AnnotatedDeclaration;
import ceylon.language.meta.declaration.Module;
import ceylon.language.meta.declaration.NestableDeclaration;
import ceylon.language.meta.declaration.OpenType;
import ceylon.language.meta.declaration.Package;
import ceylon.language.meta.model.ClassOrInterface;
import ceylon.language.meta.model.IncompatibleTypeException;
import ceylon.language.meta.model.InvocationException;
import ceylon.language.meta.model.TypeApplicationException;

import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.api.JDKUtils;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.api.RepositoryManagerBuilder;
import com.redhat.ceylon.common.log.Logger;
import com.redhat.ceylon.common.runtime.CeylonModuleClassLoader;
import com.redhat.ceylon.compiler.java.Util;
import com.redhat.ceylon.compiler.java.codegen.Naming;
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.ObjectArray.ObjectArrayIterable;
import com.redhat.ceylon.compiler.java.language.ShortArray;
import com.redhat.ceylon.compiler.java.metadata.Ceylon;
import com.redhat.ceylon.compiler.java.metadata.Variance;
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.loader.ModelLoader.DeclarationType;
import com.redhat.ceylon.compiler.loader.impl.reflect.mirror.ReflectionClass;
import com.redhat.ceylon.compiler.loader.impl.reflect.mirror.ReflectionMethod;
import com.redhat.ceylon.compiler.loader.model.AnnotationProxyClass;
import com.redhat.ceylon.compiler.loader.model.FunctionOrValueInterface;
import com.redhat.ceylon.compiler.loader.model.JavaMethod;
import com.redhat.ceylon.compiler.loader.model.LazyClass;
import com.redhat.ceylon.compiler.loader.model.LazyClassAlias;
import com.redhat.ceylon.compiler.loader.model.LazyElement;
import com.redhat.ceylon.compiler.loader.model.LazyInterface;
import com.redhat.ceylon.compiler.loader.model.LazyMethod;
import com.redhat.ceylon.compiler.loader.model.LazyPackage;
import com.redhat.ceylon.compiler.loader.model.LazyTypeAlias;
import com.redhat.ceylon.compiler.loader.model.LazyValue;
import com.redhat.ceylon.compiler.typechecker.analyzer.ExpressionVisitor;
import com.redhat.ceylon.compiler.typechecker.context.Context;
import com.redhat.ceylon.compiler.typechecker.io.VFS;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Functional;
import com.redhat.ceylon.compiler.typechecker.model.IntersectionType;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.ModuleImport;
import com.redhat.ceylon.compiler.typechecker.model.NothingType;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ProducedReference;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.UnionType;
import com.redhat.ceylon.compiler.typechecker.model.UnknownType;

public class Metamodel {

    private static RuntimeModuleManager moduleManager;
   
    // FIXME: this will need better thinking in terms of memory usage
    private static Map<com.redhat.ceylon.compiler.typechecker.model.Declaration, com.redhat.ceylon.compiler.java.runtime.metamodel.FreeNestableDeclaration> typeCheckModelToRuntimeModel
        = new HashMap<com.redhat.ceylon.compiler.typechecker.model.Declaration, com.redhat.ceylon.compiler.java.runtime.metamodel.FreeNestableDeclaration>();

    private static Map<com.redhat.ceylon.compiler.typechecker.model.Package, com.redhat.ceylon.compiler.java.runtime.metamodel.FreePackage> typeCheckPackagesToRuntimeModel
        = new HashMap<com.redhat.ceylon.compiler.typechecker.model.Package, com.redhat.ceylon.compiler.java.runtime.metamodel.FreePackage>();

    private static Map<com.redhat.ceylon.compiler.typechecker.model.Module, com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule> typeCheckModulesToRuntimeModel
        = new HashMap<com.redhat.ceylon.compiler.typechecker.model.Module, com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule>();

    private static Map<TypeDescriptor,ProducedType> typeDescriptorToProducedType = new WeakHashMap<TypeDescriptor,ProducedType>();

    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() {
        RepositoryManagerBuilder builder = new RepositoryManagerBuilder(new Logger(){

            @Override
            public void error(String str) {
                System.err.println("ERROR: "+str);
            }

            @Override
            public void warning(String str) {
                System.err.println("WARN: "+str);
            }

            @Override
            public void info(String str) {
                System.err.println("INFO: "+str);
            }

            @Override
            public void debug(String str) {
                System.err.println("DEBUG: "+str);
            }
           
        }, false, (int)com.redhat.ceylon.common.Constants.DEFAULT_TIMEOUT);
        RepositoryManager repoManager = builder.buildRepository();
        VFS vfs = new VFS();
        Context context = new Context(repoManager, vfs);
        moduleManager = new RuntimeModuleManager(context);
        moduleManager.initCoreModules();
        moduleManager.prepareForTypeChecking();
        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);
    }

    public static boolean isReified(java.lang.Object o, TypeDescriptor type){
        if (o == null) {
            return type.containsNull();
        }
        TypeDescriptor instanceType = getTypeDescriptor(o);
        if(instanceType == null)
            return false;
        return getProducedType(instanceType).isSubtypeOf(getProducedType(type));
    }

    public static ProducedType 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 ProducedType getProducedType(TypeDescriptor reifiedType) {
        ProducedType producedType;
        synchronized(getLock()){
            producedType = typeDescriptorToProducedType.get(reifiedType);
            if(producedType == null){
                producedType = reifiedType.toProducedType(moduleManager);
                typeDescriptorToProducedType.put(reifiedType, producedType);
            }
        }
        return producedType;
    }

    public static ceylon.language.meta.model.Type<?> getAppliedMetamodel(TypeDescriptor typeDescriptor) {
        if(typeDescriptor == null)
            throw Metamodel.newModelError("Metamodel not yet supported for Java types");
        ProducedType pt = getProducedType(typeDescriptor);
        return getAppliedMetamodel(pt);
    }
   
    public static com.redhat.ceylon.compiler.java.runtime.metamodel.FreeNestableDeclaration getOrCreateMetamodel(com.redhat.ceylon.compiler.typechecker.model.Declaration declaration){
        synchronized(getLock()){
            com.redhat.ceylon.compiler.java.runtime.metamodel.FreeNestableDeclaration ret = typeCheckModelToRuntimeModel.get(declaration);
            if(ret == null){
                // make sure its module is loaded
                com.redhat.ceylon.compiler.typechecker.model.Package pkg = getPackage(declaration);
                com.redhat.ceylon.compiler.typechecker.model.Module mod = pkg.getModule();
                getOrCreateMetamodel(mod);
                if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Class){
                    com.redhat.ceylon.compiler.typechecker.model.Class klass = (com.redhat.ceylon.compiler.typechecker.model.Class) declaration;
                    ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeClass(klass);
                }else if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Interface){
                    com.redhat.ceylon.compiler.typechecker.model.Interface interf = (com.redhat.ceylon.compiler.typechecker.model.Interface)declaration;
                    ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeInterface(interf);
                }else if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.TypeAlias){
                    com.redhat.ceylon.compiler.typechecker.model.TypeAlias alias = (com.redhat.ceylon.compiler.typechecker.model.TypeAlias)declaration;
                    ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeAliasDeclaration(alias);
                }else if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Method){
                    com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration method = (com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration)declaration;
                    ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeFunction(method);
                }else if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Value){
                    com.redhat.ceylon.compiler.typechecker.model.Value value = (com.redhat.ceylon.compiler.typechecker.model.Value)declaration;
                    ret = new FreeValue(value);
                }else if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Setter){
                    com.redhat.ceylon.compiler.typechecker.model.Setter value = (com.redhat.ceylon.compiler.typechecker.model.Setter)declaration;
                    ret = new FreeSetter(value);
                }else{
                    throw Metamodel.newModelError("Declaration type not supported yet: "+declaration);
                }
                typeCheckModelToRuntimeModel.put(declaration, ret);
            }
            return ret;
        }
    }

    public static boolean hasTypeParameters(com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration model) {
        if(model instanceof com.redhat.ceylon.compiler.typechecker.model.Generic)
            return hasTypeParameters((com.redhat.ceylon.compiler.typechecker.model.Generic)model);
        if(model.getContainer() instanceof com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface)
            return hasTypeParameters((com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface)model.getContainer());
        return false;
    }
   
    public static boolean hasTypeParameters(com.redhat.ceylon.compiler.typechecker.model.Generic model) {
        if(!model.getTypeParameters().isEmpty())
            return true;
        Object container = ((com.redhat.ceylon.compiler.typechecker.model.Declaration)model).getContainer();
        if(container instanceof com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface)
            return hasTypeParameters((com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface) container);
        return false;
    }

    public static com.redhat.ceylon.compiler.java.runtime.metamodel.FreePackage getOrCreateMetamodel(com.redhat.ceylon.compiler.typechecker.model.Package declaration){
        synchronized(getLock()){
            com.redhat.ceylon.compiler.java.runtime.metamodel.FreePackage ret = typeCheckPackagesToRuntimeModel.get(declaration);
            if(ret == null){
                // make sure its module is loaded
                com.redhat.ceylon.compiler.typechecker.model.Module mod = declaration.getModule();
                getOrCreateMetamodel(mod);

                ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreePackage(declaration);
                typeCheckPackagesToRuntimeModel.put(declaration, ret);
            }
            return ret;
        }
    }

    public static com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule getOrCreateMetamodel(com.redhat.ceylon.compiler.typechecker.model.Module declaration){
        return getOrCreateMetamodel(declaration, null, false /* not optional */);
    }

    private static com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule getOrCreateMetamodel(com.redhat.ceylon.compiler.typechecker.model.Module declaration,
            Set<com.redhat.ceylon.compiler.typechecker.model.Module> visitedModules, boolean optional){
        synchronized(getLock()){
            com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule ret = typeCheckModulesToRuntimeModel.get(declaration);
            if(ret == null){
                // make sure it is loaded
                loadModule(declaration, visitedModules, optional);
                if(!declaration.isAvailable())
                    return null;
                ret = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeModule(declaration);
                typeCheckModulesToRuntimeModel.put(declaration, ret);
            }
            return ret;
        }
    }

    private static void loadModule(com.redhat.ceylon.compiler.typechecker.model.Module declaration,
            Set<com.redhat.ceylon.compiler.typechecker.model.Module> 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.
        ModuleLoader moduleLoader = org.jboss.modules.Module.getContextModuleLoader();
        // no loading required for these
        if(JDKUtils.isJDKModule(declaration.getNameAsString())
                || JDKUtils.isOracleJDKModule(declaration.getNameAsString()))
            return;
        try {
            org.jboss.modules.Module jbossModule = moduleLoader.loadModule(ModuleIdentifier.create(declaration.getNameAsString(), declaration.getVersion()));
            org.jboss.modules.ModuleClassLoader cl = jbossModule.getClassLoader();
           
            // if we can force it loaded, let's do
            if(cl instanceof CeylonModuleClassLoader){
                // this can complete in another thread or this thread
                ((CeylonModuleClassLoader) cl).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());
                        }
                    }
                }
            }// it was loaded via the bootstrap module loader and does not need forcing
           
            if(visitedModules == null)
                visitedModules = new HashSet<com.redhat.ceylon.compiler.typechecker.model.Module>();
            // do not visit this module again
            visitedModules.add(declaration);
            // make sure its imports are also loaded
            for(ModuleImport mi : declaration.getImports()){
                com.redhat.ceylon.compiler.typechecker.model.Module importedModule = mi.getModule();
                // make sure we don't run in circles
                if(importedModule != null && !visitedModules.contains(importedModule))
                    getOrCreateMetamodel(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.isDefault() && !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() {
        return Metamodel.class.getClassLoader() instanceof org.jboss.modules.ModuleClassLoader;
    }

    public static ceylon.language.meta.declaration.OpenType getMetamodel(ProducedType pt) {
        TypeDeclaration declaration = pt.getDeclaration();
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Class){
            return new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeClassType(pt);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Interface){
            return new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeInterfaceType(pt);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.TypeParameter){
            com.redhat.ceylon.compiler.typechecker.model.TypeParameter tp = (com.redhat.ceylon.compiler.typechecker.model.TypeParameter) declaration;
            return new FreeTypeParameterType(tp);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.UnionType){
            return new FreeUnionType((com.redhat.ceylon.compiler.typechecker.model.UnionType)declaration);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.IntersectionType){
            return new FreeIntersectionType((com.redhat.ceylon.compiler.typechecker.model.IntersectionType)declaration);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.NothingType){
            return ceylon.language.meta.declaration.nothingType_.get_();
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.UnknownType){
            ((com.redhat.ceylon.compiler.typechecker.model.UnknownType)declaration).reportErrors();
        }
        throw Metamodel.newModelError("Declaration type not supported yet: "+declaration);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static Sequential<? extends ceylon.language.meta.declaration.OpenType> getMetamodelSequential(List<ProducedType> types) {
        if(types.isEmpty())
            return (Sequential<? extends ceylon.language.meta.declaration.OpenType>)(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(ProducedType 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<? extends ceylon.language.meta.model.Type<? extends Object>> getAppliedMetamodelSequential(List<ProducedType> types) {
        if(types.isEmpty())
            return (Sequential<? extends ceylon.language.meta.model.Type<? extends Object>>)(Sequential)empty_.get_();
        ceylon.language.meta.model.Type<?>[] ret = new ceylon.language.meta.model.Type[types.size()];
        int i=0;
        for(ProducedType pt : types){
            ret[i++] = Metamodel.getAppliedMetamodel(pt);
        }
        return Util.sequentialWrapper(TypeDescriptor.klass(ceylon.language.meta.model.Type.class, Anything.$TypeDescriptor$),
                                       ret);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <T> ceylon.language.meta.model.Type<T> getAppliedMetamodel(ProducedType pt) {
        TypeDeclaration declaration = pt.getDeclaration();
        if(declaration instanceof com.redhat.ceylon.compiler.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.AppliedClass(reifiedType, reifiedArguments, pt, null, null);
           
            TypeDescriptor reifiedContainer = getTypeDescriptorForProducedType(pt.getQualifyingType());
            return new com.redhat.ceylon.compiler.java.runtime.metamodel.AppliedMemberClass(reifiedContainer, reifiedType, reifiedArguments, pt);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Interface){
            TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt);
            if(declaration.isToplevel() || isLocalType(declaration))
                return new com.redhat.ceylon.compiler.java.runtime.metamodel.AppliedInterface<T>(reifiedType, pt, null, null);

            TypeDescriptor reifiedContainer = getTypeDescriptorForProducedType(pt.getQualifyingType());
            return new com.redhat.ceylon.compiler.java.runtime.metamodel.AppliedMemberInterface(reifiedContainer, reifiedType, pt);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.UnionType){
            TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt);
            return new AppliedUnionType<T>(reifiedType, (com.redhat.ceylon.compiler.typechecker.model.UnionType)declaration);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.IntersectionType){
            TypeDescriptor reifiedType = getTypeDescriptorForProducedType(pt);
            return new AppliedIntersectionType<T>(reifiedType, (com.redhat.ceylon.compiler.typechecker.model.IntersectionType)declaration);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.NothingType){
            return (ceylon.language.meta.model.Type<T>)ceylon.language.meta.model.nothingType_.get_();
        }
        throw Metamodel.newModelError("Declaration type not supported yet: "+declaration);
    }

    public static java.lang.Class<?> getJavaClass(com.redhat.ceylon.compiler.typechecker.model.Module module) {
        String className = module.getNameAsString() + "." + Naming.MODULE_DESCRIPTOR_CLASS_NAME;
        ReflectionClass classMirror = (ReflectionClass)moduleManager.getModelLoader().lookupClassMirror(module, className);
        return classMirror.klass;
       
    }
   
    public static java.lang.Class<?> getJavaClass(com.redhat.ceylon.compiler.typechecker.model.Package pkg) {
        String className = ((LazyPackage) pkg).getNameAsString()+ "." + Naming.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.compiler.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 LazyMethod){
            ReflectionClass classMirror = (ReflectionClass) ((LazyMethod) 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.compiler.typechecker.model.Declaration){
            return getJavaClass((com.redhat.ceylon.compiler.typechecker.model.Declaration)declaration.getContainer());
        }
        throw Metamodel.newModelError("Unsupported declaration type: " + declaration + " of type "+declaration.getClass());
    }

    public static java.lang.reflect.Method getJavaMethod(com.redhat.ceylon.compiler.typechecker.model.Method declaration) {
        if(declaration instanceof JavaMethod){
            ReflectionMethod methodMirror = (ReflectionMethod) ((JavaMethod) declaration).mirror;
            return (java.lang.reflect.Method) methodMirror.method;
        }
        if(declaration instanceof LazyMethod){
            ReflectionMethod methodMirror = (ReflectionMethod) ((LazyMethod) declaration).getMethodMirror();
            return (java.lang.reflect.Method) methodMirror.method;
        }
        throw Metamodel.newModelError("Unsupported declaration type: " + declaration);
    }

    public static TypeDescriptor getTypeDescriptorForProducedType(com.redhat.ceylon.compiler.typechecker.model.ProducedType type) {
        TypeDeclaration declaration = type.getDeclaration();
        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 NothingType){
            return TypeDescriptor.NothingType;
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.UnionType){
            TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getCaseTypes());
            return TypeDescriptor.union(tdArgs);
        }
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.IntersectionType){
            TypeDescriptor[] tdArgs = getTypeDescriptorsForProducedTypes(type.getSatisfiedTypes());
            return TypeDescriptor.intersection(tdArgs);
        }
        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 LazyMethod)
                    classMirror = (ReflectionClass) ((LazyMethod) underlyingDeclaration).classMirror;
                else
                    throw Metamodel.newModelError("Unsupported underlying declaration type: " + underlyingDeclaration);
                ret = TypeDescriptor.functionOrValue(classMirror.klass, tdArgs);
            }else
                ret = TypeDescriptor.functionOrValue(underlyingDeclaration.getName(), 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);
    }

    public static TypeDescriptor[] getTypeDescriptors(Sequential<? extends ceylon.language.meta.model.Type<?>> types) {
        Iterator<? extends ceylon.language.meta.model.Type<?>> 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 AppliedClass){
            return ((AppliedClass) appliedType).$reifiedType;
        }
        if(appliedType instanceof AppliedInterface){
            return ((AppliedInterface) appliedType).$reifiedType;
        }
        if(appliedType instanceof AppliedUnionType){
            return ((AppliedUnionType) appliedType).$reifiedUnion;
        }
        if(appliedType instanceof AppliedIntersectionType){
            return ((AppliedIntersectionType) appliedType).$reifiedIntersection;
        }
        if(appliedType == ceylon.language.meta.model.nothingType_.get_())
            return TypeDescriptor.NothingType;
        throw Metamodel.newModelError("Unsupported type: " + appliedType);
    }

    private static TypeDescriptor[] getTypeDescriptorsForProducedTypes(List<ProducedType> args) {
        TypeDescriptor[] tdArgs = new TypeDescriptor[args.size()];
        for(int i=0;i<tdArgs.length;i++){
            tdArgs[i] = getTypeDescriptorForProducedType(args.get(i));
        }
        return tdArgs;
    }

    public static ceylon.language.meta.declaration.FunctionDeclaration getMetamodel(Method method) {
        // find its container
        Scope container = method.getContainer();
        if(container instanceof com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface){
            com.redhat.ceylon.compiler.java.runtime.metamodel.FreeClassOrInterface classOrInterface = (FreeClassOrInterface) getOrCreateMetamodel((com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface) container);
            // now find the method
            ceylon.language.meta.declaration.FunctionDeclaration ret = classOrInterface.findMethod(method.getName());
            if(ret == null)
                throw Metamodel.newModelError("Failed to find method "+method.getName()+" in "+container);
            return ret;
        }
        if(container instanceof com.redhat.ceylon.compiler.typechecker.model.Package){
            ceylon.language.meta.declaration.Package pkg = getOrCreateMetamodel((com.redhat.ceylon.compiler.typechecker.model.Package) container);
            ceylon.language.meta.declaration.FunctionDeclaration ret = pkg.getFunction(method.getName());
            if(ret == null)
                throw Metamodel.newModelError("Failed to find method "+method.getName()+" in "+container);
            return ret;
        }
        throw Metamodel.newModelError("Unsupported method container for "+method.getName()+": "+container);
    }

    public static com.redhat.ceylon.compiler.typechecker.model.ProducedType getModel(ceylon.language.meta.declaration.OpenType pt) {
        if(pt instanceof FreeClassOrInterfaceType)
            return ((FreeClassOrInterfaceType)pt).producedType;
        throw Metamodel.newModelError("Unsupported produced type: " + pt);
    }

    public static com.redhat.ceylon.compiler.typechecker.model.ProducedType getModel(ceylon.language.meta.model.Type<?> pt) {
        if(pt instanceof AppliedClassOrInterface)
            return ((AppliedClassOrInterface<?>)pt).producedType;
        if(pt instanceof AppliedUnionType<?>)
            return ((AppliedUnionType<?>)pt).model;
        if(pt instanceof AppliedIntersectionType<?>)
            return ((AppliedIntersectionType<?>)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.compiler.typechecker.model.Package getPackage(com.redhat.ceylon.compiler.typechecker.model.Declaration declaration) {
        Scope scope = declaration.getContainer();
        while(scope != null && scope instanceof com.redhat.ceylon.compiler.typechecker.model.Package == false)
            scope = scope.getContainer();
        if(scope == null)
            throw Metamodel.newModelError("Declaration with no package: "+declaration);
        return (com.redhat.ceylon.compiler.typechecker.model.Package)scope;
    }


    public static java.util.List<com.redhat.ceylon.compiler.typechecker.model.ProducedType> getProducedTypes(Sequential<? extends ceylon.language.meta.model.Type<?>> types) {
        Iterator<?> iterator = types.iterator();
        Object it;
        List<com.redhat.ceylon.compiler.typechecker.model.ProducedType> producedTypes = new LinkedList<com.redhat.ceylon.compiler.typechecker.model.ProducedType>();
        while((it = iterator.next()) != finished_.get_()){
            ceylon.language.meta.model.Type<?> pt = (ceylon.language.meta.model.Type<?>) it;
            com.redhat.ceylon.compiler.typechecker.model.ProducedType 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
    <Value extends ConstrainedAnnotation<? extends Value, ? extends Values, ? super ProgramElement>,
    Values,
    ProgramElement extends Annotated>
    Class<?> getReflectedAnnotationClass(
            ClassOrInterface<? extends ConstrainedAnnotation<? extends Value, ? extends Values, ? super ProgramElement>> annotationType) {
        FreeClassOrInterface freeClass;
        if (annotationType instanceof AppliedClassOrInterface) {
            freeClass = (FreeClassOrInterface)(annotationType.getDeclaration());
        } else {
            freeClass = (FreeClassOrInterface)annotationType;
        }
        final Class<?> refAnnotationClass = getJavaClass(freeClass.declaration);
        return refAnnotationClass;
    }
   
    @SuppressWarnings("unchecked")
    private static <A extends ceylon.language.Annotation> void addAnnotation(
            Annotated annotated,
            ArrayList<A> ceylonAnnotations,
            java.lang.annotation.Annotation jAnnotation,
            Predicates.Predicate<A> pred) {
        Class<? extends java.lang.annotation.Annotation> jAnnotationType = jAnnotation.annotationType();
        if (pred != null && pred instanceof Predicates.AnnotationPredicate && !((Predicates.AnnotationPredicate<A>)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 {
                jAnnotations = (java.lang.annotation.Annotation[])jAnnotationType.getMethod("value").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
            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<A> annotationClass;
            try {
                annotationClass = (java.lang.Class<A>)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);
            }
           
            // Invoke it with the jAnnotation as the only argument
            try {
                Constructor<A> 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);
            }
        }
    }
   
    private static void addProxyCeylonAnnotation(
            Annotated annotated, ArrayList<? extends ceylon.language.Annotation> ceylonAnnotations,
            java.lang.annotation.Annotation jAnnotation) {
        Class<? extends java.lang.annotation.Annotation> jAnnotationType = jAnnotation.annotationType();
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, java.lang.reflect.Method method,
                    Object[] args) throws Throwable {
                // TODO Auto-generated method stub
                //
                return null;
            }
        };
        ClassLoader classLoader = jAnnotationType.getClassLoader();
        if (classLoader == null) {
            classLoader = ceylon.language.Annotation.class.getClassLoader();
        }
        try {
            java.lang.reflect.Proxy.newProxyInstance(classLoader,
                    new Class[]{jAnnotationType, ceylon.language.Annotation.class},
                    handler);
        } catch (IllegalArgumentException e) {
            throw Metamodel.newModelError("Element " + annotated
                    + " with Java annotation type " + jAnnotationType.getName()
                    + " (classloader: " + jAnnotationType.getClassLoader()
                    + ") using classloader for proxy: " + classLoader, e);
        }
    }
   
    public static <A extends ceylon.language.Annotation> Sequential<? extends A> annotations(
            TypeDescriptor $reifiedValues,
            Annotated annotated) {
        // TODO If the annotated is not a valid target for the annotationType
        // we can return empty immediately
        Predicates.Predicate<A> predicate = Predicates.isAnnotationOfType($reifiedValues);
        return annotations($reifiedValues, annotated, predicate);
    }

    @SuppressWarnings("unchecked")
    public static <A extends ceylon.language.Annotation> Sequential<? extends A> annotations(TypeDescriptor $reifiedValues,
            Annotated annotated, Predicates.Predicate<A> 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<A> ceylonAnnotations = new ArrayList<A>(jAnnotations.length);
        for (java.lang.annotation.Annotation jAnnotation: jAnnotations) {
            addAnnotation(annotated, ceylonAnnotations, jAnnotation, predicate);
        }
        ceylon.language.Annotation[] array = ceylonAnnotations.toArray(new ceylon.language.Annotation[0]);
    return new ObjectArrayIterable<A>($reifiedValues, (A[]) array).sequence();
    }

    public static String getJavaMethodName(Method method) {
        // FIXME: introduce a damn interface for getRealName()
        if(method instanceof JavaMethod)
            return ((JavaMethod)method).getRealName();
        else if(method instanceof LazyMethod){
            return ((LazyMethod)method).getRealMethodName();
        }else
            throw Metamodel.newModelError("Function declaration type not supported yet: "+method);
    }

    public static int getFirstDefaultedParameter(List<Parameter> parameters) {
        int i = 0;
        for(Parameter param : parameters){
            if(param.isDefaulted()){
                return i;
            }
            i++;
        }
        return -1;
    }

    public static int getVariadicParameter(List<Parameter> parameters) {
        int i = 0;
        for(Parameter param : parameters){
            if(param.isSequenced()){
                return i;
            }
            i++;
        }
        return -1;
    }

    public static Sequential<? extends ceylon.language.meta.declaration.Module> getModuleList() {
        // FIXME: this probably needs synchronisation to avoid new modules loaded during traversal
        Set<com.redhat.ceylon.compiler.typechecker.model.Module> modules = moduleManager.getContext().getModules().getListOfModules();
        com.redhat.ceylon.compiler.typechecker.model.Module[] view = new com.redhat.ceylon.compiler.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.compiler.typechecker.model.Module module : view){
            FreeModule mod = getOrCreateMetamodel(module, null, true); // optional means don't throw if it's not available
            // skip unavailable modules
            if(mod != null)
                array[i++] = mod;
        }
        ObjectArrayIterable<ceylon.language.meta.declaration.Module> iterable =
            new ObjectArrayIterable<ceylon.language.meta.declaration.Module>(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.compiler.typechecker.model.Module module = moduleManager.findLoadedModule(name, version);
        // consider it optional to get null rather than exception
        return module != null ? getOrCreateMetamodel(module, null, true) : null;
    }

    /**
     * Used by c.l.meta.modules.find, which accepts null
     */
    public static Module getDefaultModule() {
        com.redhat.ceylon.compiler.typechecker.model.Module module = moduleManager.getContext().getModules().getDefaultModule();
        // consider it optional to get null rather than exception
        return module != null ? getOrCreateMetamodel(module, null, true) : null;
    }

    public static List<ProducedType> getParameterProducedTypes(List<Parameter> parameters, ProducedReference producedReference) {
        List<ProducedType> parameterProducedTypes = new ArrayList<ProducedType>(parameters.size());
        for(Parameter parameter : parameters){
            ProducedType ft = producedReference.getTypedParameter(parameter).getFullType();
            parameterProducedTypes.add(ft);
        }
        return parameterProducedTypes;
    }
   
    public static boolean isCeylon(com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface declaration){
        if(declaration instanceof LazyClass)
            return ((LazyClass) declaration).isCeylon();
        if(declaration instanceof LazyInterface)
            return ((LazyInterface) declaration).isCeylon();
        throw Metamodel.newModelError("Declaration type not supported: "+declaration);
    }

    public static TypeDescriptor getTypeDescriptorForArguments(com.redhat.ceylon.compiler.typechecker.model.Unit unit,
            com.redhat.ceylon.compiler.typechecker.model.Functional decl,
            ProducedReference producedReference) {
        if(!decl.getParameterLists().isEmpty()){
            List<Parameter> parameters = decl.getParameterLists().get(0).getParameters();
            com.redhat.ceylon.compiler.typechecker.model.ProducedType tupleType
            = unit.getParameterTypesAsTupleType(parameters, producedReference);
            return Metamodel.getTypeDescriptorForProducedType(tupleType);
        }else{
            return TypeDescriptor.NothingType;
        }
    }

    public static ProducedType getProducedTypeForArguments(com.redhat.ceylon.compiler.typechecker.model.Unit unit,
            com.redhat.ceylon.compiler.typechecker.model.Functional decl,
            ProducedReference producedReference) {
       
        if(!decl.getParameterLists().isEmpty()){
            List<Parameter> parameters = decl.getParameterLists().get(0).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){
        // FIXME: is this really enough?
        String typeName = klass.getName();
        com.redhat.ceylon.compiler.typechecker.model.Module module = moduleManager.findModuleForClass(klass);
        com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration decl =
                (com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration)
                    moduleManager.getModelLoader().getDeclaration(module, typeName, DeclarationType.TYPE);
        return (ceylon.language.meta.declaration.NestableDeclaration) getOrCreateMetamodel(decl);
    }

    public static TypeDescriptor getTypeDescriptorForFunction(ProducedReference appliedFunction) {
        return getTypeDescriptorForProducedType(getFunctionReturnType(appliedFunction));
    }
   
    public static ProducedType getFunctionReturnType(ProducedReference appliedFunction) {
        // pull the return type out of the Callable
        ProducedType fullType = appliedFunction.getFullType();
        return fullType.getTypeArgumentList().get(0);
    }

    public static com.redhat.ceylon.compiler.typechecker.model.Parameter getParameterFromTypedDeclaration(com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration declaration) {
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.MethodOrValue)
            return ((com.redhat.ceylon.compiler.typechecker.model.MethodOrValue) 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 extends ceylon.language.meta.declaration.Declaration> 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 <T extends ceylon.language.meta.declaration.Declaration> Sequential<T> 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.<T>sequentialWrapper($reifiedElement, (T[])array);
    }
   
    @SuppressWarnings("unchecked")
    public static <T> T parseEnumerationReference(java.lang.Class<T> klass) {
        FreeClassOrInterface decl = (FreeClassOrInterface)getOrCreateMetamodel(klass);
        String getterName = Naming.getGetterName(decl.declaration);
        try {
            java.lang.reflect.Method method = klass.getMethod(getterName);
            return (T)method.invoke(null);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {/* aka ReflectiveOperationException */
            throw Metamodel.newModelError(e.toString());
        }
    }
   
    @SuppressWarnings("unchecked")
    public static <T> Sequential<? extends T> 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<T>($reifiedElement, (T[]) array).sequence();
    }

    public static Sequential<? extends ceylon.language.meta.declaration.TypeParameter> getTypeParameters(com.redhat.ceylon.compiler.typechecker.model.Generic declaration) {
        List<com.redhat.ceylon.compiler.typechecker.model.TypeParameter> typeParameters = declaration.getTypeParameters();
        ceylon.language.meta.declaration.TypeParameter[] typeParametersArray = new ceylon.language.meta.declaration.TypeParameter[typeParameters.size()];
        int i=0;
        for(com.redhat.ceylon.compiler.typechecker.model.TypeParameter tp : typeParameters){
            typeParametersArray[i++] = new com.redhat.ceylon.compiler.java.runtime.metamodel.FreeTypeParameter(tp);
        }
        return Util.sequentialWrapper(ceylon.language.meta.declaration.TypeParameter.$TypeDescriptor$, typeParametersArray);
    }

    @SuppressWarnings("hiding")
    public static <DeclarationType extends ceylon.language.meta.declaration.Declaration>
        DeclarationType findDeclarationByName(Sequential<? extends DeclarationType> declarations, String name) {
        Iterator<? extends DeclarationType> 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.compiler.typechecker.model.Declaration)
            return Metamodel.getOrCreateMetamodel((com.redhat.ceylon.compiler.typechecker.model.Declaration)container);
        if(container instanceof com.redhat.ceylon.compiler.typechecker.model.Package)
            return Metamodel.getOrCreateMetamodel((com.redhat.ceylon.compiler.typechecker.model.Package)container);
        // FIXME: can that happen?
        throw Metamodel.newModelError("Illegal container type: "+container);
    }

    public static boolean isLocalType(com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration decl) {
        return ((LazyElement)decl).isLocal();
    }

    public static ceylon.language.Map<? extends ceylon.language.meta.declaration.TypeParameter, ? extends ceylon.language.meta.model.Type<?>>
        getTypeArguments(ceylon.language.meta.declaration.GenericDeclaration declaration, ProducedReference appliedFunction) {
       
        java.util.Map<ceylon.language.meta.declaration.TypeParameter, ceylon.language.meta.model.Type<?>> typeArguments
            = new LinkedHashMap<ceylon.language.meta.declaration.TypeParameter, ceylon.language.meta.model.Type<?>>();
        Iterator<? extends ceylon.language.meta.declaration.TypeParameter> typeParameters = declaration.getTypeParameterDeclarations().iterator();
        Object it;
        java.util.Map<com.redhat.ceylon.compiler.typechecker.model.TypeParameter, com.redhat.ceylon.compiler.typechecker.model.ProducedType> ptArguments
        = appliedFunction.getTypeArguments();
        while((it = typeParameters.next()) != finished_.get_()){
            com.redhat.ceylon.compiler.java.runtime.metamodel.FreeTypeParameter tp = (com.redhat.ceylon.compiler.java.runtime.metamodel.FreeTypeParameter) it;
            com.redhat.ceylon.compiler.typechecker.model.TypeParameter tpDecl = (com.redhat.ceylon.compiler.typechecker.model.TypeParameter) tp.declaration;
            com.redhat.ceylon.compiler.typechecker.model.ProducedType 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,
                               ceylon.language.meta.model.Type<?>>(ceylon.language.meta.declaration.TypeParameter.$TypeDescriptor$,
                                                              TypeDescriptor.klass(ceylon.language.meta.model.Type.class, ceylon.language.Anything.$TypeDescriptor$),
                                                              typeArguments);
    }
   
    public static String toTypeString(ceylon.language.meta.declaration.NestableDeclaration declaration,
            ceylon.language.Map<? extends ceylon.language.meta.declaration.TypeParameter, ?> 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 <TypeOrOpenType> Either a Type (appending for a Model) or an
     * OpenType (appending for a Declaration)
     */
    private static <TypeOrOpenType> void addTypeArguments(StringBuffer string, ceylon.language.meta.declaration.GenericDeclaration declaration,
            ceylon.language.Map<? extends ceylon.language.meta.declaration.TypeParameter, TypeOrOpenType> 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 = (ceylon.language.meta.declaration.TypeParameter) it;
                Object val = typeArguments != null ? typeArguments.get(tpDecl) : null;
                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(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).getTypeArguments());
        return string.toString();
    }

    public static void checkTypeArguments(ProducedType qualifyingType, Declaration declaration, List<ProducedType> typeArguments) {
        if(declaration instanceof com.redhat.ceylon.compiler.typechecker.model.Generic){
            List<com.redhat.ceylon.compiler.typechecker.model.TypeParameter> typeParameters = ((com.redhat.ceylon.compiler.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<typeArguments.size();i++){
                ProducedType typeArgument = typeArguments.get(i);
                com.redhat.ceylon.compiler.typechecker.model.TypeParameter typeParameter = typeParameters.get(i);
                for (ProducedType st: typeParameter.getSatisfiedTypes()) {
                    ProducedType sts = st.getProducedType(qualifyingType, declaration, typeArguments);
                    if (!typeArgument.isSubtypeOf(sts)) {
                        throw new TypeApplicationException("Type argument "+i+": "+typeArgument.getProducedTypeQualifiedName()
                                +" does not conform to upper bound constraint: "+sts.getProducedTypeQualifiedName()
                                +" of type parameter "+typeParameter.getQualifiedNameString());
                    }
                }
                if(!ExpressionVisitor.argumentSatisfiesEnumeratedConstraint(qualifyingType, declaration, typeArguments, typeArgument, typeParameter)){
                    throw new TypeApplicationException("Type argument "+i+": "+typeArgument.getProducedTypeQualifiedName()
                            +" does not conform to enumerated constraints "
                            +" of type parameter "+typeParameter.getQualifiedNameString());
                }
            }
        }else{
            if(!typeArguments.isEmpty())
                throw new TypeApplicationException("Declaration does not accept type arguments");
        }
    }

    public static boolean isTypeOf(ProducedType producedType, Object instance) {
        ProducedType instanceType = Metamodel.getProducedType(instance);
        return instanceType.isSubtypeOf(producedType);
    }

    public static boolean isSuperTypeOf(ProducedType a, ceylon.language.meta.model.Type<? extends Object> type) {
        ProducedType b = Metamodel.getModel(type);
        return a.isSupertypeOf(b);
    }

    public static boolean isSubTypeOf(ProducedType a, ceylon.language.meta.model.Type<? extends Object> type) {
        ProducedType b = Metamodel.getModel(type);
        return a.isSubtypeOf(b);
    }

    public static boolean isExactly(ProducedType a, ceylon.language.meta.model.Type<? extends Object> type) {
        ProducedType b = Metamodel.getModel(type);
        return a.isExactly(b);
    }
   
    public static ceylon.language.meta.model.Type<?> union(
            ceylon.language.meta.model.Type<? extends Object> typeX,
            ceylon.language.meta.model.Type<? extends Object> typeY) {
        ProducedType x = Metamodel.getModel(typeX);
        ProducedType y = Metamodel.getModel(typeY);
        ProducedType unionType = com.redhat.ceylon.compiler.typechecker.model.Util.unionType(x, y, moduleManager.getModelLoader().getUnit());
        return getAppliedMetamodel(unionType);
    }
   
    public static ceylon.language.meta.model.Type<?> intersection(
            ceylon.language.meta.model.Type<? extends Object> typeX,
            ceylon.language.meta.model.Type<? extends Object> typeY) {
        ProducedType x = Metamodel.getModel(typeX);
        ProducedType y = Metamodel.getModel(typeY);
        ProducedType intersectionType = com.redhat.ceylon.compiler.typechecker.model.Util.intersectionType(x, y, moduleManager.getModelLoader().getUnit());
        return getAppliedMetamodel(intersectionType);
    }

    public static void checkReifiedTypeArgument(String methodName, String className, Variance variance, ProducedType appliedType, TypeDescriptor $reifiedType) {
        ProducedType expectedReifiedType = Metamodel.getProducedType($reifiedType);
        boolean check = checkReifiedTypeArgument(variance, appliedType, expectedReifiedType);
        if(!check){
            String appliedTypeString = appliedType.getProducedTypeName();
            String expectedReifiedTypeString = expectedReifiedType.getProducedTypeName();
            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, ProducedType appliedType1, TypeDescriptor $reifiedType1,
            Variance variance2, ProducedType appliedType2, TypeDescriptor $reifiedType2) {
        ProducedType expectedReifiedType1 = Metamodel.getProducedType($reifiedType1);
        ProducedType expectedReifiedType2 = Metamodel.getProducedType($reifiedType2);
        boolean check1 = checkReifiedTypeArgument(variance1, appliedType1, expectedReifiedType1);
        boolean check2 = checkReifiedTypeArgument(variance2, appliedType2, expectedReifiedType2);
        if(!check1 || !check2){
            String appliedTypeString1 = appliedType1.getProducedTypeName();
            String expectedReifiedTypeString1 = expectedReifiedType1.getProducedTypeName();
            String appliedTypeString2 = appliedType2.getProducedTypeName();
            String expectedReifiedTypeString2 = expectedReifiedType2.getProducedTypeName();
            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, ProducedType appliedType1, TypeDescriptor $reifiedType1,
            Variance variance2, ProducedType appliedType2, TypeDescriptor $reifiedType2,
            Variance variance3, ProducedType appliedType3, TypeDescriptor $reifiedType3) {
        ProducedType expectedReifiedType1 = Metamodel.getProducedType($reifiedType1);
        ProducedType expectedReifiedType2 = Metamodel.getProducedType($reifiedType2);
        ProducedType 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.getProducedTypeName();
            String expectedReifiedTypeString1 = expectedReifiedType1.getProducedTypeName();
            String appliedTypeString2 = appliedType2.getProducedTypeName();
            String expectedReifiedTypeString2 = expectedReifiedType2.getProducedTypeName();
            String appliedTypeString3 = appliedType3.getProducedTypeName();
            String expectedReifiedTypeString3 = expectedReifiedType3.getProducedTypeName();
            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, ProducedType appliedType, ProducedType 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(ProducedType 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;
        ProducedType supertype = qualifyingType.getSupertype(typeDecl);
        if(supertype == null)
            throw new IncompatibleTypeException("Invalid container type: "+qualifyingType+" is not a subtype of "+typeDecl);
    }
   
    public static <Return> Return namedApply(Callable<? extends Return> function,
            DefaultValueProvider defaultValueProvider,
            com.redhat.ceylon.compiler.typechecker.model.Functional declaration,
            ceylon.language.Iterable<? extends ceylon.language.Entry<? extends ceylon.language.String,? extends java.lang.Object>,? extends java.lang.Object> arguments,
            List<ProducedType> parameterProducedTypes){
        // FIXME: throw for Java declarations
       
        java.util.Map<java.lang.String,java.lang.Object> argumentMap = collectArguments(arguments);
       
        java.util.List<Parameter> parameters = declaration.getParameterLists().get(0).getParameters();
       
        // store the values in an array
        Array<java.lang.Object> values = new Array<java.lang.Object>(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
                ProducedType argumentType = Metamodel.getProducedType(value);
                ProducedType 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<? extends Object> argumentSequence = values.sequence();
        return Util.apply(function, argumentSequence);
    }
   
    private static Map<String, Object> collectArguments(ceylon.language.Iterable<? extends ceylon.language.Entry<? extends ceylon.language.String,? extends Object>,? extends Object> arguments) {
        java.util.Map<java.lang.String,java.lang.Object> args = new java.util.HashMap<>();
        ceylon.language.Iterator<? extends ceylon.language.Entry<? extends ceylon.language.String,? extends java.lang.Object>> iterator
                = arguments.iterator();
        java.lang.Object elem;
        while((elem = iterator.next()) != finished_.get_()){
            @SuppressWarnings("unchecked")
            ceylon.language.Entry<? extends ceylon.language.String,? extends java.lang.Object> entry
                = (ceylon.language.Entry<? extends ceylon.language.String,? extends java.lang.Object>)elem;
            args.put(entry.getKey().toString(), entry.getItem());
        }
        return args;
    }

    public static <Return> Return apply(Callable<? extends Return> function,
            Sequential<?> arguments,
            List<ProducedType> 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;
        ProducedType variadicElement = null;
        if(variadicIndex != -1)
            // it must be a Sequential<T>
            variadicElement = parameterProducedTypes.get(variadicIndex).getTypeArgumentList().get(0);
        while((arg = it.next()) != finished_.get_()){
            ProducedType parameterType = variadicIndex == -1 || i < variadicIndex ?
                    // normal param
                    parameterProducedTypes.get(i)
                    // variadic param
                    : variadicElement;
            ProducedType 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
        return Util.apply(function, arguments);
    }
   
    public static ceylon.language.meta.model.Model bind(ceylon.language.meta.model.Member<?,?> member, ProducedType containerType, Object container){
        if(container == null)
            throw new IncompatibleTypeException("Invalid container "+container+", expected type "+containerType+" but got ceylon.language::Null");
        ProducedType 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(FreeNestableDeclaration 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(FreeNestableDeclaration a, FreeNestableDeclaration 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;
            throw new ceylon.language.AssertionError("Module "+spec+" is not available");
        }
        return module;
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel

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