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

com.redhat.ceylon.compiler.js.loader.JsonPackage Maven / Gradle / Ivy

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

import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_ANNOTATIONS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_ATTRIBUTES;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_CLASSES;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_CONSTRUCTOR;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_CONSTRUCTORS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_DEFAULT;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_DS_VARIANCE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_DYNAMIC;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_FLAGS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_INTERFACES;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_JS_TSENUM;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_METATYPE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_METHODS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_MODULE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_NAME;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_OBJECTS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_PACKAGE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_PACKED_ANNS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_PARAMS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_SATISFIES;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_SELF_TYPE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_TYPE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_TYPES;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_TYPE_ARGS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_TYPE_PARAMS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.KEY_US_VARIANCE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_ALIAS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_ATTRIBUTE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_CLASS;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_GETTER;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_INTERFACE;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_METHOD;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.METATYPE_OBJECT;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.annotationBits;
import static com.redhat.ceylon.compiler.js.loader.MetamodelGenerator.partiallyQualifiedName;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.getSignature;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isVariadic;
import static com.redhat.ceylon.model.typechecker.model.Module.LANGUAGE_MODULE_NAME;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.common.Backends;
import com.redhat.ceylon.compiler.js.CompilerErrorException;
import com.redhat.ceylon.model.typechecker.model.Annotation;
import com.redhat.ceylon.model.typechecker.model.ClassAlias;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Generic;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.InterfaceAlias;
import com.redhat.ceylon.model.typechecker.model.IntersectionType;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.NothingType;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.ParameterList;
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.TypeAlias;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.UnionType;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.model.UnknownType;
import com.redhat.ceylon.model.typechecker.model.Value;
import com.redhat.ceylon.model.typechecker.util.ModuleManager;

public class JsonPackage extends LazyPackage {

    //Ugly hack to have a ref to Basic at hand, to use as implicit supertype of classes
    private final static Map idobj = new HashMap<>();
    //This is to use as the implicit supertype of interfaces
    private final static Map objclass = new HashMap<>();
    //This is for type parameters
    private final static Map voidclass = new HashMap<>();
    private Map model;
    private final String pkgname;
    private boolean loaded = false;
    private final Unit u2 = new Unit();
    private NothingType nothing = new NothingType(u2);
    private UnknownType unknown = new UnknownType(u2);

    static {
        idobj.put(KEY_NAME, "Basic");
        idobj.put(KEY_PACKAGE, LANGUAGE_MODULE_NAME);
        idobj.put(KEY_MODULE, LANGUAGE_MODULE_NAME);
        objclass.put(KEY_NAME, "Object");
        objclass.put(KEY_PACKAGE, LANGUAGE_MODULE_NAME);
        objclass.put(KEY_MODULE, LANGUAGE_MODULE_NAME);
        voidclass.put(KEY_NAME, "Anything");
        voidclass.put(KEY_PACKAGE, LANGUAGE_MODULE_NAME);
        voidclass.put(KEY_MODULE, LANGUAGE_MODULE_NAME);
    }
    public JsonPackage(String pkgname) {
        this.pkgname = pkgname;
        setName(ModuleManager.splitModuleName(pkgname));
    }

    boolean inLoadIfNecessary = false;
    protected void loadIfNecessary() {
        // model will be null if this is a module that we are currently compiling
        if (!inLoadIfNecessary && !loaded && null != model) {
            inLoadIfNecessary = true;
            loadDeclarations();
            inLoadIfNecessary = false;
        }
    }

    public void setModule(com.redhat.ceylon.model.typechecker.model.Module module) {
        if (module instanceof JsonModule && model == null) {
            model = ((JsonModule)module).getModelForPackage(getNameAsString());
            u2.setPackage(this);
            u2.setFilename("");
            u2.setFullPath("");
            u2.setRelativePath("");
            addUnit(u2);
            //Annotations
            if (model != null) {
                if (model.get("$pkg-pa") != null) {
                    int bits = (int)model.remove("$pkg-pa");
                    setShared(hasAnnotationBit(bits, "shared"));
                }
                @SuppressWarnings("unchecked")
                Object pkgAnns = model.remove("$pkg-anns");
                if (pkgAnns instanceof List) {
                    JsonPackage.setNewAnnotations(getAnnotations(), (List>>)pkgAnns);
                } else if (pkgAnns instanceof Map) {
                    JsonPackage.setOldAnnotations(getAnnotations(), (Map>)pkgAnns);
                } else if (pkgAnns != null) {
                    throw new IllegalArgumentException("Annotations should be a List (new format) or a Map (old format)");
                }
            }

            // This was part of loadDeclarations() which is now being called lazily, but
            // needs to run eagerly, for whatever reason.

            if (module.getLanguageModule() == module && LANGUAGE_MODULE_NAME.equals(pkgname)) {
                //Mark the language module as immediately available to bypass certain validations
                module.setAvailable(true);
            }
        }

        super.setModule(module);
    };
    Map getModel() { return model; }

    private void loadDeclarations() {
        if (loaded) return;
        loaded = true;
        if (!isShared()) {
            setShared(model.remove("$pkg-shared") != null);
        }
        for (Map.Entry e : model.entrySet()) {
            String k = e.getKey();
            if (!k.startsWith("$pkg-")) {
                @SuppressWarnings("unchecked")
                Map m = (Map)e.getValue();
                if (m.get(KEY_METATYPE) instanceof String) {
                    String metatype = (String)m.get(KEY_METATYPE);
                    if (METATYPE_CLASS.equals(metatype)) {
                        refineMembers(loadClass(e.getKey(), m, this, null));
                    } else if (METATYPE_INTERFACE.equals(metatype)) {
                        refineMembers(loadInterface(e.getKey(), m, this, null));
                    } else if (metatype.equals(METATYPE_ATTRIBUTE)
                            || metatype.equals(METATYPE_GETTER)) {
                        loadAttribute(k, m, this, null);
                    } else if (metatype.equals(METATYPE_METHOD)) {
                        loadMethod(k, m, this, null);
                    } else if (metatype.equals(METATYPE_OBJECT)) {
                        refineMembers((com.redhat.ceylon.model.typechecker.model.Class)loadObject(k, m, this, null));
                    } else if (metatype.equals(METATYPE_ALIAS)) {
                        loadTypeAlias(k, m, this, null);
                    }
                } else if (m.get(KEY_METATYPE) == null) {
                    throw new IllegalArgumentException("Missing metatype from entry " + m + " under " + e.getKey());
                } else if (m.get(KEY_METATYPE) instanceof ClassOrInterface) {
                    refineMembers((ClassOrInterface)m.get(KEY_METATYPE));
                } else if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof Value) {
                    TypeDeclaration td = ((Value)m.get(MetamodelGenerator.KEY_METATYPE)).getTypeDeclaration();
                    if (td != null) {
                        refineMembers((ClassOrInterface)td);
                    }
                }
            }
        }
    }

    /** Loads a class from the specified map. To avoid circularities, when the class is being created it is
     * added to the map, and once it's been fully loaded, all other keys are removed. */
    @SuppressWarnings("unchecked")
    com.redhat.ceylon.model.typechecker.model.Class loadClass(String name, Map m,
            Scope parent, final List existing) {
        com.redhat.ceylon.model.typechecker.model.Class cls;
        m.remove(KEY_NAME);
        if (m.get(KEY_METATYPE) instanceof com.redhat.ceylon.model.typechecker.model.Class) {
            cls = (com.redhat.ceylon.model.typechecker.model.Class)m.get(KEY_METATYPE);
            if (m.size() <= 3) {
                //It's been fully loaded
                return cls;
            }
        } else {
            //It's not there, so create it
            if (m.containsKey("$alias")) {
                cls = new com.redhat.ceylon.model.typechecker.model.ClassAlias();
            } else {
                cls = new com.redhat.ceylon.model.typechecker.model.Class();
            }
            cls.setAbstract(m.remove("abstract") != null);
            cls.setAnonymous(m.remove("$anon") != null);
            cls.setDynamic(m.remove(KEY_DYNAMIC) != null);
            cls.setContainer(parent);
            cls.setScope(parent);
            cls.setName(name);
            cls.setUnit(u2);
            if (parent == this) {
                u2.addDeclaration(cls);
            }
            parent.addMember(cls);
            m.put(KEY_METATYPE, cls);
            setAnnotations(cls, (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
        }
        //Type parameters are about the first thing we need to load
        final List tparms = parseTypeParameters(
                (List>)m.remove(KEY_TYPE_PARAMS), cls, existing);
        final List allparms = JsonPackage.merge(tparms, existing);
        if (m.containsKey(KEY_SELF_TYPE)) {
            for (TypeParameter t : tparms) {
                if (t.getName().equals(m.get(KEY_SELF_TYPE))) {
                    cls.setSelfType(t.getType());
                }
            }
        }
        //This is to avoid circularity
        if (!(isLanguagePackage() && ("Nothing".equals(name) || "Anything".equals(name)))) {
            if (cls.getExtendedType() == null) {
                if (m.containsKey("super")) {
                    Type father = getTypeFromJson((Map)m.get("super"),
                            parent instanceof Declaration ? (Declaration)parent : null, allparms);
                    if (father != null) {
                        m.remove("super");
                        cls.setExtendedType(father);
                    }
                } else {
                    cls.setExtendedType(getTypeFromJson(idobj,
                            parent instanceof Declaration ? (Declaration)parent : null, allparms));
                }
            }
        }

        if (cls instanceof ClassAlias) {
            ClassAlias ca = (ClassAlias) cls;
            if (m.containsKey(KEY_CONSTRUCTOR)) {
                String constructorName = (String) m.get(KEY_CONSTRUCTOR);
                Function ctorFn = (Function) ca.getExtendedType().getDeclaration().getDirectMember(constructorName, null, false);
                ca.setConstructor(ctorFn.getType().getDeclaration());
            }
            else {
                ca.setConstructor(ca.getExtendedType().getDeclaration());
            }
        }
        if (m.containsKey(KEY_CONSTRUCTORS)) {
            final Map> constructors = (Map>)m.remove(
                    KEY_CONSTRUCTORS);
            for (Map.Entry> cons : constructors.entrySet()) {
                Constructor cnst = new Constructor();
                cnst.setName("$def".equals(cons.getKey())?null:cons.getKey());
                cnst.setContainer(cls);
                cnst.setScope(cls);
                cnst.setUnit(cls.getUnit());
                cnst.setExtendedType(cls.getType());
                cnst.setDynamic(cons.getValue().remove(KEY_DYNAMIC) != null);
                setAnnotations(cnst, (Integer)cons.getValue().remove(KEY_PACKED_ANNS),
                        cons.getValue().remove(KEY_ANNOTATIONS));
                final List> modelPlist = (List>)cons.getValue().remove(
                        KEY_PARAMS);
                cls.addMember(cnst);
                if (modelPlist == null) {
                    //It's a value constructor
                    cls.setEnumerated(true);
                    Value cv = new Value();
                    cv.setName(cnst.getName());
                    cv.setType(cnst.getType());
                    cv.setContainer(cls);
                    cv.setScope(cls);
                    cv.setUnit(cls.getUnit());
                    cv.setVisibleScope(cls.getVisibleScope());
                    cv.setShared(cls.isShared());
                    cv.setDeprecated(cls.isDeprecated());
                    cls.addMember(cv);
                } else {
                    cls.setConstructors(true);
                    final ParameterList plist = parseParameters(modelPlist, cnst, allparms);
                    cnst.addParameterList(plist);
                    plist.setNamedParametersSupported(true);
                    Function cf = new Function();
                    cf.setName(cnst.getName());
                    final Type ft = cnst.appliedType(cnst.getExtendedType(), Collections.emptyList());
                    cf.setType(ft);
                    cf.addParameterList(plist);
                    cf.setContainer(cls);
                    cf.setScope(cls);
                    cf.setUnit(cls.getUnit());
                    cf.setVisibleScope(cnst.getVisibleScope());
                    cf.setShared(cnst.isShared());
                    cf.setDeprecated(cnst.isDeprecated());
                    cf.setDynamic(cnst.isDynamic());
                    cls.addMember(cf);
                }
                if (cons.getValue().containsKey(KEY_JS_TSENUM)) {
                    cnst.setTypescriptEnum((String)cons.getValue().get(KEY_JS_TSENUM));
                }
            }
        } else {
            ParameterList plist = parseParameters((List>)m.remove(KEY_PARAMS),
                    cls, allparms);
            plist.setNamedParametersSupported(true);
            cls.setParameterList(plist);
        }
        if (m.containsKey("of") && cls.getCaseTypes() == null) {
            cls.setCaseTypes(parseTypeList((List>)m.get("of"), allparms));
            m.remove("of");
        }
        if (m.containsKey(KEY_SATISFIES)) {
            List> stypes = (List>)m.remove(KEY_SATISFIES);
            cls.setSatisfiedTypes(parseTypeList(stypes, allparms));
        }
        if (m.containsKey(KEY_OBJECTS)) {
            for (Map.Entry> inner : ((Map>)m.get(KEY_OBJECTS)).entrySet()) {
                loadObject(inner.getKey(), inner.getValue(), cls, allparms);
            }
            m.remove(KEY_OBJECTS);
        }
        addAttributesAndMethods(m, cls, allparms);
        if (m.containsKey(KEY_INTERFACES)) {
            Map> cdefs = (Map>)m.get(KEY_INTERFACES);
            for (Map.Entry> cdef : cdefs.entrySet()) {
                loadInterface(cdef.getKey(), cdef.getValue(), cls, allparms);
            }
            m.remove(KEY_INTERFACES);
        }
        if (m.containsKey(KEY_CLASSES)) {
            Map> cdefs = (Map>)m.get(KEY_CLASSES);
            for (Map.Entry> cdef : cdefs.entrySet()) {
                loadClass(cdef.getKey(), cdef.getValue(), cls, allparms);
            }
            m.remove(KEY_CLASSES);
        }
        if (cls.isDynamic() &&
                (getModule().getJsMajor()<9 ||
                    (getModule().getJsMajor()==9 && getModule().getJsMinor()<1))) {
            // previous versions did not set dynamic flag on members
            cls.makeMembersDynamic();
        }
        return cls;
    }

    /** Parses the specified attributes and methods from JSON data and adds them to the specified declaration. */
    @SuppressWarnings("unchecked")
    private void addAttributesAndMethods(Map m, Declaration d, List tparms) {
        //Attributes
        Map> sub = (Map>)m.get(KEY_ATTRIBUTES);
        if (sub != null) {
            //Only add aliases in the first pass
            for(Map.Entry> e : sub.entrySet()) {
                if (d.getDirectMember(e.getKey(), null, false) == null) {
                    if (METATYPE_ALIAS.equals(e.getValue().get(KEY_METATYPE))) {
                        d.getMembers().add(loadTypeAlias(e.getKey(), e.getValue(), (Scope)d, tparms));
                    }
                }
            }
            //Then the attributes
            for(Map.Entry> e : sub.entrySet()) {
                if (d.getDirectMember(e.getKey(), null, false) == null) {
                    if (!METATYPE_ALIAS.equals(e.getValue().get(KEY_METATYPE))) {
                        d.getMembers().add(loadAttribute(e.getKey(), e.getValue(), (Scope)d, tparms));
                    }
                }
            }
        }
        //Methods
        sub = (Map>)m.get(KEY_METHODS);
        if (sub != null) {
            for(Map.Entry> e : sub.entrySet()) {
                if (d.getDirectMember(e.getKey(), null, false) == null) {
                    d.getMembers().add(loadMethod(e.getKey(), e.getValue(), (Scope)d, tparms));
                }
            }
        }
    }

    /** Creates a list of Type from the references in the maps.
     * @param types A list of maps where each map is a reference to a type or type parameter.
     * @param typeParams The type parameters that can be referenced from the list of maps. */
    private List parseTypeList(List> types, List typeParams) {
        List ts = new ArrayList<>(types.size());
        for (Map st : types) {
            ts.add(getTypeFromJson(st, null, typeParams));
        }
        return ts;
    }

    /** Creates a list of TypeParameter from a list of maps.
     * @param typeParams The list of maps to create the TypeParameters.
     * @param container The declaration which owns the resulting type parameters.
     * @param existing A list of type parameters declared in the parent scopes which can be referenced from
     * the ones that have to be parsed. */
    private List parseTypeParameters(List> typeParams, final Declaration container,
            List existing) {
        if (typeParams == null) return Collections.emptyList();
        //New array with existing parms to avoid modifying that one
        List allparms = new ArrayList<>((existing == null ? 0 : existing.size()) + typeParams.size());
        if (existing != null && !existing.isEmpty()) {
            allparms.addAll(existing);
        }
        List tparms = new ArrayList<>(typeParams.size());
        //To avoid circularity, this is done in two phases:
        //First create the type parameters
        for (Map tp : typeParams) {
            final Declaration maybe;
            if (tp.get(KEY_METATYPE) instanceof TypeParameter) {
                maybe = (TypeParameter)tp.get(KEY_METATYPE);
            } else {
                maybe = container.getDirectMember((String)tp.get(KEY_NAME), null, false);
            }
            if (maybe instanceof TypeParameter) {
                //we already had it (from partial loading elsewhere)
                allparms.add((TypeParameter)maybe);
                tparms.add((TypeParameter)maybe);
                tp.put(KEY_METATYPE, maybe);
            } else {
                TypeParameter tparm = new TypeParameter();
                tparm.setUnit(container.getUnit());
                tparm.setDeclaration(container);
                container.getMembers().add(tparm);
                if (tp.containsKey(KEY_NAME)) {
                    tparm.setName((String)tp.get(KEY_NAME));
                } else if (!tp.containsKey(KEY_TYPES)) {
                    throw new IllegalArgumentException("Invalid type parameter map " + tp);
                }
                String variance = (String)tp.get(KEY_DS_VARIANCE);
                if ("out".equals(variance)) {
                    tparm.setCovariant(true);
                } else if ("in".equals(variance)) {
                    tparm.setContravariant(true);
                }
                if (container instanceof Scope) {
                    Scope scope = (Scope)container;
                    tparm.setContainer(scope);
                    tparm.setScope(scope);
                }
                tparm.setDefaulted(tp.containsKey(KEY_DEFAULT));
                tparms.add(tparm);
                allparms.add(tparm);
                tp.put(KEY_METATYPE, tparm);
            }
        }
        if (container instanceof Generic) {
            ((Generic) container).setTypeParameters(tparms);
        }
        //Second, add defaults and heritage
        for (Map tp : typeParams) {
            TypeParameter tparm = (TypeParameter)tp.get(KEY_METATYPE);
            if (tparm.getExtendedType() == null) {
                if (tp.containsKey(KEY_PACKAGE)) {
                    //Looks like this never happens but...
                    Type subtype = getTypeFromJson(tp, container, allparms);
                    tparm.setExtendedType(subtype);
                } else if (tp.containsKey(KEY_TYPES)) {
                    if (!("u".equals(tp.get("comp")) || "i".equals(tp.get("comp")))) {
                        throw new IllegalArgumentException("Only union or intersection types are allowed as 'comp'");
                    }
                    Type subtype = getTypeFromJson(tp, container, allparms);
                    tparm.setName(subtype.asString());
                    tparm.setExtendedType(subtype);
                } else {
                    tparm.setExtendedType(getTypeFromJson(voidclass, container, null));
                }
            }
            if (tparm.isDefaulted()) {
                @SuppressWarnings("unchecked")
                final Map deftype = (Map)tp.get(KEY_DEFAULT);
                tparm.setDefaultTypeArgument(getTypeFromJson(deftype, container, existing));
            }
            if (tp.containsKey(KEY_SATISFIES)) {
                @SuppressWarnings("unchecked")
                final List> stypes = (List>)tp.get(KEY_SATISFIES);
                tparm.setSatisfiedTypes(parseTypeList(stypes, allparms));
                tparm.setConstrained(true);
            } else if (tp.containsKey("of")) {
                @SuppressWarnings("unchecked")
                final List> oftype = (List>)tp.get("of");
                tparm.setCaseTypes(parseTypeList(oftype, allparms));
                tparm.setConstrained(true);
            }
        }
        return tparms;
    }

    /** Creates a parameter list from a list of maps where each map represents a parameter.
     * @param params The list of maps to create the parameters.
     * @param owner The declaration to assign to each parameter.
     * @param typeParameters The type parameters which can be referenced from the parameters. */
    private ParameterList parseParameters(List> params, Declaration owner, List typeParameters) {
        ParameterList plist = new ParameterList();
        if (params != null) {
            for (Map p : params) {
                Parameter param = new Parameter();
                final String paramtype = (String)p.get("$pt");
                param.setHidden(p.containsKey("$hdn"));
                param.setName((String)p.get(KEY_NAME));
                param.setDeclaration(owner);
                param.setDefaulted(p.containsKey(KEY_DEFAULT));
                param.setSequenced(p.containsKey("seq"));
                param.setAtLeastOne(p.containsKey("$min1"));
                if (paramtype == null || "v".equals(paramtype)) {
                    Value _v = new Value();
                    param.setModel(_v);
                } else if ("f".equals(paramtype)) {
                    @SuppressWarnings("unchecked")
                    List>> paramLists = (List>>)p.get(KEY_PARAMS);
                    Function _m = new Function();
                    param.setModel(_m);
                    if (paramLists == null) {
                        _m.addParameterList(new ParameterList());
                    } else {
                        boolean first = true;
                        for (List> subplist : paramLists) {
                            ParameterList _params = parseParameters(subplist, _m, typeParameters);
                            if (first) {
                                first = false;
                            } else {
                                _params.setNamedParametersSupported(false);
                            }
                            _m.addParameterList(_params);
                        }
                    }
                } else {
                    throw new IllegalArgumentException("Unknown parameter type " + paramtype);
                }
                FunctionOrValue paramModel = param.getModel();
                if (paramModel != null) {
                    paramModel.setInitializerParameter(param);
                    paramModel.setName(param.getName());
                    paramModel.setUnit(u2);
                    if (owner instanceof Scope) {
                        Scope scope = (Scope)owner;
                        paramModel.setContainer(scope);
                        paramModel.setScope(scope);
                    }
                    if (p.get(KEY_TYPE) instanceof Map) {
                        @SuppressWarnings("unchecked")
                        final Map ktype = (Map)p.get(KEY_TYPE);
                        paramModel.setType(getTypeFromJson(ktype, owner, typeParameters));
                    } else {
                        //parameter type
                        for (TypeParameter tp : typeParameters) {
                            if (tp.getName().equals(p.get(KEY_TYPE))) {
                                paramModel.setType(tp.getType());
                            }
                        }
                    }
                    setAnnotations(paramModel, (Integer)p.remove(KEY_PACKED_ANNS), p.remove(KEY_ANNOTATIONS));
                }
                //owner.getMembers().add(param);
                plist.getParameters().add(param);
            }
        }
        return plist;
    }

    @SuppressWarnings("unchecked")
    Function loadMethod(String name, Map m, Scope parent, final List existing) {
        Function md = new Function();
        md.setName(name);
        m.remove(KEY_NAME);
        md.setContainer(parent);
        md.setScope(parent);
        setAnnotations(md, (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
        md.setUnit(u2);
        if (parent == this) {
            //Top-level declarations are directly added to the unit
            u2.addDeclaration(md);
            addMember(null);
        }
        if (m.containsKey(KEY_FLAGS)) {
            int flags = (int)m.remove(KEY_FLAGS);
            md.setDeclaredVoid((flags & 1) > 0);
            md.setDeferred((flags & 2) > 0);
        }
        md.setDynamic(m.remove(KEY_DYNAMIC) != null);
        final List tparms = parseTypeParameters(
                (List>)m.get(KEY_TYPE_PARAMS), md, existing);
        final List allparms = JsonPackage.merge(tparms, existing);
        md.setType(getTypeFromJson((Map)m.remove(KEY_TYPE),
                parent instanceof Declaration ? (Declaration)parent : null, allparms));
        List>> paramLists = (List>>)m.remove(KEY_PARAMS);
        if (paramLists == null) {
            md.addParameterList(new ParameterList());
        } else {
            boolean first = true;
            for (List> plist : paramLists) {
                ParameterList _params = parseParameters(plist, md, allparms);
                _params.setNamedParametersSupported(first);
                first = false;
                md.addParameterList(_params);
                for (Parameter p : _params.getParameters()) {
                    md.addMember(p.getModel());
                }
            }
        }
        return md;
    }

    FunctionOrValue loadAttribute(String name, Map m, Scope parent,
            List typeParameters) {
        String metatype = (String)m.get(KEY_METATYPE);
        Value d = new Value();
        d.setTransient(METATYPE_GETTER.equals(metatype));
        d.setName(name);
        d.setContainer(parent);
        d.setScope(parent);
        d.setUnit(u2);
        if (parent == this) {
            u2.addDeclaration(d);
            addMember(null);
        }
        setAnnotations(d, (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
        d.setDynamic(m.remove(KEY_DYNAMIC) != null);
        if (m.containsKey("var")) {
            d.setVariable(true);
        }
        @SuppressWarnings("unchecked")
        final Map ktype = (Map)m.get(KEY_TYPE);
        d.setType(getTypeFromJson(ktype, parent instanceof Declaration ? (Declaration)parent : null, typeParameters));
        @SuppressWarnings("unchecked")
        final Map smap = (Map)m.remove("$set");
        if (smap != null) {
            Setter s = new Setter();
            s.setName(name);
            s.setContainer(parent);
            s.setScope(parent);
            s.setUnit(u2);
            s.setGetter(d);
            d.setSetter(s);
            if (parent == this) {
                u2.addDeclaration(s);
                addMember(null);
            }
            setAnnotations(s, (Integer)smap.remove(KEY_PACKED_ANNS), smap.remove(KEY_ANNOTATIONS));
            s.setType(d.getType());
        }
        return d;
    }

    /** Sets the refined declarations for the type's members. */
    private void refineMembers(ClassOrInterface coi) {
        //fill refined declarations
        for (Declaration d : coi.getMembers()) {
            if (d.isActual()) {
                Declaration refined = coi.getRefinedMember(d.getName(), getSignature(d), isVariadic(d));
                if (refined==null) refined = d;
                d.setRefinedDeclaration(refined);
            }
            if (d instanceof ClassOrInterface) {
                refineMembers((ClassOrInterface)d);
            }
        }
    }

    @SuppressWarnings("unchecked")
    Interface loadInterface(String name, Map m, Scope parent, final List existing) {
        //Check if it's been loaded first
        //It hasn't been loaded, so create it
        Interface t;
        m.remove(KEY_NAME);
        if (m.get(KEY_METATYPE) instanceof Interface) {
            t = (Interface)m.get(KEY_METATYPE);
            if (m.size() <= 3) {
                //it's been loaded
                return t;
            }
        } else {
            if (m.containsKey("$alias")) {
                t = new InterfaceAlias();
            } else {
                t = new Interface();
            }
            t.setContainer(parent);
            t.setScope(parent);
            t.setName(name);
            t.setUnit(u2);
            if (parent == this) {
                u2.addDeclaration(t);
            }
            parent.addMember(t);
            m.put(KEY_METATYPE, t);
            setAnnotations(t, (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
        }
        if (m.remove(KEY_DYNAMIC) != null) {
            t.setDynamic(true);
        }
        List tparms = t.getTypeParameters();
        List> listOfMaps = (List>)m.get(KEY_TYPE_PARAMS);
        if (listOfMaps != null && (tparms == null || tparms.size() < listOfMaps.size())) {
            tparms = parseTypeParameters(listOfMaps, t, existing);
            m.remove(KEY_TYPE_PARAMS);
        }
        final List allparms = JsonPackage.merge(tparms, existing);
        //All interfaces extend Object, except aliases
        if (t.getExtendedType() == null) {
            if (t.isAlias()) {
                t.setExtendedType(getTypeFromJson((Map)m.remove("$alias"),
                        parent instanceof Declaration ? (Declaration)parent : null, allparms));
            } else {
                t.setExtendedType(getTypeFromJson(objclass,
                        parent instanceof Declaration ? (Declaration)parent : null, null));
            }
        }
        if (m.containsKey(KEY_SELF_TYPE)) {
            for (TypeParameter _tp : tparms) {
                if (_tp.getName().equals(m.get(KEY_SELF_TYPE))) {
                    t.setSelfType(_tp.getType());
                    _tp.setSelfTypedDeclaration(t);
                }
            }
            m.remove(KEY_SELF_TYPE);
        }
        if (m.containsKey("of") && t.getCaseTypes() == null) {
            t.setCaseTypes(parseTypeList((List>)m.remove("of"), allparms));
        }
        if (m.containsKey(KEY_SATISFIES)) {
            for (Type s : parseTypeList((List>)m.remove(KEY_SATISFIES), allparms)) {
                t.getSatisfiedTypes().add(s);
            }
        }
        addAttributesAndMethods(m, t, allparms);
        if (m.containsKey(KEY_INTERFACES)) {
            Map> cdefs = (Map>)m.remove(KEY_INTERFACES);
            for (Map.Entry> cdef : cdefs.entrySet()) {
                loadInterface(cdef.getKey(), cdef.getValue(), t, allparms);
            }
        }
        if (m.containsKey(KEY_CLASSES)) {
            Map> cdefs = (Map>)m.remove(KEY_CLASSES);
            for (Map.Entry> cdef : cdefs.entrySet()) {
                loadClass(cdef.getKey(), cdef.getValue(), t, allparms);
            }
        }
        if (t.isDynamic() &&
                (getModule().getJsMajor()<9 ||
                    (getModule().getJsMajor()==9 && getModule().getJsMinor()<1))) {
            // previous versions did not set dynamic flag on members
            t.makeMembersDynamic();
        }
        return t;
    }

    /** Loads an object declaration, creating it if necessary, and returns its type declaration. */
    @SuppressWarnings("unchecked")
    TypeDeclaration loadObject(String name, Map m, Scope parent, List existing) {
        Value obj;
        if (m.get(KEY_METATYPE) instanceof Value) {
            obj = (Value)m.get(KEY_METATYPE);
        } else {
            obj = new Value();
            m.put(KEY_METATYPE, obj);
            obj.setName(name);
            obj.setContainer(parent);
            obj.setScope(parent);
            obj.setUnit(u2);
            com.redhat.ceylon.model.typechecker.model.Class type = new com.redhat.ceylon.model.typechecker.model.Class();
            type.setName(name);
            type.setAnonymous(true);
            type.setUnit(u2);
            type.setContainer(parent);
            type.setScope(parent);
            if (parent == this) {
                u2.addDeclaration(obj);
                u2.addDeclaration(type);
            }
            parent.addMember(obj);
            obj.setType(type.getType());
            setAnnotations(obj, (Integer)m.get(KEY_PACKED_ANNS), m.get(KEY_ANNOTATIONS));
            setAnnotations(obj.getTypeDeclaration(), (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
            if (type.getExtendedType() == null) {
                if (m.containsKey("super")) {
                    type.setExtendedType(getTypeFromJson((Map)m.remove("super"),
                            parent instanceof Declaration ? (Declaration)parent : null, existing));
                } else {
                    type.setExtendedType(getTypeFromJson(idobj, parent instanceof Declaration ? (Declaration)parent : null, existing));
                }
            }
            if (m.containsKey(KEY_SATISFIES)) {
                List> stypes = (List>)m.remove(KEY_SATISFIES);
                type.setSatisfiedTypes(parseTypeList(stypes, existing));
            }
            if (m.containsKey(KEY_INTERFACES)) {
                for (Map.Entry> inner : ((Map>)m.remove(KEY_INTERFACES)).entrySet()) {
                    loadInterface(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            if (m.containsKey(KEY_CLASSES)) {
                for (Map.Entry> inner : ((Map>)m.remove(KEY_CLASSES)).entrySet()) {
                    loadClass(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            if (m.containsKey(KEY_OBJECTS)) {
                for (Map.Entry> inner : ((Map>)m.remove(KEY_OBJECTS)).entrySet()) {
                    loadObject(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            addAttributesAndMethods(m, type, existing);
        }
        return obj.getTypeDeclaration();
    }

    /** Load a type alias, creating it if necessary.
     * @return The TypeAlias declaration. */
    @SuppressWarnings("unchecked")
    private TypeAlias loadTypeAlias(String name, Map m, Scope parent, List existing) {
        TypeAlias alias;
        if (m.get(KEY_METATYPE) instanceof TypeAlias) {
            //It's been loaded already
            alias = (TypeAlias)m.get(KEY_METATYPE);
            if (m.size() == 1) {
                return alias;
            }
        } else {
            Declaration maybe = parent.getDirectMember(name, null, false);
            if (maybe == null) {
                alias = new TypeAlias();
                alias.setContainer(parent);
                alias.setScope(parent);
                alias.setName(name);
                alias.setUnit(u2);
                setAnnotations(alias, (Integer)m.remove(KEY_PACKED_ANNS), m.remove(KEY_ANNOTATIONS));
                m.put(KEY_METATYPE, alias);
            } else if (maybe instanceof TypeAlias) {
                alias = (TypeAlias)maybe;
            } else {
                throw new IllegalStateException(maybe + " should be an TypeAlias");
            }
        }
        //Gather available type parameters
        List> listOfMaps = (List>)m.get(KEY_TYPE_PARAMS);
        final List tparms;
        if (listOfMaps != null && alias.getTypeParameters().size() allparms = JsonPackage.merge(tparms, existing);
        //All interfaces extend Object, except aliases
        if (alias.getExtendedType() == null) {
            alias.setExtendedType(getTypeFromJson((Map)m.get("$alias"),
                    parent instanceof Declaration ? (Declaration)parent : null, allparms));
        }
        if (m.containsKey(KEY_SELF_TYPE)) {
            for (TypeParameter _tp : tparms) {
                if (_tp.getName().equals(m.get(KEY_SELF_TYPE))) {
                    alias.setSelfType(_tp.getType());
                }
            }
        }
        if (m.containsKey("of")) {
            alias.setCaseTypes(parseTypeList((List>)m.remove("of"), allparms));
        }
        if (m.containsKey(KEY_SATISFIES)) {
            List> stypes = (List>)m.remove(KEY_SATISFIES);
            alias.setSatisfiedTypes(parseTypeList(stypes, allparms));
        }
        m.clear();
        m.put(KEY_METATYPE, alias);
        if (parent == this) {
            u2.addDeclaration(alias);
        }
        parent.addMember(alias);
        return alias;
    }

    /** Looks up a type from model data, creating it if necessary. The returned type will have its
     * type parameters substituted if needed. */
    private Type getTypeFromJson(Map m, Declaration container, List typeParams) {
        TypeDeclaration td = null;
        if (m.get(KEY_METATYPE) instanceof TypeDeclaration) {
            td = (TypeDeclaration)m.get(KEY_METATYPE);
            if (td instanceof ClassOrInterface && td.getUnit().getPackage() instanceof JsonPackage) {
                ((JsonPackage)td.getUnit().getPackage()).load(td.getName(), typeParams);
            }
        }
        final String tname = (String)m.get(KEY_NAME);
        if ("$U".equals(tname)) {
            m.put(KEY_METATYPE, unknown);
            return unknown.getType();
        }
        if (td == null && m.containsKey("comp")) {
            @SuppressWarnings("unchecked")
            final List> tmaps = (List>)m.get(KEY_TYPES);
            final ArrayList types = new ArrayList<>(tmaps.size());
            if ("u".equals(m.get("comp"))) {
                UnionType ut = new UnionType(u2);
                for (Map tmap : tmaps) {
                    types.add(getTypeFromJson(tmap, container, typeParams));
                }
                ut.setCaseTypes(types);
                td = ut;
            } else if ("i".equals(m.get("comp"))) {
                IntersectionType it = new IntersectionType(u2);
                for (Map tmap : tmaps) {
                    types.add(getTypeFromJson(tmap, container, typeParams));
                }
                it.setSatisfiedTypes(types);
                td = it;
            } else {
                throw new IllegalArgumentException("Invalid composite type '" + m.get("comp") + "'");
            }
        } else if (td == null) {
            final String pname = (String)m.get(KEY_PACKAGE);
            if (pname == null) {
                //It's a ref to a type parameter
                final List containerTypeParameters;
                if (container instanceof Constructor) {
                    containerTypeParameters = ((Generic)container.getContainer()).getTypeParameters();
                } else if (container instanceof Generic) {
                    containerTypeParameters = ((Generic)container).getTypeParameters();
                } else {
                    containerTypeParameters = null;
                }
                if (containerTypeParameters != null) {
                    for (TypeParameter typeParam : containerTypeParameters) {
                        if (typeParam.getName().equals(tname)) {
                            td = typeParam;
                        }
                    }
                }
                if (td == null && typeParams != null) {
                    for (TypeParameter typeParam : typeParams) {
                        if (typeParam.getName().equals(tname)) {
                            td = typeParam;
                        }
                    }
                }
            } else {
                String mname = (String)m.get(KEY_MODULE);
                if ("$".equals(mname)) {
                    mname = LANGUAGE_MODULE_NAME;
                }
                com.redhat.ceylon.model.typechecker.model.Package rp;
                if ("$".equals(pname) || LANGUAGE_MODULE_NAME.equals(pname)) {
                    //Language module package
                    rp = isLanguagePackage()? this :
                        getModule()
                            .getLanguageModule()
                            .getDirectPackage(LANGUAGE_MODULE_NAME);
                } else if (mname == null) {
                    //local type
                    if (".".equals(pname)) {
                        rp = this;
                        if (container instanceof TypeDeclaration && tname.equals(container.getName())) {
                            td = (TypeDeclaration)container;
                        }
                    } else {
                        rp = getModule().getDirectPackage(pname);
                    }
                } else {
                    rp = getModule().getPackage(pname);
                }
                if (rp == null) {
                    throw new CompilerErrorException("Package not found: " + pname);
                }
                if (rp != this && rp instanceof JsonPackage && !((JsonPackage)rp).loaded) {
                    ((JsonPackage) rp).loadIfNecessary();
                }
                final boolean nested = tname.indexOf('.') > 0;
                final String level1 = nested ? tname.substring(0, tname.indexOf('.')) : tname;
                if (rp != null && !nested) {
                    Declaration d = rp.getDirectMember(tname, null, false);
                    if (d instanceof TypeDeclaration) {
                        td = (TypeDeclaration)d;
                        if (td.isTuple()) {
                            if (m.containsKey(KEY_TYPES)) {
                                @SuppressWarnings("unchecked")
                                List> elemaps = (List>)m.get(KEY_TYPES);
                                ArrayList elems = new ArrayList<>(elemaps.size());
                                for (Map elem : elemaps) {
                                    elems.add(getTypeFromJson(elem, container, typeParams));
                                }
                                Type tail = elems.get(elems.size()-1);
                                if ((tail.isSequence() || tail.isSequential())
                                        && !tail.isTuple() && !tail.isEmpty()) {
                                    elems.remove(elems.size()-1);
                                } else {
                                    tail = null;
                                }
                                return u2.getTupleType(elems, tail, -1);
                            } else if (m.containsKey("count")) {
                                @SuppressWarnings("unchecked")
                                Map elem = (Map)m.get(KEY_TYPE);
                                Type[] elems = new Type[(int)m.remove("count")];
                                Arrays.fill(elems, getTypeFromJson(elem, container, typeParams));
                                return u2.getTupleType(Arrays.asList(elems), null, -1);
                            }
                        }
                    } else if (d instanceof FunctionOrValue) {
                        td = ((FunctionOrValue)d).getTypeDeclaration();
                    }
                }
                if (td == null && rp instanceof JsonPackage) {
                    if (nested) {
                        td = ((JsonPackage)rp).loadNestedType(tname, typeParams);
                    } else {
                        td = (TypeDeclaration)((JsonPackage)rp).load(tname, typeParams);
                    }
                }
                //Then look in the top-level declarations
                if (nested && td == null) {
                    for (Declaration d : rp.getMembers()) {
                        if (d instanceof TypeDeclaration && level1.equals(d.getName())) {
                            td = (TypeDeclaration)d;
                        }
                    }
                    final String[] path = tname.split("\\.");
                    for (int i = 1; i < path.length; i++) {
                        td = (TypeDeclaration)td.getDirectMember(path[i], null, false);
                    }
                }
            }
        }
        //From 1.2.3 we stored type arguments in maps
        final Type newType = loadTypeArguments(m, td, container, typeParams);
        if (newType != null) {
            return newType;
        }
        //This is the old pre 1.2.3 stuff
        @SuppressWarnings("unchecked")
        final List> modelParms = (List>)m.get(KEY_TYPE_PARAMS);
        if (td != null && modelParms != null) {
            //Substitute type parameters
            final HashMap concretes = new HashMap<>();
            HashMap variances = null;
            if (td.getTypeParameters().size() < modelParms.size()) {
                if (td.getUnit().getPackage() == this) {
                    parseTypeParameters(modelParms, td, null);
                }
            }
            final Iterator viter = td.getTypeParameters().iterator();
            for (Map ptparm : modelParms) {
                TypeParameter _cparm = viter.next();
                if (ptparm.containsKey(KEY_PACKAGE) || ptparm.containsKey(KEY_TYPES)) {
                    //Substitute for proper type
                    final Type _pt = getTypeFromJson(ptparm, container, typeParams);
                    concretes.put(_cparm, _pt);
                } else if (ptparm.containsKey(KEY_NAME) && typeParams != null) {
                    //Look for type parameter with same name
                    for (TypeParameter typeParam : typeParams) {
                        if (typeParam.getName().equals(ptparm.get(KEY_NAME))) {
                            concretes.put(_cparm, typeParam.getType());
                        }
                    }
                }
                Integer usv = (Integer)ptparm.get(KEY_US_VARIANCE);
                if (usv != null) {
                    if (variances == null) {
                        variances = new HashMap<>();
                    }
                    variances.put(_cparm, SiteVariance.values()[usv]);
                }
            }
            if (!concretes.isEmpty()) {
                return td.getType().substitute(concretes, variances);
            }
        }
        if (td == null) {
            try {
                throw new IllegalArgumentException(String.format("Couldn't find type %s::%s for %s in %s<%s> (FROM pkg %s)",
                        m.get(KEY_PACKAGE), m.get(KEY_NAME),
                        m.get(KEY_MODULE), m, typeParams, getNameAsString()));
            } catch (IllegalArgumentException ex) {
                ex.printStackTrace();
            }
        }
        return td.getType();
    }

    /** Load the type arguments from a map where keys are the type argument names (including their types,
     * e.g. List.Element) and the keys are maps suitable for decoding with getTypeFromJson) */
    Type loadTypeArguments(Map m, final TypeDeclaration td,
            final Declaration container, final List typeParams) {
        if (td == null) {
            return null;
        }
        @SuppressWarnings("unchecked")
        final Map> targs = (Map>)m.get(
                KEY_TYPE_ARGS);
        if (targs == null) {
            return null;
        }
        //Substitute type parameters
        final HashMap concretes = new HashMap<>(targs.size());
        HashMap variances = null;
        Declaration d = td;
        while (d != null) {
            if (d instanceof Generic) {
                for (TypeParameter tparm : ((Generic)d).getTypeParameters()) {
                    Map targMap = targs.get(partiallyQualifiedName(d) + "." + tparm.getName());
                    if (targMap == null) {
                        //TODO error I guess
                        continue;
                    }
                    if (targMap.containsKey(KEY_PACKAGE) || targMap.containsKey(KEY_TYPES)) {
                        //Substitute for proper type
                        final Type _pt = getTypeFromJson(targMap, container, typeParams);
                        concretes.put(tparm, _pt);
                    } else if (targMap.containsKey(KEY_NAME) && typeParams != null) {
                        //Look for type parameter with same name
                        for (TypeParameter typeParam : typeParams) {
                            if (typeParam.getName().equals(targMap.get(KEY_NAME))) {
                                concretes.put(tparm, typeParam.getType());
                            }
                        }
                    }
                    Integer usv = (Integer)targMap.get(KEY_US_VARIANCE);
                    if (usv != null) {
                        if (variances == null) {
                            variances = new HashMap<>();
                        }
                        variances.put(tparm, SiteVariance.values()[usv]);
                    }
                }
            }
            d = ModelUtil.getContainingDeclaration(d);
        }
        if (!concretes.isEmpty()) {
            return td.getType().substitute(concretes, variances);
        }
        return null;
    }

    /** Load a top-level declaration with the specified name, by parsing its model data. */
    Declaration load(String name, List existing) {
        if (model == null) {
            throw new IllegalStateException("No model available to load " + getNameAsString() + "::" + name);
        }
        @SuppressWarnings("unchecked")
        final Map map = (Map)model.get(name);
        if (map == null) {
            if ("Nothing".equals(name)) {
                if (isLanguagePackage()) {
                    return nothing;
                } else if (getModule().getJsMajor()<9 ||
                        (getModule().getJsMajor()==9 && getModule().getJsMinor()<1)) {
                    //Older versions wrongly referenced a local Nothing, maintain backwards compat
                    return nothing;
                }
            } else if ("$U".equals(name)) {
                return unknown;
            }
            throw new IllegalStateException("Cannot find " + pkgname + "::" + name + " in " + model.keySet());
        }
        Object metatype = map.get(KEY_METATYPE);
        if (metatype == null) {
            throw new IllegalArgumentException("Missing metatype from entry " + map);
        }
        if (metatype.equals(METATYPE_ATTRIBUTE)
                || metatype.equals(METATYPE_GETTER)) {
            return loadAttribute(name, map, this, null);
        } else if (metatype.equals(METATYPE_CLASS) || metatype instanceof com.redhat.ceylon.model.typechecker.model.Class) {
            return loadClass(name, map, this, existing);
        } else if (metatype.equals(METATYPE_INTERFACE) || metatype instanceof com.redhat.ceylon.model.typechecker.model.Interface) {
            return loadInterface(name, map, this, existing);
        } else if (metatype.equals(METATYPE_METHOD)) {
            return loadMethod(name, map, this, existing);
        } else if (metatype.equals(METATYPE_OBJECT) || metatype instanceof Value) {
            return loadObject(name, map, this, existing);
        } else if (metatype.equals(METATYPE_ALIAS) || metatype instanceof TypeAlias) {
            return loadTypeAlias(name, map, this, existing);
        }
        System.out.println("WTF is this shit " + map);
        return null;
    }

    public static boolean hasAnnotationBit(int bits, String annotationName) {
        final int idx = annotationBits.indexOf(annotationName);
        if (idx < 0) return false;
        return (bits & (1 << idx)) > 0;
    }

    private void setBitAnnotations(Declaration d, int bits) {
        d.setShared(hasAnnotationBit(bits, "shared"));
        d.setActual(hasAnnotationBit(bits, "actual"));
        d.setFormal(hasAnnotationBit(bits, "formal"));
        d.setDefault(hasAnnotationBit(bits, "default"));
        d.setNativeBackends(hasAnnotationBit(bits, "native") ? Backend.JavaScript.asSet() : Backends.ANY);
        d.setAnnotation(hasAnnotationBit(bits, "annotation"));
        d.setStatic(hasAnnotationBit(bits, "static"));
        if (hasAnnotationBit(bits, "sealed")) {
            ((TypeDeclaration)d).setSealed(true);
        }
        if (d instanceof com.redhat.ceylon.model.typechecker.model.Class) {
            ((com.redhat.ceylon.model.typechecker.model.Class)d).setFinal(hasAnnotationBit(bits, "final"));
            ((com.redhat.ceylon.model.typechecker.model.Class)d).setAbstract(hasAnnotationBit(bits, "abstract"));
        } else if (d instanceof Constructor) {
            ((Constructor)d).setAbstract(hasAnnotationBit(bits, "abstract"));
        }
        if (hasAnnotationBit(bits, "late")) {
            ((Value)d).setLate(true);
        }
        if (hasAnnotationBit(bits, "variable")) {
            ((Value)d).setVariable(true);
        }
    }

    @SuppressWarnings("unchecked")
    private void setAnnotations(Declaration d, Integer bits, Object anns) {
        if (bits != null) {
            setBitAnnotations(d, bits);
        }
        if (anns == null) return;
        if (anns instanceof List) {
            setNewAnnotations(d.getAnnotations(), (List>>)anns);
        } else if (anns instanceof Map) {
            setOldAnnotations(d.getAnnotations(), (Map>)anns);
        } else {
            throw new IllegalArgumentException("Annotations should be a List (new format) or a Map (old format)");
        }
    }
    static void setNewAnnotations(List existing, List>> anns) {
        for (Map> a : anns) {
            String name = a.keySet().iterator().next();
            Annotation ann = new Annotation();
            ann.setName(name);
            for (String arg : a.get(name)) {
                ann.addPositionalArgument(arg);
            }
            existing.add(ann);
        }
    }

    static void setOldAnnotations(List existing, Map> m) {
        for (Map.Entry> e : m.entrySet()) {
            String name = e.getKey();
            Annotation ann = new Annotation();
            ann.setName(name);
            for (String arg : e.getValue()) {
                ann.addPositionalArgument(arg);
            }
            existing.add(ann);
        }
    }

    /** Load a nested type that hasn't been loaded yet */
    private TypeDeclaration loadNestedType(final String fqn, List typeParams) {
        try{
        String[] path = fqn.split("\\.");
        @SuppressWarnings("unchecked")
        Map typeMap = (Map)model.get(path[0]);
        if (typeMap.get(KEY_METATYPE) instanceof TypeDeclaration == false) {
            load(path[0], typeParams);
        }
        TypeDeclaration td = (TypeDeclaration)typeMap.get(KEY_METATYPE);
        for (int i = 1; i < path.length; i++) {
            @SuppressWarnings("unchecked")
            Map subtypes = (Map)typeMap.get(KEY_INTERFACES);
            Map childMap = null;
            int type = 0;
            if (subtypes != null) {
                childMap = (Map)subtypes.get(path[i]);
                type = 1;
            }
            if (childMap == null) {
                subtypes = (Map)typeMap.get(KEY_CLASSES);
                if (subtypes != null) {
                    childMap = (Map)subtypes.get(path[i]);
                    type = 2;
                }
            }
            Declaration member = td.getDirectMember(path[i], null, false);
            TypeDeclaration child;
            if(member instanceof Value
                    && ((Value) member).getTypeDeclaration() instanceof Constructor)
                child = ((Value) member).getTypeDeclaration().getExtendedType().getDeclaration();
            else
                child = (TypeDeclaration) member;
            
            if (child == null) {
                switch(type) {
                case 1:child = loadInterface(path[i], childMap, td, typeParams);
                break;
                case 2:child = loadClass(path[i], childMap, td, typeParams);
                break;
                }
            }
            td = child;
        }
        return td;
        }catch(RuntimeException x){
            throw new RuntimeException("Failed to load inner type "+fqn+" in package "+getQualifiedNameString(), x);
        }
    }

    /** Create a new list that contains all the type parameters in the first list,
     * and the type parameters from the second list that don't have any names already present in the first list.
     */
    public static List merge(List l1, List l2) {
        int size = (l1 == null ? 0 : l1.size()) + (l2 == null ? 0 : l2.size());
        ArrayList merged = new ArrayList<>(size);
        HashSet names = new HashSet<>();
        if (l1 != null) {
            for (TypeParameter t : l1) {
                merged.add(t);
                names.add(t.getName());
            }
        }
        if (l2 != null) {
            for (TypeParameter t : l2) {
                if (!names.contains(t.getName())) {
                    merged.add(t);
                }
            }
        }
        return merged;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy