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

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

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

import static com.redhat.ceylon.common.Versions.getJvmLanguageModuleVersion;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.intersection;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.union;

import java.io.File;
import java.io.IOException;
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.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;

import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.common.Backends;
import com.redhat.ceylon.common.BooleanUtil;
import com.redhat.ceylon.common.JVMModuleUtil;
import com.redhat.ceylon.common.ModuleSpec;
import com.redhat.ceylon.common.ModuleUtil;
import com.redhat.ceylon.common.Nullable;
import com.redhat.ceylon.common.Versions;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.cmr.RepositoryException;
import com.redhat.ceylon.model.loader.mirror.AccessibleMirror;
import com.redhat.ceylon.model.loader.mirror.AnnotatedMirror;
import com.redhat.ceylon.model.loader.mirror.AnnotationMirror;
import com.redhat.ceylon.model.loader.mirror.ClassMirror;
import com.redhat.ceylon.model.loader.mirror.FieldMirror;
import com.redhat.ceylon.model.loader.mirror.FunctionalInterfaceType;
import com.redhat.ceylon.model.loader.mirror.MethodMirror;
import com.redhat.ceylon.model.loader.mirror.PackageMirror;
import com.redhat.ceylon.model.loader.mirror.TypeKind;
import com.redhat.ceylon.model.loader.mirror.TypeMirror;
import com.redhat.ceylon.model.loader.mirror.TypeParameterMirror;
import com.redhat.ceylon.model.loader.mirror.VariableMirror;
import com.redhat.ceylon.model.loader.model.AnnotationProxyClass;
import com.redhat.ceylon.model.loader.model.AnnotationProxyMethod;
import com.redhat.ceylon.model.loader.model.AnnotationTarget;
import com.redhat.ceylon.model.loader.model.FieldValue;
import com.redhat.ceylon.model.loader.model.JavaBeanValue;
import com.redhat.ceylon.model.loader.model.JavaMethod;
import com.redhat.ceylon.model.loader.model.JavaParameterValue;
import com.redhat.ceylon.model.loader.model.LazyClass;
import com.redhat.ceylon.model.loader.model.LazyClassAlias;
import com.redhat.ceylon.model.loader.model.LazyContainer;
import com.redhat.ceylon.model.loader.model.LazyElement;
import com.redhat.ceylon.model.loader.model.LazyFunction;
import com.redhat.ceylon.model.loader.model.LazyInterface;
import com.redhat.ceylon.model.loader.model.LazyInterfaceAlias;
import com.redhat.ceylon.model.loader.model.LazyModule;
import com.redhat.ceylon.model.loader.model.LazyPackage;
import com.redhat.ceylon.model.loader.model.LazyTypeAlias;
import com.redhat.ceylon.model.loader.model.LazyValue;
import com.redhat.ceylon.model.loader.model.LocalDeclarationContainer;
import com.redhat.ceylon.model.loader.model.OutputElement;
import com.redhat.ceylon.model.loader.model.SetterWithLocalDeclarations;
import com.redhat.ceylon.model.typechecker.model.Annotated;
import com.redhat.ceylon.model.typechecker.model.Annotation;
import com.redhat.ceylon.model.typechecker.model.Class;
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.DeclarationCompleter;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.ModuleImport;
import com.redhat.ceylon.model.typechecker.model.Modules;
import com.redhat.ceylon.model.typechecker.model.Package;
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.TypedDeclaration;
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;

/**
 * Abstract class of a model loader that can load a model from a compiled Java representation,
 * while being agnostic of the reflection API used to load the compiled Java representation.
 *
 * @author Stéphane Épardaud 
 */
public abstract class AbstractModelLoader implements ModelCompleter, ModelLoader, DeclarationCompleter {

    public static final String JAVA_BASE_MODULE_NAME = "java.base";
    public static final String CEYLON_LANGUAGE = "ceylon.language";
    public static final String CEYLON_LANGUAGE_MODEL = "ceylon.language.meta.model";
    public static final String CEYLON_LANGUAGE_MODEL_DECLARATION = "ceylon.language.meta.declaration";
    public static final String CEYLON_LANGUAGE_SERIALIZATION = "ceylon.language.serialization";
    
    private static final String TIMER_MODEL_LOADER_CATEGORY = "model loader";
    
    public static final String CEYLON_CEYLON_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Ceylon";
    protected static final String CEYLON_MODULE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Module";
    protected static final String CEYLON_PACKAGE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Package";
    public static final String CEYLON_IGNORE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Ignore";
    private static final String CEYLON_CLASS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Class";
    private static final String CEYLON_JPA_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Jpa";
    private static final String CEYLON_ENUMERATED_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Enumerated";
    //private static final String CEYLON_CONSTRUCTOR_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Constructor";
    //private static final String CEYLON_PARAMETERLIST_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.ParameterList";
    public static final String CEYLON_NAME_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Name";
    private static final String CEYLON_SEQUENCED_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Sequenced";
    private static final String CEYLON_FUNCTIONAL_PARAMETER_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.FunctionalParameter";
    private static final String CEYLON_DEFAULTED_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Defaulted";
    private static final String CEYLON_SATISFIED_TYPES_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.SatisfiedTypes";
    private static final String CEYLON_CASE_TYPES_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.CaseTypes";
    private static final String CEYLON_TYPE_PARAMETERS = "com.redhat.ceylon.compiler.java.metadata.TypeParameters";
    private static final String CEYLON_TYPE_INFO_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.TypeInfo";
    public static final String CEYLON_ATTRIBUTE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Attribute";
    public static final String CEYLON_SETTER_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Setter";
    public static final String CEYLON_OBJECT_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Object";
    public static final String CEYLON_METHOD_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Method";
    public static final String CEYLON_CONTAINER_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Container";
    public static final String CEYLON_LOCAL_CONTAINER_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.LocalContainer";
    public static final String CEYLON_LOCAL_DECLARATION_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.LocalDeclaration";
    public static final String CEYLON_LOCAL_DECLARATIONS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.LocalDeclarations";
    private static final String CEYLON_MEMBERS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Members";
    private static final String CEYLON_ANNOTATIONS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Annotations";
    public static final String CEYLON_VALUETYPE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.ValueType";
    public static final String CEYLON_ALIAS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Alias";
    public static final String CEYLON_TYPE_ALIAS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.TypeAlias";
    public static final String CEYLON_ANNOTATION_INSTANTIATION_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.AnnotationInstantiation";
    public static final String CEYLON_FINAL_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Final";
    public static final String CEYLON_ANNOTATION_INSTANTIATION_ARGUMENTS_MEMBER = "arguments";
    public static final String CEYLON_ANNOTATION_INSTANTIATION_ANNOTATION_MEMBER = "primary";
    
    public static final String CEYLON_ANNOTATION_INSTANTIATION_TREE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.AnnotationInstantiationTree";
    public static final String CEYLON_STRING_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.StringValue";
    public static final String CEYLON_STRING_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.StringExprs";
    public static final String CEYLON_BOOLEAN_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.BooleanValue";
    public static final String CEYLON_BOOLEAN_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.BooleanExprs";
    public static final String CEYLON_INTEGER_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.IntegerValue";
    public static final String CEYLON_INTEGER_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.IntegerExprs";
    public static final String CEYLON_CHARACTER_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.CharacterValue";
    public static final String CEYLON_CHARACTER_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.CharacterExprs";
    public static final String CEYLON_FLOAT_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.FloatValue";
    public static final String CEYLON_FLOAT_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.FloatExprs";
    public static final String CEYLON_OBJECT_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.ObjectValue";
    public static final String CEYLON_OBJECT_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.ObjectExprs";
    public static final String CEYLON_DECLARATION_VALUE_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.DeclarationValue";
    public static final String CEYLON_DECLARATION_EXPRS_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.DeclarationExprs";
    private static final String CEYLON_TRANSIENT_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Transient";
    private static final String CEYLON_DYNAMIC_ANNOTATION = "com.redhat.ceylon.compiler.java.metadata.Dynamic";
    private static final String JAVA_DEPRECATED_ANNOTATION = "java.lang.Deprecated";
    
    static final String CEYLON_LANGUAGE_ABSTRACT_ANNOTATION = "ceylon.language.AbstractAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_ACTUAL_ANNOTATION = "ceylon.language.ActualAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_ANNOTATION_ANNOTATION = "ceylon.language.AnnotationAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_DEFAULT_ANNOTATION = "ceylon.language.DefaultAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_FORMAL_ANNOTATION = "ceylon.language.FormalAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_SHARED_ANNOTATION = "ceylon.language.SharedAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_LATE_ANNOTATION = "ceylon.language.LateAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_SEALED_ANNOTATION = "ceylon.language.SealedAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_VARIABLE_ANNOTATION = "ceylon.language.VariableAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_FINAL_ANNOTATION = "ceylon.language.FinalAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_NATIVE_ANNOTATION = "ceylon.language.NativeAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_OPTIONAL_ANNOTATION = "ceylon.language.OptionalAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_SERIALIZABLE_ANNOTATION = "ceylon.language.SerializableAnnotation$annotation$";
    
    static final String CEYLON_LANGUAGE_DOC_ANNOTATION = "ceylon.language.DocAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_THROWS_ANNOTATIONS = "ceylon.language.ThrownExceptionAnnotation$annotations$";
    static final String CEYLON_LANGUAGE_THROWS_ANNOTATION = "ceylon.language.ThrownExceptionAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_AUTHORS_ANNOTATION = "ceylon.language.AuthorsAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_SEE_ANNOTATIONS = "ceylon.language.SeeAnnotation$annotations$";
    static final String CEYLON_LANGUAGE_SEE_ANNOTATION = "ceylon.language.SeeAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_DEPRECATED_ANNOTATION = "ceylon.language.DeprecationAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_SUPPRESS_WARNINGS_ANNOTATION = "ceylon.language.SuppressWarningsAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_LICENSE_ANNOTATION = "ceylon.language.LicenseAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_TAGS_ANNOTATION = "ceylon.language.TagsAnnotation$annotation$";
    static final String CEYLON_LANGUAGE_ALIASES_ANNOTATION = "ceylon.language.AliasesAnnotation$annotation$";

    // important that these are with ::
    private static final String CEYLON_LANGUAGE_CALLABLE_TYPE_NAME = "ceylon.language::Callable";
    private static final String CEYLON_LANGUAGE_TUPLE_TYPE_NAME = "ceylon.language::Tuple";
    private static final String CEYLON_LANGUAGE_SEQUENTIAL_TYPE_NAME = "ceylon.language::Sequential";
    private static final String CEYLON_LANGUAGE_SEQUENCE_TYPE_NAME = "ceylon.language::Sequence";
    private static final String CEYLON_LANGUAGE_EMPTY_TYPE_NAME = "ceylon.language::Empty";
    
    private static final TypeMirror OBJECT_TYPE = simpleCeylonObjectType("java.lang.Object");
    private static final TypeMirror CHAR_SEQUENCE_TYPE = simpleCeylonObjectType("java.lang.CharSequence");
    private static final TypeMirror ANNOTATION_TYPE = simpleCeylonObjectType("java.lang.annotation.Annotation");
    private static final TypeMirror CEYLON_OBJECT_TYPE = simpleCeylonObjectType("ceylon.language.Object");
    private static final TypeMirror CEYLON_ANNOTATION_TYPE = simpleCeylonObjectType("ceylon.language.Annotation");
    private static final TypeMirror CEYLON_CONSTRAINED_ANNOTATION_TYPE = simpleCeylonObjectType("ceylon.language.ConstrainedAnnotation");
//    private static final TypeMirror CEYLON_FUNCTION_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.FunctionDeclaration");
    private static final TypeMirror CEYLON_FUNCTION_OR_VALUE_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.FunctionOrValueDeclaration");
    private static final TypeMirror CEYLON_VALUE_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ValueDeclaration");
    private static final TypeMirror CEYLON_PACKAGE_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.Package");
    private static final TypeMirror CEYLON_ALIAS_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.AliasDeclaration");
    private static final TypeMirror CEYLON_CLASS_OR_INTERFACE_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ClassOrInterfaceDeclaration");
    private static final TypeMirror CEYLON_CLASS_WITH_INIT_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ClassWithInitializerDeclaration");
    private static final TypeMirror CEYLON_CLASS_WITH_CTORS_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ClassWithConstructorsDeclaration");
    private static final TypeMirror CEYLON_CONSTRUCTOR_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ConstructorDeclaration");
    private static final TypeMirror CEYLON_CALLABLE_CONSTRUCTOR_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.CallableConstructorDeclaration");
    private static final TypeMirror CEYLON_VALUE_CONSTRUCTOR_DECLARATION_TYPE = simpleCeylonObjectType("ceylon.language.meta.declaration.ValueConstructorDeclaration");
    private static final TypeMirror CEYLON_ANNOTATED_TYPE = simpleCeylonObjectType("ceylon.language.Annotated");
    private static final TypeMirror CEYLON_BASIC_TYPE = simpleCeylonObjectType("ceylon.language.Basic");
    private static final TypeMirror CEYLON_REIFIED_TYPE_TYPE = simpleCeylonObjectType("com.redhat.ceylon.compiler.java.runtime.model.ReifiedType");
    private static final TypeMirror CEYLON_SERIALIZABLE_TYPE = simpleCeylonObjectType("com.redhat.ceylon.compiler.java.runtime.serialization.Serializable");
    private static final TypeMirror CEYLON_TYPE_DESCRIPTOR_TYPE = simpleCeylonObjectType("com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor");
    
    private static final TypeMirror THROWABLE_TYPE = simpleCeylonObjectType("java.lang.Throwable");
//    private static final TypeMirror ERROR_TYPE = simpleCeylonObjectType("java.lang.Error");
    private static final TypeMirror EXCEPTION_TYPE = simpleCeylonObjectType("java.lang.Exception");
    private static final TypeMirror CEYLON_THROWABLE_TYPE = simpleCeylonObjectType("java.lang.Throwable");
    private static final TypeMirror CEYLON_EXCEPTION_TYPE = simpleCeylonObjectType("ceylon.language.Exception");
    
    private static final TypeMirror STRING_TYPE = simpleJDKObjectType("java.lang.String");
    private static final TypeMirror CEYLON_STRING_TYPE = simpleCeylonObjectType("ceylon.language.String");

    private static final TypeMirror CLASS_TYPE = simpleJDKObjectType("java.lang.Class");

    private static final TypeMirror PRIM_BOOLEAN_TYPE = simpleJDKObjectType("boolean");
    private static final TypeMirror CEYLON_BOOLEAN_TYPE = simpleCeylonObjectType("ceylon.language.Boolean");
    
    private static final TypeMirror PRIM_BYTE_TYPE = simpleJDKObjectType("byte");
    private static final TypeMirror CEYLON_BYTE_TYPE = simpleCeylonObjectType("ceylon.language.Byte");
    
    private static final TypeMirror PRIM_SHORT_TYPE = simpleJDKObjectType("short");

    private static final TypeMirror PRIM_INT_TYPE = simpleJDKObjectType("int");
    private static final TypeMirror PRIM_LONG_TYPE = simpleJDKObjectType("long");
    private static final TypeMirror CEYLON_INTEGER_TYPE = simpleCeylonObjectType("ceylon.language.Integer");
    
    private static final TypeMirror PRIM_FLOAT_TYPE = simpleJDKObjectType("float");
    private static final TypeMirror PRIM_DOUBLE_TYPE = simpleJDKObjectType("double");
    private static final TypeMirror CEYLON_FLOAT_TYPE = simpleCeylonObjectType("ceylon.language.Float");

    private static final TypeMirror PRIM_CHAR_TYPE = simpleJDKObjectType("char");
    private static final TypeMirror CEYLON_CHARACTER_TYPE = simpleCeylonObjectType("ceylon.language.Character");
    
    // this one has no "_" postfix because that's how we look it up
    protected static final String JAVA_LANG_BYTE_ARRAY = "java.lang.ByteArray";
    protected static final String JAVA_LANG_SHORT_ARRAY = "java.lang.ShortArray";
    protected static final String JAVA_LANG_INT_ARRAY = "java.lang.IntArray";
    protected static final String JAVA_LANG_LONG_ARRAY = "java.lang.LongArray";
    protected static final String JAVA_LANG_FLOAT_ARRAY = "java.lang.FloatArray";
    protected static final String JAVA_LANG_DOUBLE_ARRAY = "java.lang.DoubleArray";
    protected static final String JAVA_LANG_CHAR_ARRAY = "java.lang.CharArray";
    protected static final String JAVA_LANG_BOOLEAN_ARRAY = "java.lang.BooleanArray";
    protected static final String JAVA_LANG_OBJECT_ARRAY = "java.lang.ObjectArray";
    
    // this one has the "_" postfix because that's what we translate it to
    private static final String CEYLON_BYTE_ARRAY = "com.redhat.ceylon.compiler.java.language.ByteArray";
    private static final String CEYLON_SHORT_ARRAY = "com.redhat.ceylon.compiler.java.language.ShortArray";
    private static final String CEYLON_INT_ARRAY = "com.redhat.ceylon.compiler.java.language.IntArray";
    private static final String CEYLON_LONG_ARRAY = "com.redhat.ceylon.compiler.java.language.LongArray";
    private static final String CEYLON_FLOAT_ARRAY = "com.redhat.ceylon.compiler.java.language.FloatArray";
    private static final String CEYLON_DOUBLE_ARRAY = "com.redhat.ceylon.compiler.java.language.DoubleArray";
    private static final String CEYLON_CHAR_ARRAY = "com.redhat.ceylon.compiler.java.language.CharArray";
    private static final String CEYLON_BOOLEAN_ARRAY = "com.redhat.ceylon.compiler.java.language.BooleanArray";
    private static final String CEYLON_OBJECT_ARRAY = "com.redhat.ceylon.compiler.java.language.ObjectArray";

    private static final TypeMirror JAVA_BYTE_ARRAY_TYPE = simpleJDKObjectType("java.lang.ByteArray");
    private static final TypeMirror JAVA_SHORT_ARRAY_TYPE = simpleJDKObjectType("java.lang.ShortArray");
    private static final TypeMirror JAVA_INT_ARRAY_TYPE = simpleJDKObjectType("java.lang.IntArray");
    private static final TypeMirror JAVA_LONG_ARRAY_TYPE = simpleJDKObjectType("java.lang.LongArray");
    private static final TypeMirror JAVA_FLOAT_ARRAY_TYPE = simpleJDKObjectType("java.lang.FloatArray");
    private static final TypeMirror JAVA_DOUBLE_ARRAY_TYPE = simpleJDKObjectType("java.lang.DoubleArray");
    private static final TypeMirror JAVA_CHAR_ARRAY_TYPE = simpleJDKObjectType("java.lang.CharArray");
    private static final TypeMirror JAVA_BOOLEAN_ARRAY_TYPE = simpleJDKObjectType("java.lang.BooleanArray");
    private static final TypeMirror JAVA_IO_SERIALIZABLE_TYPE_TYPE = simpleJDKObjectType("java.io.Serializable");
    
    protected static final String JAVA_LANG_TRANSIENT_ANNOTATION = "java.lang.transient";
    protected static final String JAVA_LANG_VOLATILE_ANNOTATION = "java.lang.volatile";
    protected static final String JAVA_LANG_SYNCHRONIZED_ANNOTATION = "java.lang.synchronized";
    protected static final String JAVA_LANG_NATIVE_ANNOTATION = "java.lang.native";
    protected static final String JAVA_LANG_STRICTFP_ANNOTATION = "java.lang.strictfp";
    
    private static final String CEYLON_INTEROP_TRANSIENT_ANNOTATION = "com.redhat.ceylon.compiler.java.language.transient";
    private static final String CEYLON_INTEROP_VOLATILE_ANNOTATION = "com.redhat.ceylon.compiler.java.language.volatile";
    private static final String CEYLON_INTEROP_SYNCHRONIZED_ANNOTATION = "com.redhat.ceylon.compiler.java.language.synchronized";
    private static final String CEYLON_INTEROP_NATIVE_ANNOTATION = "com.redhat.ceylon.compiler.java.language.native";
    private static final String CEYLON_INTEROP_STRICTFP_ANNOTATION = "com.redhat.ceylon.compiler.java.language.strictfp";
    
    private static final Set CEYLON_INTEROP_DECLARATIONS = new HashSet();
    static {
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_BYTE_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_SHORT_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INT_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_LONG_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_BOOLEAN_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_FLOAT_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_DOUBLE_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_CHAR_ARRAY);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_OBJECT_ARRAY);
        
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INTEROP_NATIVE_ANNOTATION);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INTEROP_TRANSIENT_ANNOTATION);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INTEROP_VOLATILE_ANNOTATION);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INTEROP_SYNCHRONIZED_ANNOTATION);
        CEYLON_INTEROP_DECLARATIONS.add(CEYLON_INTEROP_STRICTFP_ANNOTATION);
    }
    
    private static TypeMirror simpleJDKObjectType(String name) {
        return new SimpleReflType(name, SimpleReflType.Module.JDK, TypeKind.DECLARED);
    }
    private static TypeMirror simpleCeylonObjectType(String name) {
        return new SimpleReflType(name, SimpleReflType.Module.CEYLON, TypeKind.DECLARED);
    }

    protected Map valueDeclarationsByName = new HashMap();
    protected Map typeDeclarationsByName = new HashMap();
    protected Map unitsByPackage = new HashMap();
    protected TypeParser typeParser;
    /** 
     * The type factory 
     * (should not be used while completing a declaration)
     */
    protected Unit typeFactory;
    protected final Set loadedPackages = new HashSet();
    protected final Map packagesByName = new HashMap();
    protected boolean packageDescriptorsNeedLoading = false;
    protected boolean isBootstrap;
    private ModuleManager moduleManager;
    protected Modules modules;
    protected Map classMirrorCache = new HashMap();
    protected boolean binaryCompatibilityErrorRaised = false;
    protected Timer timer;
    private Map modulelessPackages = new HashMap();
    private ParameterNameParser parameterNameParser = new ParameterNameParser(this);
    protected JdkProvider jdkProvider;
    
    protected final void initModuleManager(ModuleManager moduleManager) {
        this.moduleManager = moduleManager;
    }
    
    protected ModuleManager getModuleManager() {
        return moduleManager;
    }

    /**
     * Loads a given package, if required. This is mostly useful for the javac reflection impl.
     * 
     * @param the module to load the package from
     * @param packageName the package name to load
     * @param loadDeclarations true to load all the declarations in this package.
     * @return 
     */
    public abstract boolean loadPackage(Module module, String packageName, boolean loadDeclarations);

    public final Object getLock(){
        return this;
    }
    
    public class EmbeddedException extends RuntimeException {
    private static final long serialVersionUID = 1L;
        public EmbeddedException(Exception cause) {
            super(cause);
        }
    }
    
    public  T embeddingSync(Callable action) throws Exception {
        return action.call();
    }
    
    final public  T synchronizedCall(final Callable action) {
        try {
            return embeddingSync(new Callable() {
                @Override
                public T call() throws Exception {
                    synchronized(getLock()) {
                        return action.call();
                    }                    
                };
            });
        } catch(RuntimeException e) {
            throw e;
        } catch(Exception e) {
            throw new EmbeddedException(e);
        }
    }

    final public void synchronizedRun(final Runnable action) {
        synchronizedCall(new Callable() {
            @Override
            public Object call() throws Exception {
                action.run();
                return null;
            }
        });
    }

    /**
     * To be redefined by subclasses if they compile a mix of Java + Ceylon files at the same go
     */
    protected boolean hasJavaAndCeylonSources() {
        return false;
    }

    /**
     * To be redefined by subclasses if they don't need local declarations.
     */
    protected boolean needsLocalDeclarations(){
        return true;
    }

    /**
     * For subclassers to skip private members. Defaults to false.
     */
    protected boolean needsPrivateMembers() {
        return true;
    }

    public boolean searchAgain(ClassMirror cachedMirror, Module module, String name) {
        return false;
    }
    
    public boolean searchAgain(Declaration cachedDeclaration, LazyPackage lazyPackage, String name) {
        return false;
    }
    
    /**
     * Looks up a ClassMirror by name. Uses cached results, and caches the result of calling lookupNewClassMirror
     * on cache misses.
     * 
     * @param module the module in which we should find the class
     * @param name the name of the Class to load
     * @return a ClassMirror for the specified class, or null if not found.
     */
    public final ClassMirror lookupClassMirror(final Module theModule, final String theName) {
        return synchronizedCall(new Callable(){
            @Override
            public ClassMirror call() throws Exception {
                Module module = theModule;
                String name = theName;
                timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY);
                try{
                    // Java array classes are not where we expect them
                    if (JAVA_LANG_OBJECT_ARRAY.equals(name)
                            || JAVA_LANG_BOOLEAN_ARRAY.equals(name)
                            || JAVA_LANG_BYTE_ARRAY.equals(name)
                            || JAVA_LANG_SHORT_ARRAY.equals(name)
                            || JAVA_LANG_INT_ARRAY.equals(name)
                            || JAVA_LANG_LONG_ARRAY.equals(name)
                            || JAVA_LANG_FLOAT_ARRAY.equals(name)
                            || JAVA_LANG_DOUBLE_ARRAY.equals(name)
                            || JAVA_LANG_CHAR_ARRAY.equals(name)) {
                        // turn them into their real class location (get rid of the "java.lang" prefix)
                        name = "com.redhat.ceylon.compiler.java.language" + name.substring(9);
                        module = getLanguageModule();
                    }
                    if (JAVA_LANG_TRANSIENT_ANNOTATION.equals(name)
                            || JAVA_LANG_VOLATILE_ANNOTATION.equals(name)
                            || JAVA_LANG_SYNCHRONIZED_ANNOTATION.equals(name)
                            || JAVA_LANG_NATIVE_ANNOTATION.equals(name)
                            || JAVA_LANG_STRICTFP_ANNOTATION.equals(name)) {
                        name = "com.redhat.ceylon.compiler.java.language" + name.substring(9);
                        module = getLanguageModule();
                    }
                    String cacheKey = cacheKeyByModule(module, name);
                    // we use containsKey to be able to cache null results
                    if(classMirrorCache.containsKey(cacheKey)) {
                        ClassMirror cachedMirror = classMirrorCache.get(cacheKey);
                        if (! searchAgain(cachedMirror, module, name)) {
                            return cachedMirror;
                        }
                    }
                    ClassMirror mirror = lookupNewClassMirror(module, name);
                    // we even cache null results
                    classMirrorCache.put(cacheKey, mirror);
                    return mirror;
                }finally{
                    timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY);
                }
            }
        });
    }

    protected String cacheKeyByModule(Module module, String name) {
        return getCacheKeyByModule(module, name);
    }

    public static String getCacheKeyByModule(Module module, String name){
        String moduleSignature = module.getSignature();
        StringBuilder buf = new StringBuilder(moduleSignature.length()+1+name.length());
        // '/' is allowed in module version but not in module or class name, so we're good
        return buf.append(moduleSignature).append('/').append(name).toString();
    }

    protected boolean lastPartHasLowerInitial(String name) {
        int index = name.lastIndexOf('.');
        if (index != -1){
            name = name.substring(index+1);
        }
        // remove any possibly quoting char
        name = NamingBase.stripLeadingDollar(name);
        if(!name.isEmpty()){
            int codepoint = name.codePointAt(0);
            return JvmBackendUtil.isLowerCase(codepoint);
        }
        return false;
    }
    
    /**
     * Looks up a ClassMirror by name. Called by lookupClassMirror on cache misses.
     * 
     * @param module the module in which we should find the given class
     * @param name the name of the Class to load
     * @return a ClassMirror for the specified class, or null if not found.
     */
    protected abstract ClassMirror lookupNewClassMirror(Module module, String name);

    /**
     * Adds the given module to the set of modules from which we can load classes.
     * 
     * @param module the module
     * @param artifact the module's artifact, if any. Can be null. 
     */
    public abstract void addModuleToClassPath(Module module, ArtifactResult artifact);

    /**
     * Returns true if the given module has been added to this model loader's classpath.
     * Defaults to true.
     */
    public boolean isModuleInClassPath(Module module){
        return true;
    }
    
    /**
     * Returns true if the given method is overriding an inherited method (from super class or interfaces).
     */
    protected abstract boolean isOverridingMethod(MethodMirror methodMirror);

    /**
     * Returns true if the given method is overloading an inherited method (from super class or interfaces).
     */
    protected abstract boolean isOverloadingMethod(MethodMirror methodMirror);

    /**
     * Logs a warning.
     */
    protected abstract void logWarning(String message);

    /**
     * Logs a debug message.
     */
    protected abstract void logVerbose(String message);
    
    /**
     * Logs an error
     */
    protected abstract void logError(String message);
    
    public void loadStandardModules(){
        // set up the type factory
        Timer nested = timer.nestedTimer();
        nested.startTask("load ceylon.language");
        Module languageModule = loadLanguageModuleAndPackage();
        
        nested.endTask();
        
        nested.startTask("load JDK");
        // make sure the jdk modules are loaded
        String jdkModuleSpec = getAlternateJdkModuleSpec();
        Module jdkModule = null;
        if(jdkModuleSpec != null){
            ModuleSpec spec = ModuleSpec.parse(jdkModuleSpec);
            jdkModule = findOrCreateModule(spec.getName(), spec.getVersion());
        }
        if(jdkModule == null){
            jdkProvider = new JdkProvider();
            loadJDKModules();
            jdkModule = findOrCreateModule(JAVA_BASE_MODULE_NAME, jdkProvider.getJDKVersion());
        }
        nested.endTask();
        
        /*
         * We start by loading java.lang and ceylon.language because we will need them no matter what.
         */
        nested.startTask("load standard packages");
        loadPackage(jdkModule, "java.lang", false);
        loadPackage(languageModule, "com.redhat.ceylon.compiler.java.metadata", false);
        loadPackage(languageModule, "com.redhat.ceylon.compiler.java.language", false);
        nested.endTask();
    }
    
    protected String getAlternateJdkModuleSpec() {
    	return null;
    }
    
    protected Module loadLanguageModuleAndPackage() {
        Module languageModule = findOrCreateModule(CEYLON_LANGUAGE, null);
        addModuleToClassPath(languageModule, null);
        Package languagePackage = findOrCreatePackage(languageModule, CEYLON_LANGUAGE);
        typeFactory.setPackage(languagePackage);
        
        // make sure the language module has its real dependencies added, because we need them in the classpath
        // otherwise we will get errors on the Util and Metamodel calls we insert
        // WARNING! Make sure this list is always the same as the one in /ceylon-runtime/dist/repo/ceylon/language/_version_/module.xml
        // Note that we lie about the module exports: we pretend they're not imported at compile-time
        // while they are at run-time (in the module.xml file), so that users don't get these imports
        // visible in all their modules.
        languageModule.addImport(new ModuleImport(null, findOrCreateModule("com.redhat.ceylon.common", Versions.CEYLON_VERSION_NUMBER), false, false, Backend.Java));
        languageModule.addImport(new ModuleImport(null, findOrCreateModule("com.redhat.ceylon.model", Versions.CEYLON_VERSION_NUMBER), false, false, Backend.Java));
        
        return languageModule;
    }
    
    protected void loadJDKModules() {
    	String version = jdkProvider.getJDKVersion();
        for(String jdkModule : jdkProvider.getJDKModuleNames())
            findOrCreateModule(jdkModule, version);
    }

    /**
     * This is meant to be called if your subclass doesn't call loadStandardModules for whatever reason
     */
    public void setupWithNoStandardModules() {
        Module languageModule = modules.getLanguageModule();
        if(languageModule == null)
            throw new RuntimeException("Assertion failed: language module is null");
        Package languagePackage = languageModule.getPackage(CEYLON_LANGUAGE);
        if(languagePackage == null)
            throw new RuntimeException("Assertion failed: language package is null");
        typeFactory.setPackage(languagePackage);
    }

    enum ClassType {
        ATTRIBUTE, METHOD, OBJECT, CLASS, INTERFACE;
    }
    
    private ClassMirror loadClass(Module module, String pkgName, String className) {
        ClassMirror moduleClass = null;
        try{
            // no need loading the package since we know the name of the class
            moduleClass = lookupClassMirror(module, className);
        }catch(Exception x){
            logVerbose("[Failed to complete class "+className+"]");
        }
        return moduleClass;
    }

    private Declaration convertNonPrimitiveTypeToDeclaration(Module moduleScope, TypeMirror type, Scope scope, DeclarationType declarationType) {
        switch(type.getKind()){
        case VOID:
            return typeFactory.getAnythingDeclaration();
        case ARRAY:
            return ((Class)convertToDeclaration(getLanguageModule(), JAVA_LANG_OBJECT_ARRAY, DeclarationType.TYPE));
        case DECLARED:
            return convertDeclaredTypeToDeclaration(moduleScope, type, declarationType);
        case TYPEVAR:
            return safeLookupTypeParameter(scope, type.getQualifiedName());
        case WILDCARD:
            return typeFactory.getNothingDeclaration();
        // those can't happen
        case BOOLEAN:
        case BYTE:
        case CHAR:
        case SHORT:
        case INT:
        case LONG:
        case FLOAT:
        case DOUBLE:
            // all the autoboxing should already have been done
            throw new RuntimeException("Expected non-primitive type: "+type);
        case ERROR:
            return null;
        default:
            throw new RuntimeException("Failed to handle type "+type);
        }
    }

    private Declaration convertDeclaredTypeToDeclaration(Module moduleScope, TypeMirror type, DeclarationType declarationType) {
        // SimpleReflType does not do declared class so we make an exception for it
        String typeName = type.getQualifiedName();
        if(type instanceof SimpleReflType){
            Module module = null;
            switch(((SimpleReflType) type).getModule()){
            case CEYLON: module = getLanguageModule(); break;
            case JDK : module = getJDKBaseModule(); break;
            }
            return convertToDeclaration(module, typeName, declarationType);
        }
        ClassMirror classMirror = type.getDeclaredClass();
        Module module = findModuleForClassMirror(classMirror);
        if(isImported(moduleScope, module)){
            return convertToDeclaration(module, typeName, declarationType);
        }else{
            if(module != null && isFlatClasspath() && isMavenModule(moduleScope))
                return convertToDeclaration(module, typeName, declarationType);
            String error = "Declaration '" + typeName + "' could not be found in module '" + moduleScope.getNameAsString() 
                    + "' or its imported modules";
            if(module != null && !module.isDefaultModule())
                error += " but was found in the non-imported module '"+module.getNameAsString()+"'";
            return logModelResolutionException(null, moduleScope, error).getDeclaration();
        }
    }
    
    public Declaration convertToDeclaration(Module module, ClassMirror classMirror, DeclarationType declarationType) {
        return convertToDeclaration(module, null, classMirror, declarationType);
    }
    
    private Declaration convertToDeclaration(Module module, Declaration container, ClassMirror classMirror, DeclarationType declarationType) {
        // find its package
        String pkgName = getPackageNameForQualifiedClassName(classMirror);
        if (pkgName.equals("java.lang")) {
            module = getJDKBaseModule();
        }
        
        Declaration decl = findCachedDeclaration(module, container, classMirror, declarationType);
        if (decl != null) {
            return decl;
        }
        
        // avoid ignored classes
        if(classMirror.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null)
            return null;
        // avoid local interfaces that were pulled to the toplevel if required
        if(classMirror.getAnnotation(CEYLON_LOCAL_CONTAINER_ANNOTATION) != null
                && !needsLocalDeclarations())
            return null;
        // avoid Ceylon annotations
        if(classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null
                && classMirror.isAnnotationType())
            return null;
        // avoid module and package descriptors too
        if(classMirror.getAnnotation(CEYLON_MODULE_ANNOTATION) != null
                || classMirror.getAnnotation(CEYLON_PACKAGE_ANNOTATION) != null)
            return null;
        
        List decls = new ArrayList();
        
        LazyPackage pkg = findOrCreatePackage(module, pkgName);

        decl = createDeclaration(container, classMirror, declarationType, decls);
        cacheDeclaration(module, container, classMirror, declarationType, decl, decls);

        // find/make its Unit
        Unit unit = getCompiledUnit(pkg, classMirror);

        // set all the containers
        for(Declaration d : decls){
        
            // add it to its Unit
            d.setUnit(unit);
            unit.addDeclaration(d);

            setContainer(classMirror, d, pkg);
        }

        return decl;
    }

    public String getPackageNameForQualifiedClassName(String pkg, String qualifiedName){
        // Java array classes we pretend come from java.lang
        for (String name : CEYLON_INTEROP_DECLARATIONS) {
            if(qualifiedName.startsWith(name)) {
                return "java.lang";
            }
        }
        return unquotePackageName(pkg);
    }
    
    protected String getPackageNameForQualifiedClassName(ClassMirror classMirror) {
        return getPackageNameForQualifiedClassName(classMirror.getPackage().getQualifiedName(), classMirror.getQualifiedName());
    }
    
    private String unquotePackageName(PackageMirror pkg) {
        return unquotePackageName(pkg.getQualifiedName());
    }

    private String unquotePackageName(String pkg) {
        return JvmBackendUtil.removeChar('$', pkg);
    }

    private void setContainer(ClassMirror classMirror, Declaration d, LazyPackage pkg) {
        // add it to its package if it's not an inner class
        if(!classMirror.isInnerClass() && !classMirror.isLocalClass()){
            d.setContainer(pkg);
            d.setScope(pkg);
            pkg.addCompiledMember(d);
            if(d instanceof LazyInterface && ((LazyInterface) d).isCeylon()){
                setInterfaceCompanionClass(d, null, pkg);
            }
            ModelUtil.setVisibleScope(d);
        }else if(classMirror.isLocalClass() && !classMirror.isInnerClass()){
            // set its container to the package for now, but don't add it to the package as a member because it's not
            Scope localContainer = getLocalContainer(pkg, classMirror, d);
            if(localContainer != null){
                d.setContainer(localContainer);
                d.setScope(localContainer);
                // do not add it as member, it has already been registered by getLocalContainer
            }else{
                d.setContainer(pkg);
                d.setScope(pkg);
            }
            ((LazyElement)d).setLocal(true);
        }else if(d instanceof ClassOrInterface || d instanceof TypeAlias){
            // do overloads later, since their container is their abstract superclass's container and
            // we have to set that one first
            if(d instanceof Class == false || !((Class)d).isOverloaded()){
                ClassOrInterface container = getContainer(pkg.getModule(), classMirror);
                if (d.isNativeHeader() && container.isNative()) {
                   container = (ClassOrInterface)ModelUtil.getNativeHeader(container);
                }else if (d.isNativeImplementation()
                        // Here we check if it's a native header, not just native, otherwise it would match
                        // for every Java declaration, who don't have native headers
                        && container.isNativeHeader()) {
                    container = (ClassOrInterface)ModelUtil.getNativeDeclaration(container, Backend.Java);
                }

                d.setContainer(container);
                d.setScope(container);
                if(d instanceof LazyInterface && ((LazyInterface) d).isCeylon()){
                    setInterfaceCompanionClass(d, container, pkg);
                }
                // let's not trigger lazy-loading
                ((LazyContainer)container).addMember(d);
                ModelUtil.setVisibleScope(d);
                // now we can do overloads
                if(d instanceof Class && ((Class)d).getOverloads() != null){
                    for(Declaration overload : ((Class)d).getOverloads()){
                        overload.setContainer(container);
                        overload.setScope(container);
                        // let's not trigger lazy-loading
                        ((LazyContainer)container).addMember(overload);
                        ModelUtil.setVisibleScope(overload);
                    }
                }

                // Adds extra members for annotation interop.
                if (d instanceof LazyInterface
                        && !((LazyInterface)d).isCeylon()
                        && ((LazyInterface)d).isAnnotationType()) {
                    for (Declaration decl : makeInteropAnnotation((LazyInterface) d, container)) {
                        container.addMember(decl);
                    }
                }
            }
        }
    }

    /**
     * Creates extra members to be added to the {@code container} for annotation interop.
     * For a Java declaration {@code @interface Annotation} we generate
     * a model corresponding to:
     * 
     *   annotation class Annotation$Proxy(...) satisfies Annotation {
     *       // a `shared` class parameter for each method of Annotation
     *   }
     *   annotation JavaAnnotation javaAnnotation(...) => JavaAnnotation$Proxy(...);
     * 
* * We also make a {@code *__method}, {@code *__field} etc version for each * {@code @Target} program element * @param iface The model of the annotation @interface * @param container The container in which the generated members belong * @return A list of members to add to the container */ public List makeInteropAnnotation(LazyInterface iface, Scope container) { List declarations = new ArrayList<>(); AnnotationProxyClass klass = makeInteropAnnotationClass(iface, container); AnnotationProxyMethod method = makeInteropAnnotationConstructor(iface, klass, null, container); declarations.add(method); for (OutputElement target : AnnotationTarget.outputTargets(klass)) { declarations.add(makeInteropAnnotationConstructor(iface, klass, target, container)); } declarations.add(klass); return declarations; } protected void setInterfaceCompanionClass(Declaration d, ClassOrInterface container, LazyPackage pkg) { // find its companion class in its real container ClassMirror containerMirror = null; if(container instanceof LazyClass){ containerMirror = ((LazyClass) container).classMirror; }else if(container instanceof LazyInterface){ // container must be a LazyInterface, as TypeAlias doesn't contain anything containerMirror = ((LazyInterface)container).companionClass; if(containerMirror == null){ throw new ModelResolutionException("Interface companion class for "+container.getQualifiedNameString()+" not set up"); } } String companionName; if(containerMirror != null) companionName = containerMirror.getFlatName() + "$" + NamingBase.suffixName(NamingBase.Suffix.$impl, d.getName()); else{ // toplevel String p = pkg.getNameAsString(); companionName = ""; if(!p.isEmpty()) companionName = p + "."; companionName += NamingBase.suffixName(NamingBase.Suffix.$impl, d.getName()); } ClassMirror companionClass = lookupClassMirror(pkg.getModule(), companionName); if(companionClass == null){ ((Interface)d).setCompanionClassNeeded(false); } ((LazyInterface)d).companionClass = companionClass; } private Scope getLocalContainer(Package pkg, ClassMirror classMirror, Declaration declaration) { AnnotationMirror localContainerAnnotation = classMirror.getAnnotation(CEYLON_LOCAL_CONTAINER_ANNOTATION); String qualifier = getAnnotationStringValue(classMirror, CEYLON_LOCAL_DECLARATION_ANNOTATION, "qualifier"); // deal with types local to functions in the body of toplevel non-lazy attributes, whose container is ultimately the package Boolean isPackageLocal = getAnnotationBooleanValue(classMirror, CEYLON_LOCAL_DECLARATION_ANNOTATION, "isPackageLocal"); if(BooleanUtil.isTrue(isPackageLocal)){ // make sure it still knows it's a local declaration.setQualifier(qualifier); return null; } LocalDeclarationContainer methodDecl = null; // we get a @LocalContainer annotation for local interfaces if(localContainerAnnotation != null){ methodDecl = (LocalDeclarationContainer) findLocalContainerFromAnnotationAndSetCompanionClass(pkg, (Interface) declaration, localContainerAnnotation); }else{ // all the other cases stay where they belong MethodMirror method = classMirror.getEnclosingMethod(); if(method == null) return null; // see where that method belongs ClassMirror enclosingClass = method.getEnclosingClass(); while(enclosingClass.isAnonymous()){ // this gives us the method in which the anonymous class is, which should be the one we're looking for method = enclosingClass.getEnclosingMethod(); if(method == null) return null; // and the method's containing class enclosingClass = method.getEnclosingClass(); } // if we are in a setter class, the attribute is declared in the getter class, so look for its declaration there TypeMirror getterClass = (TypeMirror) getAnnotationValue(enclosingClass, CEYLON_SETTER_ANNOTATION, "getterClass"); boolean isSetter = false; // we use void.class as default value if(getterClass != null && !getterClass.isPrimitive()){ enclosingClass = getterClass.getDeclaredClass(); isSetter = true; } String javaClassName = enclosingClass.getQualifiedName(); // make sure we don't go looking in companion classes if(javaClassName.endsWith(NamingBase.Suffix.$impl.name())) javaClassName = javaClassName.substring(0, javaClassName.length() - 5); // find the enclosing declaration Declaration enclosingClassDeclaration = convertToDeclaration(pkg.getModule(), javaClassName, DeclarationType.TYPE); if(enclosingClassDeclaration instanceof ClassOrInterface){ ClassOrInterface containerDecl = (ClassOrInterface) enclosingClassDeclaration; // now find the method's declaration // FIXME: find the proper overload if any String name = method.getName(); if(method.isConstructor() || name.startsWith(NamingBase.Prefix.$default$.toString())){ methodDecl = (LocalDeclarationContainer) containerDecl; }else{ // this is only for error messages String type; // lots of special cases if(isStringAttribute(method)){ name = "string"; type = "attribute"; }else if(isHashAttribute(method)){ name = "hash"; type = "attribute"; }else if(isGetter(method)) { // simple attribute name = getJavaAttributeName(method); type = "attribute"; }else if(isSetter(method)) { // simple attribute name = getJavaAttributeName(method); type = "attribute setter"; isSetter = true; }else{ type = "method"; } // strip any escaping or private suffix // it can be foo$priv$canonical so get rid of that one first if (name.endsWith(NamingBase.Suffix.$canonical$.toString())) { name = name.substring(0, name.length()-11); } name = JvmBackendUtil.strip(name, true, method.isPublic() || method.isProtected() || method.isDefaultAccess()); if(name.indexOf('$') > 0){ // may be a default parameter expression? get the method name which is first name = name.substring(0, name.indexOf('$')); } methodDecl = (LocalDeclarationContainer) containerDecl.getDirectMember(name, null, false); if(methodDecl == null) throw new ModelResolutionException("Failed to load outer "+type+" " + name + " for local type " + classMirror.getQualifiedName().toString()); // if it's a setter we wanted, let's get it if(isSetter){ LocalDeclarationContainer setter = (LocalDeclarationContainer) ((Value)methodDecl).getSetter(); if(setter == null) throw new ModelResolutionException("Failed to load outer "+type+" " + name + " for local type " + classMirror.getQualifiedName().toString()); methodDecl = setter; } } }else if(enclosingClassDeclaration instanceof LazyFunction){ // local and toplevel methods methodDecl = (LazyFunction)enclosingClassDeclaration; }else if(enclosingClassDeclaration instanceof LazyValue){ // local and toplevel attributes if(enclosingClassDeclaration.isToplevel() && method.getName().equals(NamingBase.Unfix.set_.name())) isSetter = true; if(isSetter){ LocalDeclarationContainer setter = (LocalDeclarationContainer) ((LazyValue)enclosingClassDeclaration).getSetter(); if(setter == null) throw new ModelResolutionException("Failed to toplevel attribute setter " + enclosingClassDeclaration.getName() + " for local type " + classMirror.getQualifiedName().toString()); methodDecl = setter; }else methodDecl = (LazyValue)enclosingClassDeclaration; }else{ throw new ModelResolutionException("Unknown container type " + enclosingClassDeclaration + " for local type " + classMirror.getQualifiedName().toString()); } } // we have the method, now find the proper local qualifier if any if(qualifier == null) return null; declaration.setQualifier(qualifier); methodDecl.addLocalDeclaration(declaration); return methodDecl; } private Scope findLocalContainerFromAnnotationAndSetCompanionClass(Package pkg, Interface declaration, AnnotationMirror localContainerAnnotation) { @SuppressWarnings("unchecked") List path = (List) localContainerAnnotation.getValue("path"); // we start at the package Scope scope = pkg; for(String name : path){ scope = (Scope) getDirectMember(scope, name); } String companionClassName = (String) localContainerAnnotation.getValue("companionClassName"); if(companionClassName == null || companionClassName.isEmpty()){ declaration.setCompanionClassNeeded(false); return scope; } ClassMirror container; Scope javaClassScope; if(scope instanceof TypedDeclaration && ((TypedDeclaration) scope).isMember()) javaClassScope = scope.getContainer(); else javaClassScope = scope; if(javaClassScope instanceof LazyInterface){ container = ((LazyInterface)javaClassScope).companionClass; }else if(javaClassScope instanceof LazyClass){ container = ((LazyClass) javaClassScope).classMirror; }else if(javaClassScope instanceof LazyValue){ container = ((LazyValue) javaClassScope).classMirror; }else if(javaClassScope instanceof LazyFunction){ container = ((LazyFunction) javaClassScope).classMirror; }else if(javaClassScope instanceof SetterWithLocalDeclarations){ container = ((SetterWithLocalDeclarations) javaClassScope).classMirror; }else{ throw new ModelResolutionException("Unknown scope class: "+javaClassScope); } String qualifiedCompanionClassName = container.getQualifiedName() + "$" + companionClassName; ClassMirror companionClassMirror = lookupClassMirror(pkg.getModule(), qualifiedCompanionClassName); if(companionClassMirror == null) throw new ModelResolutionException("Could not find companion class mirror: "+qualifiedCompanionClassName); ((LazyInterface)declaration).companionClass = companionClassMirror; return scope; } /** * Looks for a direct member of type ClassOrInterface. We're not using Class.getDirectMember() * because it skips object types and we want them. */ public static Declaration getDirectMember(Scope container, String name) { if(name.isEmpty()) return null; boolean wantsSetter = false; if(name.startsWith(NamingBase.Suffix.$setter$.name())){ wantsSetter = true; name = name.substring(8); } if(Character.isDigit(name.charAt(0))){ // this is a local type we have a different accessor for it return ((LocalDeclarationContainer)container).getLocalDeclaration(name); } // don't even try using getDirectMember except on Package, // because it will fail miserably during completion, since we // will for instance have only anonymous types first, before we load their anonymous values, and // when we go looking for them we won't be able to find them until we add their anonymous values, // which is too late if(container instanceof Package){ // don't use Package.getMembers() as it loads the whole package Declaration result = container.getDirectMember(name, null, false); return selectTypeOrSetter(result, wantsSetter); }else{ // must be a Declaration for(Declaration member : container.getMembers()){ // avoid constructors with no name if(member.getName() == null) continue; if(!member.getName().equals(name)) continue; Declaration result = selectTypeOrSetter(member, wantsSetter); if(result != null) return result; } } // not found return null; } private static Declaration selectTypeOrSetter(Declaration member, boolean wantsSetter) { // if we found a type or a method/value we're good to go if (member instanceof ClassOrInterface || member instanceof Constructor || member instanceof Function) { return member; } // if it's a Value return its object type by preference, the member otherwise if (member instanceof Value){ TypeDeclaration typeDeclaration = ((Value) member).getTypeDeclaration(); if(typeDeclaration instanceof Class && typeDeclaration.isAnonymous()) return typeDeclaration; // did we want the setter? if(wantsSetter) return ((Value)member).getSetter(); // must be a non-object value return member; } return null; } private ClassOrInterface getContainer(Module module, ClassMirror classMirror) { AnnotationMirror containerAnnotation = classMirror.getAnnotation(CEYLON_CONTAINER_ANNOTATION); if(containerAnnotation != null){ TypeMirror javaClassMirror = (TypeMirror)containerAnnotation.getValue("klass"); String javaClassName = javaClassMirror.getQualifiedName(); ClassOrInterface containerDecl = (ClassOrInterface) convertToDeclaration(module, javaClassName, DeclarationType.TYPE); if(containerDecl == null) throw new ModelResolutionException("Failed to load outer type " + javaClassName + " for inner type " + classMirror.getQualifiedName().toString()); return containerDecl; }else{ return (ClassOrInterface) convertToDeclaration(module, classMirror.getEnclosingClass(), DeclarationType.TYPE); } } private Declaration findCachedDeclaration(Module module, Declaration container, ClassMirror classMirror, DeclarationType declarationType) { ClassType type = getClassType(classMirror); String key = classMirror.getCacheKey(module); boolean isNativeHeaderMember = container != null && container.isNativeHeader(); if (isNativeHeaderMember) { key = key + "$header"; } // see if we already have it Map declarationCache = getCacheByType(type, declarationType); return declarationCache.get(key); } private void cacheDeclaration(Module module, Declaration container, ClassMirror classMirror, DeclarationType declarationType, Declaration decl, List decls) { ClassType type = getClassType(classMirror); String key = classMirror.getCacheKey(module); boolean isNativeHeaderMember = container != null && container.isNativeHeader(); if (isNativeHeaderMember) { key = key + "$header"; } if(type == ClassType.OBJECT){ typeDeclarationsByName.put(key, getByType(decls, Class.class)); valueDeclarationsByName.put(key, getByType(decls, Value.class)); }else { Map declarationCache = getCacheByType(type, declarationType); declarationCache.put(key, decl); } } private Map getCacheByType(ClassType type, DeclarationType declarationType) { Map declarationCache = null; switch(type){ case OBJECT: if(declarationType == DeclarationType.TYPE){ declarationCache = typeDeclarationsByName; break; } // else fall-through to value case ATTRIBUTE: case METHOD: declarationCache = valueDeclarationsByName; break; case CLASS: case INTERFACE: declarationCache = typeDeclarationsByName; } return declarationCache; } private Declaration getByType(List decls, java.lang.Class klass) { for (Declaration decl : decls) { if (klass.isAssignableFrom(decl.getClass())) { return decl; } } return null; } private Declaration createDeclaration(Declaration container, ClassMirror classMirror, DeclarationType declarationType, List decls) { Declaration decl = null; Declaration hdr = null; checkBinaryCompatibility(classMirror); ClassType type = getClassType(classMirror); boolean isCeylon = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null; boolean isNativeHeaderMember = container != null && container.isNativeHeader(); try{ // make it switch(type){ case ATTRIBUTE: decl = makeToplevelAttribute(classMirror, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, true); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeToplevelAttribute(classMirror, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } break; case METHOD: decl = makeToplevelMethod(classMirror, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, true); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeToplevelMethod(classMirror, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } break; case OBJECT: // we first make a class Declaration objectClassDecl = makeLazyClass(classMirror, null, null, isNativeHeaderMember); setNonLazyDeclarationProperties(objectClassDecl, classMirror, classMirror, classMirror, true); if (isCeylon && shouldCreateNativeHeader(objectClassDecl, container)) { Declaration hdrobj = makeLazyClass(classMirror, null, null, true); setNonLazyDeclarationProperties(hdrobj, classMirror, classMirror, classMirror, true); decls.add(initNativeHeader(hdrobj, objectClassDecl)); } decls.add(objectClassDecl); // then we make a value for it, if it's not an inline object expr if(objectClassDecl.isNamed()){ Declaration objectDecl = makeToplevelAttribute(classMirror, isNativeHeaderMember); setNonLazyDeclarationProperties(objectDecl, classMirror, classMirror, classMirror, true); if (isCeylon && shouldCreateNativeHeader(objectDecl, container)) { Declaration hdrobj = makeToplevelAttribute(classMirror, true); setNonLazyDeclarationProperties(hdrobj, classMirror, classMirror, classMirror, true); decls.add(initNativeHeader(hdrobj, objectDecl)); } decls.add(objectDecl); // which one did we want? decl = declarationType == DeclarationType.TYPE ? objectClassDecl : objectDecl; }else{ decl = objectClassDecl; } break; case CLASS: if(classMirror.getAnnotation(CEYLON_ALIAS_ANNOTATION) != null){ decl = makeClassAlias(classMirror); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, true); }else if(classMirror.getAnnotation(CEYLON_TYPE_ALIAS_ANNOTATION) != null){ decl = makeTypeAlias(classMirror); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, true); }else{ final List constructors = getClassConstructors(classMirror, classMirror, constructorOnly); if (!constructors.isEmpty()) { Boolean hasConstructors = hasConstructors(classMirror); if (constructors.size() > 1) { if (hasConstructors == null || !hasConstructors) { decl = makeOverloadedConstructor(constructors, classMirror, decls, isCeylon); } else { decl = makeLazyClass(classMirror, null, null, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyClass(classMirror, null, null, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } } } else { if (hasConstructors == null || !hasConstructors) { // single constructor MethodMirror constructor = constructors.get(0); // if the class and constructor have different visibility, we pretend there's an overload of one // if it's a ceylon class we don't care that they don't match sometimes, like for inner classes // where the constructor is protected because we want to use an accessor, in this case the class // visibility is to be used // Same for coercion if(isCeylon || (getJavaVisibility(classMirror) == getJavaVisibility(constructor) && !isCoercedMethod(constructor))){ decl = makeLazyClass(classMirror, null, constructor, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyClass(classMirror, null, constructor, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } }else{ decl = makeOverloadedConstructor(constructors, classMirror, decls, isCeylon); } } else { decl = makeLazyClass(classMirror, null, null, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyClass(classMirror, null, null, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } } } } else if(isCeylon && classMirror.getAnnotation(CEYLON_OBJECT_ANNOTATION) != null) { // objects don't need overloading stuff decl = makeLazyClass(classMirror, null, null, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyClass(classMirror, null, null, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } } else { // no visible constructors decl = makeLazyClass(classMirror, null, null, isNativeHeaderMember); setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyClass(classMirror, null, null, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } } if (!isCeylon) { setSealedFromConstructorMods(decl, constructors); } } break; case INTERFACE: boolean isAlias = classMirror.getAnnotation(CEYLON_ALIAS_ANNOTATION) != null; if(isAlias){ decl = makeInterfaceAlias(classMirror); }else{ decl = makeLazyInterface(classMirror, isNativeHeaderMember); } setNonLazyDeclarationProperties(decl, classMirror, classMirror, classMirror, isCeylon); if (!isAlias && isCeylon && shouldCreateNativeHeader(decl, container)) { hdr = makeLazyInterface(classMirror, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); } break; } }catch(ModelResolutionException x){ // FIXME: this may not be the best thing to do, perhaps we should have an erroneous Class,Interface,Function // etc, like javac's model does? decl = logModelResolutionException(x, null, "Failed to load declaration "+classMirror).getDeclaration(); } // objects have special handling above if (type != ClassType.OBJECT){ if (hdr != null) { decls.add(initNativeHeader(hdr, decl)); } decls.add(decl); } return hdr != null ? hdr : decl; } private ClassType getClassType(ClassMirror classMirror) { ClassType type; if(classMirror.isCeylonToplevelAttribute()){ type = ClassType.ATTRIBUTE; }else if(classMirror.isCeylonToplevelMethod()){ type = ClassType.METHOD; }else if(classMirror.isCeylonToplevelObject()){ type = ClassType.OBJECT; }else if(classMirror.isInterface()){ type = ClassType.INTERFACE; }else{ type = ClassType.CLASS; } return type; } private boolean shouldCreateNativeHeader(Declaration decl, Declaration container) { // TODO instead of checking for "shared" we should add an annotation // to all declarations that have a native header and check that here if (decl.isNativeImplementation() && decl.isShared()) { if (container != null) { if (!decl.isOverloaded()) { return !container.isNative(); } } else { return true; } } return false; } private boolean shouldLinkNatives(Declaration decl) { // TODO instead of checking for "shared" we should add an annotation // to all declarations that have a native header and check that here if (decl.isNative() && decl.isShared()) { Declaration container = (Declaration)decl.getContainer(); return container.isNative(); } return false; } private Declaration initNativeHeader(Declaration hdr, Declaration impl) { List al = getOverloads(hdr); if (al == null) { al = new ArrayList(1); } al.add(impl); setOverloads(hdr, al); return hdr; } private void initNativeHeaderMember(Declaration dec) { if (dec.isNativeImplementation()) { Declaration hdr = ModelUtil.getNativeHeader(dec); if (hdr != null) { initNativeHeader(hdr, dec); } } } /** Returns: *
    *
  • true if the class has named constructors ({@code @Class(...constructors=true)}).
  • *
  • false if the class has an initializer constructor.
  • *
  • null if the class lacks {@code @Class} (i.e. is not a Ceylon class).
  • *
* @param classMirror * @return */ private Boolean hasConstructors(ClassMirror classMirror) { AnnotationMirror a = classMirror.getAnnotation(CEYLON_CLASS_ANNOTATION); Boolean hasConstructors; if (a != null) { hasConstructors = (Boolean)a.getValue("constructors"); if (hasConstructors == null) { hasConstructors = false; } } else { hasConstructors = null; } return hasConstructors; } private boolean isDefaultNamedCtor(ClassMirror classMirror, MethodMirror ctor) { return classMirror.getName().equals(getCtorName(ctor)); } private String getCtorName(MethodMirror ctor) { AnnotationMirror nameAnno = ctor.getAnnotation(CEYLON_NAME_ANNOTATION); if (nameAnno != null) { return (String)nameAnno.getValue(); } else { return null; } } private void setSealedFromConstructorMods(Declaration decl, final List constructors) { boolean effectivelySealed = true; for (MethodMirror ctor : constructors) { if (ctor.isPublic() || ctor.isProtected()) { effectivelySealed = false; break; } } if (effectivelySealed && decl instanceof Class) { Class type = (Class)decl; type.setSealed(effectivelySealed); if (type.getOverloads() != null) { for (Declaration oload : type.getOverloads()) { ((Class)oload).setSealed(effectivelySealed); } } } } private Declaration makeOverloadedConstructor(List constructors, ClassMirror classMirror, List decls, boolean isCeylon) { // If the class has multiple constructors we make a copy of the class // for each one (each with it's own single constructor) and make them // a subclass of the original Class supercls = makeLazyClass(classMirror, null, null, false); // the abstraction class gets the class modifiers setNonLazyDeclarationProperties(supercls, classMirror, classMirror, classMirror, isCeylon); supercls.setAbstraction(true); List overloads = new ArrayList(constructors.size()); // all filtering is done in getClassConstructors for (MethodMirror constructor : constructors) { LazyClass subdecl = makeLazyClass(classMirror, supercls, constructor, false); // the subclasses class get the constructor modifiers setNonLazyDeclarationProperties(subdecl, constructor, constructor, classMirror, isCeylon); subdecl.setOverloaded(true); overloads.add(subdecl); decls.add(subdecl); if(!isCeylon && isCoercedMethod(constructor)){ LazyClass subdecl2 = makeLazyClass(classMirror, supercls, constructor, false); subdecl2.setCoercionPoint(true); subdecl2.setRealClass(subdecl); // the subclasses class get the constructor modifiers setNonLazyDeclarationProperties(subdecl2, constructor, constructor, classMirror, isCeylon); subdecl2.setOverloaded(true); overloads.add(subdecl2); decls.add(subdecl2); } } supercls.setOverloads(overloads); return supercls; } private void setNonLazyDeclarationProperties(Declaration decl, AccessibleMirror mirror, AnnotatedMirror annotatedMirror, ClassMirror classMirror, boolean isCeylon) { if(isCeylon){ // when we're in a local type somewhere we must turn public declarations into package or protected ones, so // we have to check the shared annotation decl.setShared(mirror.isPublic() || annotatedMirror.getAnnotation(CEYLON_LANGUAGE_SHARED_ANNOTATION) != null); setDeclarationAliases(decl, annotatedMirror); }else{ decl.setShared(mirror.isPublic() || (mirror.isDefaultAccess() && classMirror.isInnerClass()) || mirror.isProtected()); decl.setPackageVisibility(mirror.isDefaultAccess()); decl.setProtectedVisibility(mirror.isProtected()); } decl.setDeprecated(isDeprecated(annotatedMirror)); } private enum JavaVisibility { PRIVATE, PACKAGE, PROTECTED, PUBLIC; } private JavaVisibility getJavaVisibility(AccessibleMirror mirror) { if(mirror.isPublic()) return JavaVisibility.PUBLIC; if(mirror.isProtected()) return JavaVisibility.PROTECTED; if(mirror.isDefaultAccess()) return JavaVisibility.PACKAGE; return JavaVisibility.PRIVATE; } protected Declaration makeClassAlias(ClassMirror classMirror) { LazyClassAlias decl = new LazyClassAlias(classMirror, this); decl.setStatic(classMirror.isStatic()); return decl; } protected Declaration makeTypeAlias(ClassMirror classMirror) { LazyTypeAlias decl = new LazyTypeAlias(classMirror, this); decl.setStatic(classMirror.isStatic()); return decl; } protected Declaration makeInterfaceAlias(ClassMirror classMirror) { LazyInterfaceAlias decl = new LazyInterfaceAlias(classMirror, this); decl.setStatic(classMirror.isStatic()); return decl; } private void checkBinaryCompatibility(ClassMirror classMirror) { // let's not report it twice if(binaryCompatibilityErrorRaised) return; AnnotationMirror annotation = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION); if(annotation == null) return; // Java class, no check Integer major = (Integer) annotation.getValue("major"); if(major == null) major = 0; Integer minor = (Integer) annotation.getValue("minor"); if(minor == null) minor = 0; if(!Versions.isJvmBinaryVersionSupported(major.intValue(), minor.intValue())){ logError("Ceylon class " + classMirror.getQualifiedName() + " was compiled by an incompatible version of the Ceylon compiler" +"\nThe class was compiled using "+major+"."+minor+"." +"\nThis compiler supports "+Versions.JVM_BINARY_MAJOR_VERSION+"."+Versions.JVM_BINARY_MINOR_VERSION+"." +"\nPlease try to recompile your module using a compatible compiler." +"\nBinary compatibility will only be supported after Ceylon 1.2."); binaryCompatibilityErrorRaised = true; } } interface MethodMirrorFilter { boolean accept(MethodMirror methodMirror); } MethodMirrorFilter constructorOnly = new MethodMirrorFilter() { @Override public boolean accept(MethodMirror methodMirror) { return methodMirror.isConstructor(); } }; class ValueConstructorGetter implements MethodMirrorFilter{ private ClassMirror classMirror; ValueConstructorGetter(ClassMirror classMirror) { this.classMirror = classMirror; } @Override public boolean accept(MethodMirror methodMirror) { return (!methodMirror.isConstructor() && methodMirror.getAnnotation(CEYLON_ENUMERATED_ANNOTATION) != null && methodMirror.getReturnType().getDeclaredClass().toString().equals(classMirror.toString())); } }; private void setHasJpaConstructor(LazyClass c, ClassMirror classMirror) { for(MethodMirror methodMirror : classMirror.getDirectMethods()){ if (methodMirror.isConstructor() && methodMirror.getAnnotation(CEYLON_JPA_ANNOTATION) != null) { c.setHasJpaConstructor(true); break; } } } private List getClassConstructors(ClassMirror instantiatedType, ClassMirror methodContainer, MethodMirrorFilter p) { LinkedList constructors = new LinkedList(); boolean isFromJDK = isFromJDK(methodContainer); for(MethodMirror methodMirror : methodContainer.getDirectMethods()){ // We skip members marked with @Ignore, unless they value constructor getters if(methodMirror.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null &&methodMirror.getAnnotation(CEYLON_ENUMERATED_ANNOTATION) == null) continue; if (!p.accept(methodMirror)) continue; // FIXME: tmp hack to skip constructors that have type params as we don't handle them yet if(!methodMirror.getTypeParameters().isEmpty()) continue; // FIXME: temporary, because some private classes from the jdk are // referenced in private methods but not available if(isFromJDK && !methodMirror.isPublic() // allow protected because we can subclass them, but not package-private because we can't define // classes in the jdk packages && !methodMirror.isProtected()) continue; // if we are expecting Ceylon code, check that we have enough reified type parameters if(methodContainer.getAnnotation(AbstractModelLoader.CEYLON_CEYLON_ANNOTATION) != null){ List tpAnnotations = getTypeParametersFromAnnotations(instantiatedType); int tpCount = tpAnnotations != null ? tpAnnotations.size() : instantiatedType.getTypeParameters().size(); if(!checkReifiedTypeDescriptors(tpCount, instantiatedType.getQualifiedName(), methodMirror, true)) continue; } constructors.add(methodMirror); } return constructors; } private boolean checkReifiedTypeDescriptors(int tpCount, String containerName, MethodMirror methodMirror, boolean isConstructor) { List params = methodMirror.getParameters(); int actualTypeDescriptorParameters = 0; for(VariableMirror param : params){ if(param.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null && sameType(CEYLON_TYPE_DESCRIPTOR_TYPE, param.getType())){ actualTypeDescriptorParameters++; }else break; } if(tpCount != actualTypeDescriptorParameters){ if(isConstructor) logError("Constructor for '"+containerName+"' should take "+tpCount +" reified type arguments (TypeDescriptor) but has '"+actualTypeDescriptorParameters+"': skipping constructor."); else logError("Function '"+containerName+"."+methodMirror.getName()+"' should take "+tpCount +" reified type arguments (TypeDescriptor) but has '"+actualTypeDescriptorParameters+"': method is invalid."); return false; } return true; } protected Unit getCompiledUnit(LazyPackage pkg, ClassMirror classMirror) { String key = getPackageCacheKey(pkg); Unit unit = unitsByPackage.get(key); if(unit == null){ unit = new Unit(); unit.setPackage(pkg); unitsByPackage.put(key, unit); } return unit; } /** * Package.equals() just uses the package name, so collisions between * the same package in different versions of a module are possible, thus * we can't use the package itself a the cache key. */ protected String getPackageCacheKey(LazyPackage pkg) { String key = pkg.getQualifiedNameString()+"/"+pkg.getModule().getVersion(); return key; } protected LazyValue makeToplevelAttribute(ClassMirror classMirror, boolean isNativeHeader) { LazyValue value = new LazyValue(classMirror, this); AnnotationMirror objectAnnotation = classMirror.getAnnotation(CEYLON_OBJECT_ANNOTATION); if(objectAnnotation == null) { manageNativeBackend(value, classMirror, isNativeHeader); } else { manageNativeBackend(value, getGetterMethodMirror(value, value.classMirror, true), isNativeHeader); } return value; } protected LazyFunction makeToplevelMethod(ClassMirror classMirror, boolean isNativeHeader) { LazyFunction method = new LazyFunction(classMirror, this); manageNativeBackend(method, getFunctionMethodMirror(method), isNativeHeader); return method; } protected LazyClass makeLazyClass(ClassMirror classMirror, Class superClass, MethodMirror initOrDefaultConstructor, boolean isNativeHeader) { LazyClass klass = new LazyClass(classMirror, this, superClass, initOrDefaultConstructor); AnnotationMirror objectAnnotation = classMirror.getAnnotation(CEYLON_OBJECT_ANNOTATION); if(objectAnnotation != null){ klass.setAnonymous(true); // isFalse will only consider non-null arguments, and we default to true if null if(BooleanUtil.isFalse((Boolean) objectAnnotation.getValue("named"))) klass.setNamed(false); } klass.setAnnotation(classMirror.getAnnotation(CEYLON_LANGUAGE_ANNOTATION_ANNOTATION) != null); if(klass.isCeylon()) klass.setAbstract(classMirror.getAnnotation(CEYLON_LANGUAGE_ABSTRACT_ANNOTATION) != null // for toplevel classes if the annotation is missing we respect the java abstract modifier // for member classes that would be ambiguous between formal and abstract so we don't and require // the model annotation || (!classMirror.isInnerClass() && !classMirror.isLocalClass() && classMirror.isAbstract())); else klass.setAbstract(classMirror.isAbstract()); klass.setFormal(classMirror.getAnnotation(CEYLON_LANGUAGE_FORMAL_ANNOTATION) != null); klass.setDefault(classMirror.getAnnotation(CEYLON_LANGUAGE_DEFAULT_ANNOTATION) != null); klass.setSerializable(classMirror.getAnnotation(CEYLON_LANGUAGE_SERIALIZABLE_ANNOTATION) != null || classMirror.getQualifiedName().equals("ceylon.language.Array") || classMirror.getQualifiedName().equals("ceylon.language.Tuple")); // hack to make Throwable sealed until ceylon/ceylon.language#408 is fixed klass.setSealed(classMirror.getAnnotation(CEYLON_LANGUAGE_SEALED_ANNOTATION) != null || CEYLON_LANGUAGE.equals(classMirror.getPackage().getQualifiedName()) && "Throwable".equals(classMirror.getName())); boolean actual = classMirror.getAnnotation(CEYLON_LANGUAGE_ACTUAL_ANNOTATION) != null; klass.setActual(actual); klass.setActualCompleter(this); klass.setFinal(classMirror.isFinal()); klass.setStatic(classMirror.isStatic()); if(objectAnnotation == null) { manageNativeBackend(klass, classMirror, isNativeHeader); } else { manageNativeBackend(klass, getGetterMethodMirror(klass, klass.classMirror, true), isNativeHeader); } return klass; } protected LazyInterface makeLazyInterface(ClassMirror classMirror, boolean isNativeHeader) { LazyInterface iface = new LazyInterface(classMirror, this); iface.setSealed(classMirror.getAnnotation(CEYLON_LANGUAGE_SEALED_ANNOTATION) != null); iface.setDynamic(classMirror.getAnnotation(CEYLON_DYNAMIC_ANNOTATION) != null); if (iface.isCeylon()) { AnnotationMirror container = classMirror.getAnnotation(CEYLON_CONTAINER_ANNOTATION); if (container!=null) { Object value = container.getValue("isStatic"); if (value!=null) { iface.setStatic(Boolean.TRUE.equals(value)); } } } else { iface.setStatic(classMirror.getEnclosingClass() != null); } manageNativeBackend(iface, classMirror, isNativeHeader); return iface; } @Nullable public Declaration convertToDeclaration(Module module, String typeName, DeclarationType declarationType) { return convertToDeclaration(module, null, typeName, declarationType); } @Nullable private Declaration convertToDeclaration(final Module theModule, final Declaration theContainer, final String theTypeName, final DeclarationType theDeclarationType) { return synchronizedCall(new Callable() { @Override public Declaration call() throws Exception { Module module = theModule; Declaration container = theContainer; String typeName = theTypeName; DeclarationType declarationType = theDeclarationType; // FIXME: this needs to move to the type parser and report warnings //This should be done where the TypeInfo annotation is parsed //to avoid retarded errors because of a space after a comma typeName = typeName.trim(); timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); try{ if ("ceylon.language.Nothing".equals(typeName)) { return typeFactory.getNothingDeclaration(); } else if ("java.lang.Throwable".equals(typeName)) { // FIXME: this being here is highly dubious return convertToDeclaration(modules.getLanguageModule(), "ceylon.language.Throwable", declarationType); } else if ("java.lang.Exception".equals(typeName)) { // FIXME: this being here is highly dubious return convertToDeclaration(modules.getLanguageModule(), "ceylon.language.Exception", declarationType); } else if ("java.lang.annotation.Annotation".equals(typeName)) { // FIXME: this being here is highly dubious // here we prefer Annotation over ConstrainedAnnotation but that's fine return convertToDeclaration(modules.getLanguageModule(), "ceylon.language.Annotation", declarationType); } ClassMirror classMirror; try{ classMirror = lookupClassMirror(module, typeName); }catch(NoClassDefFoundError x){ // FIXME: this may not be the best thing to do. If the class is not there we don't know what type of declaration // to return, but perhaps if we use annotation scanner rather than reflection we can figure it out, at least // in cases where the supertype is missing, which throws in reflection at class load. return logModelResolutionException(x.getMessage(), module, "Unable to load type "+typeName).getDeclaration(); } if (classMirror == null) { // special case when bootstrapping because we may need to pull the decl from the typechecked model if(isBootstrap && typeName.startsWith(CEYLON_LANGUAGE+".")){ Declaration languageDeclaration = findLanguageModuleDeclarationForBootstrap(typeName); if(languageDeclaration != null) return languageDeclaration; } String modulePackagePrefix = module.getNameAsString()+"."; if(hasJavaAndCeylonSources() && container == null && typeName.startsWith(modulePackagePrefix)){ // perhaps it's a java source file depending on a ceylon source file, in which // case try to resolve it from source int lastDot = typeName.length(); // FIXME: deal with escapes too List qualified = null; while((lastDot = lastIndexOf(typeName, "$.", lastDot-1)) != -1){ String packagePart = typeName.substring(0, lastDot); String namePart = typeName.substring(lastDot+1); int namePartDot = indexOf(namePart, "$."); if(namePartDot != -1) namePart = namePart.substring(0, namePartDot); if(packagePart.startsWith(modulePackagePrefix) || packagePart.equals(module.getNameAsString())){ Package pkg = module.getPackage(packagePart); if(pkg instanceof LazyPackage){ Declaration memberFromSource = ((LazyPackage) pkg).getDirectMemberFromSource(namePart, Backends.JAVA); if(memberFromSource != null){ if(qualified != null){ for(String path : qualified){ memberFromSource = memberFromSource.getDirectMember(path, null, false); if(memberFromSource == null){ // give up break; } } } return memberFromSource; }else{ // we did find a package, let's not look further up break; } } // no package, try further up if(qualified == null) qualified = new LinkedList(); qualified.add(0, namePart); }else{ // we've gone too far up break; } } // at this point we're left with either the default package, or we looked up past // the module package // FIXME: support the default package } throw new ModelResolutionException("Failed to resolve "+typeName); } // we only allow source loading when it's java code we're compiling in the same go // (well, technically before the ceylon code) if(classMirror.isLoadedFromSource() && !classMirror.isJavaSource()) return null; return convertToDeclaration(module, container, classMirror, declarationType); }finally{ timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } } }); } private int indexOf(String string, String elements) { int smallerIndex = -1; for(int i=0;i largerIndex) // or keep it if we found something after our last find largerIndex = index; } return largerIndex; } private Type newUnknownType() { return new UnknownType(typeFactory).getType(); } protected TypeParameter safeLookupTypeParameter(Scope scope, String name) { TypeParameter param = lookupTypeParameter(scope, name); if(param == null) throw new ModelResolutionException("Type param "+name+" not found in "+scope); return param; } private TypeParameter lookupTypeParameter(Scope scope, String name) { if(scope instanceof Function){ Function m = (Function) scope; for(TypeParameter param : m.getTypeParameters()){ if(param.getName().equals(name)) return param; } if (!m.isToplevel()) { // look it up in its container return lookupTypeParameter(scope.getContainer(), name); } else { // not found return null; } }else if(scope instanceof ClassOrInterface || scope instanceof TypeAlias){ TypeDeclaration decl = (TypeDeclaration) scope; for(TypeParameter param : decl.getTypeParameters()){ if(param.getName().equals(name)) return param; } if (!decl.isToplevel()) { // look it up in its container return lookupTypeParameter(scope.getContainer(), name); } else { // not found return null; } }else if (scope instanceof Constructor) { return lookupTypeParameter(scope.getContainer(), name); }else if(scope instanceof Value || scope instanceof Setter){ Declaration decl = (Declaration) scope; if (!decl.isToplevel()) { // look it up in its container return lookupTypeParameter(scope.getContainer(), name); } else { // not found return null; } }else throw new ModelResolutionException("Type param "+name+" lookup not supported for scope "+scope); } // // Packages public LazyPackage findExistingPackage(final Module theModule, final String thePkgName) { return synchronizedCall(new Callable() { @Override public LazyPackage call() throws Exception { Module module = theModule; String pkgName = thePkgName; String quotedPkgName = JVMModuleUtil.quoteJavaKeywords(pkgName); LazyPackage pkg = findCachedPackage(module, quotedPkgName); if(pkg != null) return loadPackage(pkg); // special case for the jdk module String moduleName = module.getNameAsString(); if(jdkProvider.isJDKModule(moduleName)){ if(jdkProvider.isJDKPackage(moduleName, pkgName)){ return findOrCreatePackage(module, pkgName); } return null; } // only create it if it exists if(((LazyModule)module).containsPackage(pkgName) && loadPackage(module, pkgName, false)){ return findOrCreatePackage(module, pkgName); } return null; } }); } public LazyPackage loadPackage(LazyPackage pkg) { if(!pkg.isDescriptorLoaded() && packageDescriptorsNeedLoading) loadPackageDescriptor(pkg); return pkg; } private LazyPackage findCachedPackage(Module module, String quotedPkgName) { LazyPackage pkg = packagesByName.get(cacheKeyByModule(module, quotedPkgName)); if(pkg != null){ // only return it if it matches the module we're looking for, because if it doesn't we have an issue already logged // for a direct dependency on same module different versions logged, so no need to confuse this further if(module != null && pkg.getModule() != null && !module.equals(pkg.getModule())) return null; return pkg; } return null; } public LazyPackage findOrCreatePackage(final Module module, final String pkgName) { return synchronizedCall(new Callable() { @Override public LazyPackage call() throws Exception { String quotedPkgName = JVMModuleUtil.quoteJavaKeywords(pkgName); LazyPackage pkg = findCachedPackage(module, quotedPkgName); if(pkg != null){ return loadPackage(pkg); } // try to find it from the module, perhaps it already got created and we didn't catch it if(module instanceof LazyModule){ pkg = (LazyPackage) ((LazyModule) module).findPackageNoLazyLoading(pkgName); }else if(module != null){ pkg = (LazyPackage) module.getDirectPackage(pkgName); } boolean isNew = pkg == null; if(pkg == null){ pkg = new LazyPackage(AbstractModelLoader.this); // FIXME: some refactoring needed pkg.setName(Arrays.asList(pkgName.split("\\."))); } packagesByName.put(cacheKeyByModule(module, quotedPkgName), pkg); // only bind it if we already have a module if(isNew && module != null){ pkg.setModule(module); if(module instanceof LazyModule) ((LazyModule) module).addPackage(pkg); else module.getPackages().add(pkg); } // only load package descriptors for new packages after a certain phase if(packageDescriptorsNeedLoading) loadPackageDescriptor(pkg); return pkg; } }); } public void loadPackageDescriptors() { synchronizedRun(new Runnable() { @Override public void run() { for(LazyPackage pkg : packagesByName.values()){ loadPackageDescriptor(pkg); } packageDescriptorsNeedLoading = true; } }); } private void loadPackageDescriptor(LazyPackage pkg) { if(!pkg.getModule().isAvailable()) lazyLoadModule(pkg.getModule()); // Consider the descriptor loaded, we're not going to change our mind pkg.setDescriptorLoaded(true); // Don't try to load a package descriptor for ceylon.language // if we're bootstrapping if (isBootstrap && pkg.getQualifiedNameString().startsWith(CEYLON_LANGUAGE)) { return; } // let's not load package descriptors for Java modules if(pkg.getModule() != null && ((LazyModule)pkg.getModule()).isJava()){ pkg.setShared(((LazyModule)pkg.getModule()).isExportedJavaPackage(pkg.getNameAsString())); return; } String quotedQualifiedName = JVMModuleUtil.quoteJavaKeywords(pkg.getQualifiedNameString()); // FIXME: not sure the toplevel package can have a package declaration String className = quotedQualifiedName.isEmpty() ? NamingBase.PACKAGE_DESCRIPTOR_CLASS_NAME : quotedQualifiedName + "." + NamingBase.PACKAGE_DESCRIPTOR_CLASS_NAME; logVerbose("[Trying to look up package from "+className+"]"); Module module = pkg.getModule(); if(module == null) throw new RuntimeException("Assertion failed: module is null for package "+pkg.getNameAsString()); ClassMirror packageClass = loadClass(module, quotedQualifiedName, className); if(packageClass == null){ logVerbose("[Failed to complete "+className+"]"); // missing: leave it private return; } // did we compile it from source or class? if(packageClass.isLoadedFromSource()){ // must have come from source, in which case we walked it and // loaded its values already logVerbose("[We are compiling the package "+className+"]"); return; } loadCompiledPackage(packageClass, pkg); } public void lazyLoadModule(Module module) { } private void loadCompiledPackage(ClassMirror packageClass, Package pkg) { String name = getAnnotationStringValue(packageClass, CEYLON_PACKAGE_ANNOTATION, "name"); Boolean shared = getAnnotationBooleanValue(packageClass, CEYLON_PACKAGE_ANNOTATION, "shared"); // FIXME: validate the name? if(name == null || name.isEmpty()){ logWarning("Package class "+pkg.getQualifiedNameString()+" contains no name, ignoring it"); return; } if(shared == null){ logWarning("Package class "+pkg.getQualifiedNameString()+" contains no shared, ignoring it"); return; } pkg.setShared(shared); } public Module lookupModuleByPackageName(String packageName) { for(Module module : modules.getListOfModules()){ // don't try the default module because it will always say yes if(module.isDefaultModule()) continue; // skip modules we're not loading things from if(!ModelUtil.equalModules(module,getLanguageModule()) && !isModuleInClassPath(module)) continue; if(module instanceof LazyModule){ if(((LazyModule)module).containsPackage(packageName)) return module; }else if(isSubPackage(module.getNameAsString(), packageName)){ return module; } } if(jdkProvider.isJDKPackage(packageName)){ String moduleName = jdkProvider.getJDKModuleNameForPackage(packageName); return findModule(moduleName, jdkProvider.getJDKVersion()); } if(packageName.startsWith("com.redhat.ceylon.compiler.java.runtime") || packageName.startsWith("com.redhat.ceylon.compiler.java.language") || packageName.startsWith("com.redhat.ceylon.compiler.java.metadata")){ return getLanguageModule(); } return modules.getDefaultModule(); } private boolean isSubPackage(String moduleName, String pkgName) { return pkgName.equals(moduleName) || pkgName.startsWith(moduleName+"."); } // // Modules /** * Finds or creates a new module. This is mostly useful to force creation of modules such as jdk * or ceylon.language modules. */ protected Module findOrCreateModule(final String theModuleName, final String theVersion) { return synchronizedCall(new Callable() { @Override public Module call() throws Exception { String moduleName = theModuleName; String version = theVersion; boolean isJdk = false; // make sure it isn't loaded Module module = getLoadedModule(moduleName, version); if(module != null) return module; // this method gets called before we set the jdk provider, but for the default and language module if(jdkProvider != null && jdkProvider.isJDKModule(moduleName)){ isJdk = true; } java.util.List moduleNameList = Arrays.asList(moduleName.split("\\.")); module = moduleManager.getOrCreateModule(moduleNameList, version); // make sure that when we load the ceylon language module we set it to where // the typechecker will look for it if(moduleName.equals(CEYLON_LANGUAGE) && modules.getLanguageModule() == null){ modules.setLanguageModule(module); } // TRICKY We do this only when isJava is true to prevent resetting // the value to false by mistake. LazyModule get's created with // this attribute to false by default, so it should work if (isJdk && module instanceof LazyModule) { ((LazyModule)module).setJava(true); module.setNativeBackends(Backend.Java.asSet()); } // FIXME: this can't be that easy. if(isJdk) module.setAvailable(true); return module; } }); } public boolean loadCompiledModule(Module module) { return loadCompiledModule(module, true); } public boolean loadCompiledModule(final Module theModule, final boolean theLoadModuleImports) { return synchronizedCall(new Callable() { @Override public Boolean call() throws Exception { Module module = theModule; boolean loadModuleImports = theLoadModuleImports; if(module.isDefaultModule()) return false; String pkgName = module.getNameAsString(); if(pkgName.isEmpty()) return false; ClassMirror moduleClass = findModuleClass(module, pkgName); if(moduleClass != null){ // load its module annotation return loadCompiledModule(module, moduleClass, loadModuleImports); } // give up return false; } }); } protected ClassMirror findModuleClass(Module module, String pkgName) { String moduleClassName = pkgName + "." + NamingBase.MODULE_DESCRIPTOR_CLASS_NAME; logVerbose("[Trying to look up module from "+moduleClassName+"]"); ClassMirror moduleClass = loadClass(module, pkgName, moduleClassName); if(moduleClass == null){ // perhaps we have an old module? String oldModuleClassName = pkgName + "." + NamingBase.OLD_MODULE_DESCRIPTOR_CLASS_NAME; logVerbose("[Trying to look up older module descriptor from "+oldModuleClassName+"]"); ClassMirror oldModuleClass = loadClass(module, pkgName, oldModuleClassName); // keep it only if it has a module annotation, otherwise it could be a normal value if(oldModuleClass != null && oldModuleClass.getAnnotation(CEYLON_MODULE_ANNOTATION) != null) moduleClass = oldModuleClass; } return moduleClass; } private boolean loadCompiledModule(Module module, ClassMirror moduleClass, boolean loadModuleImports) { String moduleClassName = moduleClass.getQualifiedName(); String name = getAnnotationStringValue(moduleClass, CEYLON_MODULE_ANNOTATION, "name"); String version = getAnnotationStringValue(moduleClass, CEYLON_MODULE_ANNOTATION, "version"); if(name == null || name.isEmpty()){ logWarning("Module class "+moduleClassName+" contains no name, ignoring it"); return false; } if(!name.equals(module.getNameAsString())){ logWarning("Module class "+moduleClassName+" declares an invalid name: "+name+". It should be: "+module.getNameAsString()); return false; } if(version == null || version.isEmpty()){ logWarning("Module class "+moduleClassName+" contains no version, ignoring it"); return false; } if(!version.equals(module.getVersion())){ logWarning("Module class "+moduleClassName+" declares an invalid version: "+version+". It should be: "+module.getVersion()); return false; } int major = getAnnotationIntegerValue(moduleClass, CEYLON_CEYLON_ANNOTATION, "major", 0); int minor = getAnnotationIntegerValue(moduleClass, CEYLON_CEYLON_ANNOTATION, "minor", 0); module.setJvmMajor(major); module.setJvmMinor(minor); // no need to load the "nativeBackends" annotation value, it's loaded from annotations setAnnotations(module, moduleClass, false); if(loadModuleImports){ List imports = getAnnotationArrayValue(moduleClass, CEYLON_MODULE_ANNOTATION, "dependencies"); if(imports != null){ boolean supportsNamespaces = ModuleUtil.supportsImportsWithNamespaces(major, minor); for (AnnotationMirror importAttribute : imports) { String dependencyName = (String) importAttribute.getValue("name"); if (dependencyName != null) { String namespace; if (supportsNamespaces) { namespace = (String) importAttribute.getValue("namespace"); if (namespace != null && namespace.isEmpty()) { namespace = null; } } else { if (ModuleUtil.isMavenModule(dependencyName)) { namespace = "maven"; } else { namespace = null; } } String dependencyVersion = (String) importAttribute.getValue("version"); Module dependency = moduleManager.getOrCreateModule(ModuleManager.splitModuleName(dependencyName), dependencyVersion); Boolean optionalVal = (Boolean) importAttribute.getValue("optional"); Boolean exportVal = (Boolean) importAttribute.getValue("export"); List nativeBackends = (List) importAttribute.getValue("nativeBackends"); Backends backends = nativeBackends == null ? Backends.ANY : Backends.fromAnnotations(nativeBackends); ModuleImport moduleImport = moduleManager.findImport(module, dependency); if (moduleImport == null) { boolean optional = optionalVal != null && optionalVal; boolean export = exportVal != null && exportVal; moduleImport = new ModuleImport(namespace, dependency, optional, export, backends); module.addImport(moduleImport); } } } } } module.setAvailable(true); modules.getListOfModules().add(module); Module languageModule = modules.getLanguageModule(); module.setLanguageModule(languageModule); if(loadModuleImports){ if(!ModelUtil.equalModules(module, languageModule)){ boolean found = false; for (ModuleImport mi : module.getImports()) { if (mi.getModule().isLanguageModule()) { found = true; break; } } if (!found) { // It's not really a LazyModule because we're not loading // it lazily. It's only here for module version analysis. // But other stuff expects non-source modules to be lazy. LazyModule oldLangMod = new LazyModule() { @Override protected AbstractModelLoader getModelLoader() { return AbstractModelLoader.this; }}; oldLangMod.setLanguageModule(oldLangMod); oldLangMod.setName(Arrays.asList("ceylon", "language")); oldLangMod.setVersion(getJvmLanguageModuleVersion(major, minor)); oldLangMod.setNativeBackends(Backends.JAVA); oldLangMod.setJvmMajor(major); oldLangMod.setJvmMinor(minor); ModuleImport moduleImport = new ModuleImport(null, oldLangMod, false, false); module.addImport(moduleImport); } } } return true; } // // Utils for loading type info from the model @SuppressWarnings("unchecked") private List getAnnotationArrayValue(AnnotatedMirror mirror, String type, String field) { return (List) getAnnotationValue(mirror, type, field); } @SuppressWarnings("unchecked") private List getAnnotationArrayValue(AnnotatedMirror mirror, String type) { return (List) getAnnotationValue(mirror, type); } private String getAnnotationStringValue(AnnotatedMirror mirror, String type) { return getAnnotationStringValue(mirror, type, "value"); } protected String getAnnotationStringValue(AnnotatedMirror mirror, String type, String field) { return (String) getAnnotationValue(mirror, type, field); } private Boolean getAnnotationBooleanValue(AnnotatedMirror mirror, String type, String field) { return (Boolean) getAnnotationValue(mirror, type, field); } private int getAnnotationIntegerValue(AnnotatedMirror mirror, String type, String field, int defaultValue) { Integer val = (Integer) getAnnotationValue(mirror, type, field); return val != null ? val : defaultValue; } @SuppressWarnings("unchecked") private List getAnnotationStringValues(AnnotationMirror annotation, String field) { return (List)annotation.getValue(field); } private Object getAnnotationValue(AnnotatedMirror mirror, String type) { return getAnnotationValue(mirror, type, "value"); } private Object getAnnotationValue(AnnotatedMirror mirror, String type, String fieldName) { AnnotationMirror annotation = mirror.getAnnotation(type); if(annotation != null){ return annotation.getValue(fieldName); } return null; } // // ModelCompleter @Override public void complete(final AnnotationProxyClass klass) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); complete(klass, klass.iface); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final AnnotationProxyMethod ctor) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); AnnotationProxyClass klass = ctor.proxyClass; LazyInterface iface = klass.iface; complete(ctor, klass, iface); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final LazyInterface iface) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); complete(iface, iface.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void completeTypeParameters(final LazyInterface iface) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeTypeParameters(iface, iface.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final LazyClass klass) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); complete(klass, klass.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void completeTypeParameters(final LazyClass klass) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeTypeParameters(klass, klass.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void completeTypeParameters(final LazyClassAlias lazyClassAlias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAliasTypeParameters(lazyClassAlias, lazyClassAlias.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void completeTypeParameters(final LazyInterfaceAlias lazyInterfaceAlias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAliasTypeParameters(lazyInterfaceAlias, lazyInterfaceAlias.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void completeTypeParameters(final LazyTypeAlias lazyTypeAlias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAliasTypeParameters(lazyTypeAlias, lazyTypeAlias.classMirror); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final LazyInterfaceAlias alias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAlias(alias, alias.classMirror, CEYLON_ALIAS_ANNOTATION); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final LazyClassAlias alias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAlias(alias, alias.classMirror, CEYLON_ALIAS_ANNOTATION); String constructorName = (String)alias.classMirror.getAnnotation(CEYLON_ALIAS_ANNOTATION).getValue("constructor"); Declaration extendedTypeDeclaration = alias.getExtendedType().getDeclaration(); if (constructorName != null && !constructorName.isEmpty()) { Declaration constructor = alias.getExtendedType().getDeclaration().getMember(constructorName, null, false); if (constructor instanceof FunctionOrValue && ((FunctionOrValue)constructor).getTypeDeclaration() instanceof Constructor) { alias.setConstructor(((FunctionOrValue)constructor).getTypeDeclaration()); } else { logError("class aliased constructor " + constructorName + " which is no longer a constructor of " + alias.getExtendedType().getDeclaration().getQualifiedNameString()); } } else { if (extendedTypeDeclaration instanceof Class) { Class theClass = (Class) extendedTypeDeclaration; if (theClass.hasConstructors()) { alias.setConstructor(theClass.getDefaultConstructor()); } else if(theClass.getParameterList() != null) { alias.setConstructor(theClass); } } } // Find the instantiator method MethodMirror instantiator = null; ClassMirror instantiatorClass = alias.isToplevel() ? alias.classMirror : alias.classMirror.getEnclosingClass(); String aliasName = NamingBase.getAliasInstantiatorMethodName(alias); for (MethodMirror method : instantiatorClass.getDirectMethods()) { if (method.getName().equals(aliasName)) { instantiator = method; break; } } // Read the parameters from the instantiator, rather than the aliased class if (instantiator != null) { setParameters(alias, alias.classMirror, instantiator, true, alias, false); } timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } @Override public void complete(final LazyTypeAlias alias) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); completeLazyAlias(alias, alias.classMirror, CEYLON_TYPE_ALIAS_ANNOTATION); timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } }); } private void complete(AnnotationProxyMethod ctor, AnnotationProxyClass klass, LazyInterface iface) { ParameterList ctorpl = new ParameterList(); ctorpl.setPositionalParametersSupported(false); ctor.addParameterList(ctorpl); List ctorParams = new ArrayList(); for (Declaration member : iface.getMembers()) { boolean isValue = member.getName().equals("value"); if (member instanceof JavaMethod) { JavaMethod m = (JavaMethod)member; Parameter ctorParam = new Parameter(); ctorParams.add(ctorParam); Value value = new Value(); ctorParam.setModel(value); value.setInitializerParameter(ctorParam); ctorParam.setDeclaration(ctor); value.setContainer(klass); value.setScope(klass); ctorParam.setDefaulted(m.isDefaultedAnnotation()); value.setName(member.getName()); ctorParam.setName(member.getName()); value.setType(annotationParameterType(iface.getUnit(), m)); value.setUnboxed(true); value.setUnit(iface.getUnit()); if(isValue) ctorpl.getParameters().add(0, ctorParam); else ctorpl.getParameters().add(ctorParam); ctor.addMember(value); } } makeInteropAnnotationConstructorInvocation(ctor, klass, ctorParams); } private void complete(AnnotationProxyClass klass, LazyInterface iface) { ParameterList classpl = new ParameterList(); klass.addParameterList(classpl); for (Declaration member : iface.getMembers()) { boolean isValue = member.getName().equals("value"); if (member instanceof JavaMethod) { JavaMethod m = (JavaMethod)member; Parameter klassParam = new Parameter(); Value value = new Value(); klassParam.setModel(value); value.setInitializerParameter(klassParam); klassParam.setDeclaration(klass); value.setContainer(klass); value.setScope(klass); value.setName(member.getName()); klassParam.setName(member.getName()); value.setType(annotationParameterType(iface.getUnit(), m)); value.setUnboxed(true); value.setUnit(iface.getUnit()); if(isValue) classpl.getParameters().add(0, klassParam); else classpl.getParameters().add(klassParam); klass.addMember(value); } } } private void completeLazyAliasTypeParameters(TypeDeclaration alias, ClassMirror mirror) { // type parameters setTypeParameters(alias, mirror, true); } private void completeLazyAlias(TypeDeclaration alias, ClassMirror mirror, String aliasAnnotationName) { // now resolve the extended type AnnotationMirror aliasAnnotation = mirror.getAnnotation(aliasAnnotationName); String extendedTypeString = (String) aliasAnnotation.getValue(); Type extendedType = decodeType(extendedTypeString, alias, ModelUtil.getModuleContainer(alias), "alias target"); alias.setExtendedType(extendedType); } private void completeTypeParameters(ClassOrInterface klass, ClassMirror classMirror) { boolean isCeylon = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null; setTypeParameters(klass, classMirror, isCeylon); } private void complete(ClassOrInterface klass, ClassMirror classMirror) { boolean isFromJDK = isFromJDK(classMirror); boolean isCeylon = (classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null); boolean isNativeHeaderMember = klass.isNativeHeader(); // now that everything has containers, do the inner classes if(klass instanceof Class == false || !((Class)klass).isOverloaded()){ // this will not load inner classes of overloads, but that's fine since we want them in the // abstracted super class (the real one) addInnerClasses(klass, classMirror); } // Java classes with multiple constructors get turned into multiple Ceylon classes // Here we get the specific constructor that was assigned to us (if any) MethodMirror constructor = null; if (klass instanceof LazyClass) { constructor = ((LazyClass)klass).getConstructor(); setHasJpaConstructor((LazyClass)klass, classMirror); } // Set up enumerated constructors before looking at getters, // because the type of the getter is the constructor's type Boolean hasConstructors = hasConstructors(classMirror); if (hasConstructors != null && hasConstructors) { HashMap m = new HashMap<>(); // Get all the java constructors... for (MethodMirror ctorMirror : getClassConstructors(classMirror, classMirror, constructorOnly)) { m.put(getCtorName(ctorMirror), ctorMirror); } for (MethodMirror ctor : getClassConstructors( classMirror, classMirror.getEnclosingClass() != null ? classMirror.getEnclosingClass() : classMirror, new ValueConstructorGetter(classMirror))) { Object name = getAnnotationValue(ctor, CEYLON_NAME_ANNOTATION); MethodMirror ctorMirror = m.remove(name); Constructor c; // When for each value constructor getter we can add a Value+Constructor if (ctorMirror == null) { // Only add a Constructor using the getter if we couldn't find a matching java constructor c = addConstructor((Class)klass, classMirror, ctor, isNativeHeaderMember); } else { c = addConstructor((Class)klass, classMirror, ctorMirror, isNativeHeaderMember); } FunctionOrValue fov = addConstructorMethorOrValue((Class)klass, classMirror, ctor, c, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(c, klass)) { Constructor hdr; if (ctorMirror == null) { hdr = addConstructor((Class)klass, classMirror, ctor, true); } else { hdr = addConstructor((Class)klass, classMirror, ctorMirror, true); } FunctionOrValue hdrfov = addConstructorMethorOrValue((Class)klass, classMirror, ctorMirror, hdr, true); initNativeHeader(hdr, c); initNativeHeader(hdrfov, fov); } else if (isCeylon && shouldLinkNatives(c)) { initNativeHeaderMember(c); initNativeHeaderMember(fov); } } // Everything left must be a callable constructor, so add Function+Constructor for (MethodMirror ctorMirror : m.values()) { Constructor c = addConstructor((Class)klass, classMirror, ctorMirror, isNativeHeaderMember); FunctionOrValue fov = addConstructorMethorOrValue((Class)klass, classMirror, ctorMirror, c, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(c, klass)) { Constructor hdr = addConstructor((Class)klass, classMirror, ctorMirror, true); FunctionOrValue hdrfov = addConstructorMethorOrValue((Class)klass, classMirror, ctorMirror, hdr, true); initNativeHeader(hdr, c); initNativeHeader(hdrfov, fov); } else if (isCeylon && shouldLinkNatives(c)) { initNativeHeaderMember(c); initNativeHeaderMember(fov); } } } // Turn a list of possibly overloaded methods into a map // of lists that contain methods with the same name Map> methods = new LinkedHashMap>(); collectMethods(classMirror.getDirectMethods(), methods, isCeylon, isFromJDK); if(isCeylon && klass instanceof LazyInterface && JvmBackendUtil.isCompanionClassNeeded(klass)){ ClassMirror companionClass = ((LazyInterface)klass).companionClass; if(companionClass != null) collectMethods(companionClass.getDirectMethods(), methods, isCeylon, isFromJDK); else logWarning("CompanionClass missing for "+klass); } boolean seenStringAttribute = false; boolean seenHashAttribute = false; boolean seenStringGetter = false; boolean seenHashGetter = false; MethodMirror stringSetter = null; MethodMirror hashSetter = null; Map> getters = new HashMap<>(); Map> setters = new HashMap<>(); // Collect attributes for(List methodMirrors : methods.values()){ for (MethodMirror methodMirror : methodMirrors) { // same tests as in isMethodOverloaded() if(methodMirror.isConstructor() || isInstantiator(methodMirror)) { break; } else if(isGetter(methodMirror)) { String name = getJavaAttributeName(methodMirror); putMultiMap(getters, name, methodMirror); } else if(isSetter(methodMirror)) { String name = getJavaAttributeName(methodMirror); putMultiMap(setters, name, methodMirror); } else if(isHashAttribute(methodMirror)) { putMultiMap(getters, "hash", methodMirror); seenHashAttribute = true; } else if(isStringAttribute(methodMirror)) { putMultiMap(getters, "string", methodMirror); seenStringAttribute = true; } else { // we never map getString to a property, or generate one if(isStringGetter(methodMirror)) seenStringGetter = true; // same for getHash else if(isHashGetter(methodMirror)) seenHashGetter = true; else if(isStringSetter(methodMirror)){ stringSetter = methodMirror; // we will perhaps add it later continue; }else if(isHashSetter(methodMirror)){ hashSetter = methodMirror; // we will perhaps add it later continue; } } } } // now figure out which properties to add NEXT_PROPERTY: for(Map.Entry> getterEntry : getters.entrySet()){ String propertyName = getterEntry.getKey(); List getterList = getterEntry.getValue(); for(MethodMirror getterMethod : getterList){ // if it's hashCode() or toString() they win if(isHashAttribute(getterMethod)) { // ERASURE // Un-erasing 'hash' attribute from 'hashCode' method Declaration decl = addValue(klass, getterMethod, "hash", isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, getterMethod, "hash", true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } // remove it as a method and add all other getters with the same name // as methods removeMultiMap(methods, getterMethod.getName(), getterMethod); // next property continue NEXT_PROPERTY; } if(isStringAttribute(getterMethod)) { // ERASURE // Un-erasing 'string' attribute from 'toString' method Declaration decl = addValue(klass, getterMethod, "string", isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, getterMethod, "string", true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } // remove it as a method and add all other getters with the same name // as methods removeMultiMap(methods, getterMethod.getName(), getterMethod); // next property continue NEXT_PROPERTY; } } // we've weeded out toString/hashCode, now if we have a single property it's easy we just add it if(getterList.size() == 1){ // FTW! MethodMirror getterMethod = getterList.get(0); // simple attribute Declaration decl = addValue(klass, getterMethod, propertyName, isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, getterMethod, propertyName, true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } // remove it as a method removeMultiMap(methods, getterMethod.getName(), getterMethod); // next property continue NEXT_PROPERTY; } // we have more than one // if we have a setter let's favour the one that matches the setter List matchingSetters = setters.get(propertyName); if(matchingSetters != null){ if(matchingSetters.size() == 1){ // single setter will tell us what we need MethodMirror matchingSetter = matchingSetters.get(0); MethodMirror bestGetter = null; boolean booleanSetter = matchingSetter.getParameters().get(0).getType().getKind() == TypeKind.BOOLEAN; /* * Getters do not support overloading since they have no parameters, so they can only differ based on * name. For boolean properties we favour "is" getters, otherwise "get" getters. */ for(MethodMirror getterMethod : getterList){ if(propertiesMatch(klass, getterMethod, matchingSetter)){ if(bestGetter == null) bestGetter = getterMethod; else{ // we have two getters, find the best one if(booleanSetter){ // favour the "is" getter if(getterMethod.getName().startsWith("is")) bestGetter = getterMethod; // else keep the current best, it must be an "is" getter }else{ // favour the "get" getter if(getterMethod.getName().startsWith("get")) bestGetter = getterMethod; // else keep the current best, it must be a "get" getter } break; } } } if(bestGetter != null){ // got it! // simple attribute Declaration decl = addValue(klass, bestGetter, propertyName, isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, bestGetter, propertyName, true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } // remove it as a method and add all other getters with the same name // as methods removeMultiMap(methods, bestGetter.getName(), bestGetter); // next property continue NEXT_PROPERTY; }// else we cannot find the right getter thanks to the setter, keep looking } } // setters did not help us, we have more than one getter, one must be "is"/boolean, the other "get" if(getterList.size() == 2){ // if the "get" is also a boolean, prefer the "is" MethodMirror isMethod = null; MethodMirror getMethod = null; for(MethodMirror getterMethod : getterList){ if(getterMethod.getName().startsWith("is")) isMethod = getterMethod; else if(getterMethod.getName().startsWith("get")) getMethod = getterMethod; } if(isMethod != null && getMethod != null){ MethodMirror bestGetter; if(getMethod.getReturnType().getKind() == TypeKind.BOOLEAN){ // pick the is method bestGetter = isMethod; }else{ // just take the getter bestGetter = getMethod; } // simple attribute Declaration decl = addValue(klass, bestGetter, propertyName, isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, bestGetter, propertyName, true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } // remove it as a method and add all other getters with the same name // as methods removeMultiMap(methods, bestGetter.getName(), bestGetter); // next property continue NEXT_PROPERTY; } } } Set fieldNames = new HashSet(); // collect field names first for(FieldMirror fieldMirror : classMirror.getDirectFields()){ if(!keepField(fieldMirror, isCeylon, isFromJDK)) continue; // do not change the name case here otherwise it will appear taken by itself fieldNames.add(fieldMirror.getName()); } // now handle fields for(FieldMirror fieldMirror : classMirror.getDirectFields()){ if(!keepField(fieldMirror, isCeylon, isFromJDK)) continue; String name = fieldMirror.getName(); if(!isCeylon && !JvmBackendUtil.isInitialLowerCase(name)){ String newName = NamingBase.getJavaBeanName(name); if(!fieldNames.contains(newName) && !addingFieldWouldConflictWithMember(klass, newName) && !methods.containsKey(newName)) name = newName; } // skip the field if "we've already got one" boolean conflicts = addingFieldWouldConflictWithMember(klass, name); if (!conflicts) { Declaration decl = addValue(klass, name, fieldMirror, isCeylon, isNativeHeaderMember); if (isCeylon && shouldCreateNativeHeader(decl, klass)) { Declaration hdr = addValue(klass, name, fieldMirror, true, true); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, decl); } else if (isCeylon && shouldLinkNatives(decl)) { initNativeHeaderMember(decl); } } } // Now mark all Values for which Setters exist as variable for(List variables : setters.values()){ for(MethodMirror setter : variables){ String name = getJavaAttributeName(setter); // make sure we handle private postfixes name = JvmBackendUtil.strip(name, isCeylon, setter.isPublic()); Declaration decl = klass.getMember(name, null, false); // skip Java fields, which we only get if there is no getter method, in that case just add the setter method if (decl instanceof JavaBeanValue) { JavaBeanValue value = (JavaBeanValue)decl; // only add the setter if it has the same visibility as the getter if (setter.isPublic() && value.mirror.isPublic() || setter.isProtected() && value.mirror.isProtected() || setter.isDefaultAccess() && value.mirror.isDefaultAccess() || (!setter.isPublic() && !value.mirror.isPublic() && !setter.isProtected() && !value.mirror.isProtected() && !setter.isDefaultAccess() && !value.mirror.isDefaultAccess())) { VariableMirror setterParam = setter.getParameters().get(0); Module module = ModelUtil.getModuleContainer(klass); Type paramType = obtainType(setterParam.getType(), setterParam, klass, module, VarianceLocation.INVARIANT, "setter '"+setter.getName()+"'", klass); NullStatus nullPolicy = getUncheckedNullPolicy(isCeylon, setterParam.getType(), setterParam); // if there's no annotation on the setter param, inherit annotations from getter if(nullPolicy == NullStatus.UncheckedNull) nullPolicy = getUncheckedNullPolicy(isCeylon, value.mirror.getReturnType(), value.mirror); switch(nullPolicy){ case Optional: if(!isCeylon){ paramType = makeOptionalTypePreserveUnderlyingType(paramType, module); } break; } // only add the setter if it has exactly the same type as the getter if(paramType.isExactly(value.getType())){ value.setVariable(true); value.setSetterName(setter.getName()); if(value.isTransient()){ // must be a real setter makeSetter(value, null); } if(!isCeylon && isCoercedMethod(setter)){ // leave it as a method so we get a fake method for it }else{ // remove it as a method removeMultiMap(methods, setter.getName(), setter); } }else logVerbose("Setter parameter type for "+name+" does not match corresponding getter type, adding setter as a method"); } else { logVerbose("Setter visibility for "+name+" does not match corresponding getter visibility, adding setter as a method"); } } } } // special cases if we have hashCode() setHash() and no getHash() if(hashSetter != null){ if(seenHashAttribute && !seenHashGetter){ Declaration attr = klass.getDirectMember("hash", null, false); if(attr instanceof JavaBeanValue){ ((JavaBeanValue) attr).setVariable(true); ((JavaBeanValue) attr).setSetterName(hashSetter.getName()); // remove it as a method removeMultiMap(methods, hashSetter.getName(), hashSetter); } } } // special cases if we have toString() setString() and no getString() if(stringSetter != null){ if(seenStringAttribute && !seenStringGetter){ Declaration attr = klass.getDirectMember("string", null, false); if(attr instanceof JavaBeanValue){ ((JavaBeanValue) attr).setVariable(true); ((JavaBeanValue) attr).setSetterName(stringSetter.getName()); // remove it as a method removeMultiMap(methods, stringSetter.getName(), stringSetter); } } } // Add the methods, treat remaining getters/setters as methods for(List methodMirrors : methods.values()){ boolean isOverloaded = isMethodOverloaded(methodMirrors, isCeylon); List overloads = null; for (MethodMirror methodMirror : methodMirrors) { // normal method Function m = addMethod(klass, methodMirror, classMirror, isCeylon, isOverloaded, isNativeHeaderMember, false); if (!isOverloaded && isCeylon && shouldCreateNativeHeader(m, klass)) { Declaration hdr = addMethod(klass, methodMirror, classMirror, true, isOverloaded, true, false); setNonLazyDeclarationProperties(hdr, classMirror, classMirror, classMirror, true); initNativeHeader(hdr, m); } else if (isCeylon && shouldLinkNatives(m)) { initNativeHeaderMember(m); } if (m.isOverloaded()) { overloads = overloads == null ? new ArrayList(methodMirrors.size()) : overloads; overloads.add(m); } if(isOverloaded && !isCeylon && isCoercedMethod(methodMirror)){ Function coercionMethod = addMethod(klass, methodMirror, classMirror, isCeylon, isOverloaded, isNativeHeaderMember, true); coercionMethod.setRealFunction(m); overloads.add(coercionMethod); } } if (overloads != null && !overloads.isEmpty()) { // We create an extra "abstraction" method for overloaded methods Function abstractionMethod = addMethod(klass, methodMirrors.get(0), classMirror, isCeylon, false, false, false); abstractionMethod.setAbstraction(true); abstractionMethod.setOverloads(overloads); abstractionMethod.setType(newUnknownType()); } } // Having loaded methods and values, we can now set the constructor parameters if(constructor != null && !isDefaultNamedCtor(classMirror, constructor) && (!(klass instanceof LazyClass) || !((LazyClass)klass).isAnonymous())) setParameters((Class)klass, classMirror, constructor, isCeylon, klass, klass.isCoercionPoint()); // Now marry-up attributes and parameters) if (klass instanceof Class) { for (Declaration m : klass.getMembers()) { if (JvmBackendUtil.isValue(m)) { Value v = (Value)m; Parameter p = ((Class)klass).getParameter(v.getName()); if (p != null) { p.setHidden(true); } } } } setExtendedType(klass, classMirror); setSatisfiedTypes(klass, classMirror); setCaseTypes(klass, classMirror); setAnnotations(klass, classMirror, isNativeHeaderMember); if(klass instanceof Interface) klass.setSamName(isFunctionalInterfaceWithExceptions(classMirror)); // local declarations come last, because they need all members to be completed first if(!klass.isAlias()){ ClassMirror containerMirror = classMirror; if(klass instanceof LazyInterface){ ClassMirror companionClass = ((LazyInterface) klass).companionClass; if(companionClass != null) containerMirror = companionClass; } addLocalDeclarations((LazyContainer) klass, containerMirror, classMirror); } if (!isCeylon) { // In java, a class can inherit a public member from a non-public supertype for (Declaration d : klass.getMembers()) { if (d.isShared()) { d.setVisibleScope(null); } } } } private boolean addingFieldWouldConflictWithMember(ClassOrInterface klass, String name) { return klass.getDirectMember(name, null, false) != null || "equals".equals(name) || "string".equals(name) || "hash".equals(name); } private boolean keepField(FieldMirror fieldMirror, boolean isCeylon, boolean isFromJDK) { // We skip members marked with @Ignore if(fieldMirror.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null) return false; if(skipPrivateMember(fieldMirror)) return false; if(isCeylon && fieldMirror.isStatic()) return false; // FIXME: temporary, because some private classes from the jdk are // referenced in private methods but not available if(isFromJDK && !fieldMirror.isPublic() && !fieldMirror.isProtected()) return false; return true; } private boolean propertiesMatch(ClassOrInterface klass, MethodMirror getter, MethodMirror setter) { // only add the setter if it has the same visibility as the getter if (setter.isPublic() && getter.isPublic() || setter.isProtected() && getter.isProtected() || setter.isDefaultAccess() && getter.isDefaultAccess() || (!setter.isPublic() && !getter.isPublic() && !setter.isProtected() && !getter.isProtected() && !setter.isDefaultAccess() && !getter.isDefaultAccess())) { Module module = ModelUtil.getModuleContainer(klass); VariableMirror setterParam = setter.getParameters().get(0); Type paramType = obtainType(setterParam.getType(), setterParam, klass, module, VarianceLocation.INVARIANT, "setter '"+setter.getName()+"'", klass); Type returnType = obtainType(getter.getReturnType(), getter, klass, module, VarianceLocation.INVARIANT, "getter '"+getter.getName()+"'", klass); // only add the setter if it has exactly the same type as the getter if(paramType.isExactly(returnType)){ return true; } } return false; } private void removeMultiMap(Map> map, Key key, Val val) { List list = map.get(key); if(list != null){ list.remove(val); if(list.isEmpty()) map.remove(key); } } private void putMultiMap(Map> map, Key key, Val value) { List list = map.get(key); if(list == null){ list = new LinkedList<>(); map.put(key, list); } list.add(value); } private Constructor addConstructor(Class klass, ClassMirror classMirror, MethodMirror ctor, boolean isNativeHeader) { boolean isCeylon = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null; Constructor constructor = new Constructor(); constructor.setName(getCtorName(ctor)); constructor.setContainer(klass); constructor.setScope(klass); constructor.setUnit(klass.getUnit()); constructor.setAbstract(ctor.getAnnotation(CEYLON_LANGUAGE_ABSTRACT_ANNOTATION) != null); constructor.setExtendedType(klass.getType()); setNonLazyDeclarationProperties(constructor, ctor, ctor, classMirror, isCeylon); setAnnotations(constructor, ctor, isNativeHeader); klass.addMember(constructor); return constructor; } protected FunctionOrValue addConstructorMethorOrValue(Class klass, ClassMirror classMirror, MethodMirror ctor, Constructor constructor, boolean isNativeHeader) { boolean isCeylon = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null; if (ctor.getAnnotation(CEYLON_ENUMERATED_ANNOTATION) != null) { klass.setEnumerated(true); Value v = new Value(); v.setName(constructor.getName()); v.setType(constructor.getType()); v.setContainer(klass); v.setScope(klass); v.setUnit(klass.getUnit()); v.setVisibleScope(constructor.getVisibleScope()); // read annotations from the getter method setNonLazyDeclarationProperties(v, ctor, ctor, classMirror, isCeylon); setAnnotations(v, ctor, isNativeHeader); klass.addMember(v); return v; } else { setParameters(constructor, classMirror, ctor, true, klass, false); klass.setConstructors(true); Function f = new Function(); f.setName(constructor.getName()); f.setType(constructor.getType()); f.addParameterList(constructor.getParameterList()); f.setContainer(klass); f.setScope(klass); f.setUnit(klass.getUnit()); f.setVisibleScope(constructor.getVisibleScope()); // read annotations from the constructor setNonLazyDeclarationProperties(f, ctor, ctor, classMirror, isCeylon); setAnnotations(f, ctor, isNativeHeader); klass.addMember(f); return f; } } private boolean isMethodOverloaded(List methodMirrors, boolean isCeylon) { // it's overloaded if we have more than one method (non constructor/value) boolean one = false; for (MethodMirror methodMirror : methodMirrors) { // same tests as in complete(ClassOrInterface klass, ClassMirror classMirror) if(methodMirror.isConstructor() || isInstantiator(methodMirror) || isGetter(methodMirror) // Don't reject setters because we may have added those that // did not match the getters or for coercion || isHashAttribute(methodMirror) || isStringAttribute(methodMirror) || methodMirror.getName().equals("hash") || methodMirror.getName().equals("string")){ break; } // if we're overloading a supertype method too if(isOverloadingMethod(methodMirror)) return true; if(!isCeylon && isCoercedMethod(methodMirror)) return true; if(one) return true; one = true; } return false; } private void collectMethods(List methodMirrors, Map> methods, boolean isCeylon, boolean isFromJDK) { for(MethodMirror methodMirror : methodMirrors){ // We skip members marked with @Ignore if(methodMirror.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null) continue; if(skipPrivateMember(methodMirror)) continue; if(methodMirror.isStaticInit()) continue; // these are not relevant for our caller if(methodMirror.isConstructor() || isInstantiator(methodMirror)) { continue; } // FIXME: temporary, because some private classes from the jdk are // referenced in private methods but not available if(isFromJDK && !methodMirror.isPublic() && !methodMirror.isProtected()) continue; String methodName = methodMirror.getName(); List homonyms = methods.get(methodName); if (homonyms == null) { homonyms = new LinkedList(); methods.put(methodName, homonyms); } homonyms.add(methodMirror); } } private boolean skipPrivateMember(AccessibleMirror mirror) { return !mirror.isPublic() && !mirror.isProtected() && !mirror.isDefaultAccess() && !needsPrivateMembers(); } private void addLocalDeclarations(LocalDeclarationContainer container, ClassMirror classContainerMirror, AnnotatedMirror annotatedMirror) { if(!needsLocalDeclarations()) return; AnnotationMirror annotation = annotatedMirror.getAnnotation(CEYLON_LOCAL_DECLARATIONS_ANNOTATION); if(annotation == null) return; List values = getAnnotationStringValues(annotation, "value"); String parentClassName = classContainerMirror.getQualifiedName(); Package pkg = ModelUtil.getPackageContainer(container); Module module = pkg.getModule(); for(String scope : values){ // assemble the name with the parent String name; if(scope.startsWith("::")){ // interface pulled to toplevel name = pkg.getNameAsString() + "." + scope.substring(2); }else{ name = parentClassName; name += "$" + scope; } Declaration innerDecl = convertToDeclaration(module, (Declaration)container, name, DeclarationType.TYPE); if(innerDecl == null) throw new ModelResolutionException("Failed to load local type " + name + " for outer type " + container.getQualifiedNameString()); } } private boolean isInstantiator(MethodMirror methodMirror) { return methodMirror.getName().endsWith("$aliased$"); } private boolean isFromJDK(ClassMirror classMirror) { String pkgName = unquotePackageName(classMirror.getPackage()); return jdkProvider.isJDKPackage(pkgName); } private void setAnnotations(Annotated annotated, AnnotatedMirror classMirror, boolean isNativeHeader) { if (classMirror.getAnnotation(CEYLON_ANNOTATIONS_ANNOTATION) != null) { // If the class has @Annotations then use it (in >=1.2 only ceylon.language does) Long mods = (Long)getAnnotationValue(classMirror, CEYLON_ANNOTATIONS_ANNOTATION, "modifiers"); if (mods != null) { // If there is a modifiers value then use it to load the modifiers for (LanguageAnnotation mod : LanguageAnnotation.values()) { if (mod.isModifier()) { if ((mod.mask & mods) != 0) { annotated.getAnnotations().addAll(mod.makeFromCeylonAnnotation(null)); } } } } // Load anything else the long way, reading the @Annotation(name=...) List annotations = getAnnotationArrayValue(classMirror, CEYLON_ANNOTATIONS_ANNOTATION); if(annotations != null) { for(AnnotationMirror annotation : annotations){ annotated.getAnnotations().add(readModelAnnotation(annotation)); } } } else { // If the class lacks @Annotations then set the modifier annotations // according to the presence of @Shared$annotation etc for (LanguageAnnotation mod : LanguageAnnotation.values()) { if (classMirror.getAnnotation(mod.annotationType) != null) { annotated.getAnnotations().addAll(mod.makeFromCeylonAnnotation(classMirror.getAnnotation(mod.annotationType))); } } // Hack for anonymous classes where the getter method has the annotations, // but the typechecker wants them on the Class model. if ((annotated instanceof Class) && ((Class)annotated).isAnonymous()) { Class clazz = (Class)annotated; Declaration objectValue = clazz.getContainer().getDirectMember(clazz.getName(), null, false); if (objectValue != null) { annotated.getAnnotations().addAll(objectValue.getAnnotations()); } } } boolean hasCeylonDeprecated = false; for(Annotation a : annotated.getAnnotations()) { if (a.getName().equals("deprecated")) { hasCeylonDeprecated = true; break; } } // Add a ceylon deprecated("") if it's annotated with java.lang.Deprecated // and doesn't already have the ceylon annotation if (classMirror.getAnnotation(JAVA_DEPRECATED_ANNOTATION) != null) { if (!hasCeylonDeprecated) { Annotation modelAnnotation = new Annotation(); modelAnnotation.setName("deprecated"); modelAnnotation.getPositionalArguments().add(""); annotated.getAnnotations().add(modelAnnotation); hasCeylonDeprecated = true; } } if (annotated instanceof Declaration && !((Declaration)annotated).getNativeBackends().none()) { // Do nothing : // it has already been managed when in the makeLazyXXX() function } else { manageNativeBackend(annotated, classMirror, isNativeHeader); } } private void manageNativeBackend(Annotated annotated, AnnotatedMirror mirror, boolean isNativeHeader) { if (mirror == null) return; // Set "native" annotation @SuppressWarnings("unchecked") List nativeBackends = (List)getAnnotationValue(mirror, CEYLON_LANGUAGE_NATIVE_ANNOTATION, "backends"); if (nativeBackends != null) { Backends backends = Backends.fromAnnotations(nativeBackends); if (isNativeHeader) { backends = Backends.HEADER; } else if (backends.header()) { // Elements in the class file marked `native("")` are actually // default implementations taken from the header that were // copied to the output, so here we reset them to `native("jvm")` backends = Backends.JAVA; } if (annotated instanceof Declaration) { Declaration decl = (Declaration)annotated; decl.setNativeBackends(backends); if (isNativeHeader) { List al = new ArrayList(1); setOverloads(decl, al); } } else if (annotated instanceof Module) { ((Module)annotated).setNativeBackends(backends); } } else { // Mark native Classes and Interfaces as well, but don't deal with overloads and such if (annotated instanceof LazyClass && !((LazyClass)annotated).isCeylon() || annotated instanceof LazyInterface && !((LazyInterface)annotated).isCeylon()) { ((Declaration)annotated).setNativeBackends(Backend.Java.asSet()); } } } protected boolean isDeprecated(AnnotatedMirror classMirror){ if (classMirror.getAnnotation(JAVA_DEPRECATED_ANNOTATION) != null) return true; if (classMirror.getAnnotation(CEYLON_ANNOTATIONS_ANNOTATION) != null) { // Load anything else the long way, reading the @Annotation(name=...) List annotations = getAnnotationArrayValue(classMirror, CEYLON_ANNOTATIONS_ANNOTATION); if(annotations != null) { for(AnnotationMirror annotation : annotations){ String name = (String) annotation.getValue(); if(name != null && name.equals("deprecated")) return true; } } return false; } else { // If the class lacks @Annotations then set the modifier annotations // according to the presence of @Shared$annotation etc return classMirror.getAnnotation(LanguageAnnotation.DEPRECATED.annotationType) != null; } } public static List getOverloads(Declaration decl) { if (decl instanceof Function) { return ((Function)decl).getOverloads(); } else if (decl instanceof Value) { return ((Value)decl).getOverloads(); } else if (decl instanceof Class) { return ((Class)decl).getOverloads(); } return null; } public static void setOverloads(Declaration decl, List overloads) { if (decl instanceof Function) { ((Function)decl).setOverloads(overloads); } else if (decl instanceof Value) { ((Value)decl).setOverloads(overloads); } else if (decl instanceof Class) { ((Class)decl).setOverloads(overloads); } } private Annotation readModelAnnotation(AnnotationMirror annotation) { Annotation modelAnnotation = new Annotation(); modelAnnotation.setName((String) annotation.getValue()); @SuppressWarnings("unchecked") List arguments = (List) annotation.getValue("arguments"); if(arguments != null){ modelAnnotation.getPositionalArguments().addAll(arguments); }else{ @SuppressWarnings("unchecked") List namedArguments = (List) annotation.getValue("namedArguments"); if(namedArguments != null){ for(AnnotationMirror namedArgument : namedArguments){ String argName = (String) namedArgument.getValue("name"); String argValue = (String) namedArgument.getValue("value"); modelAnnotation.getNamedArguments().put(argName, argValue); } } } return modelAnnotation; } private void addInnerClasses(ClassOrInterface klass, ClassMirror classMirror) { AnnotationMirror membersAnnotation = classMirror.getAnnotation(CEYLON_MEMBERS_ANNOTATION); if(membersAnnotation == null) addInnerClassesFromMirror(klass, classMirror); else addInnerClassesFromAnnotation(klass, membersAnnotation); } private void addInnerClassesFromAnnotation(ClassOrInterface klass, AnnotationMirror membersAnnotation) { @SuppressWarnings("unchecked") List members = (List) membersAnnotation.getValue(); for(AnnotationMirror member : members){ TypeMirror javaClassMirror = (TypeMirror)member.getValue("klass"); String javaClassName; // void.class is the default value, I guess it's a primitive? if(javaClassMirror != null && !javaClassMirror.isPrimitive()){ javaClassName = javaClassMirror.getQualifiedName(); }else{ // we get the class name as a string String name = (String)member.getValue("javaClassName"); ClassMirror container = null; if(klass instanceof LazyClass){ container = ((LazyClass) klass).classMirror; }else if(klass instanceof LazyInterface){ if(((LazyInterface) klass).isCeylon()) container = ((LazyInterface) klass).companionClass; else container = ((LazyInterface) klass).classMirror; } if(container == null) throw new ModelResolutionException("Unknown container type: " + klass + " when trying to load inner class " + name); javaClassName = container.getQualifiedName()+"$"+name; } Declaration innerDecl = convertToDeclaration(ModelUtil.getModuleContainer(klass), klass, javaClassName, DeclarationType.TYPE); if(innerDecl == null) throw new ModelResolutionException("Failed to load inner type " + javaClassName + " for outer type " + klass.getQualifiedNameString()); if(shouldLinkNatives(innerDecl)) { initNativeHeaderMember(innerDecl); } } } /** * Allows subclasses to do something to the class name */ protected String assembleJavaClass(String javaClass, String packageName) { return javaClass; } private void addInnerClassesFromMirror(ClassOrInterface klass, ClassMirror classMirror) { boolean isJDK = isFromJDK(classMirror); Module module = ModelUtil.getModule(klass); for(ClassMirror innerClass : classMirror.getDirectInnerClasses()){ // We skip members marked with @Ignore if(innerClass.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null) continue; // We skip anonymous inner classes if(innerClass.isAnonymous()) continue; // We skip private classes, otherwise the JDK has a ton of unresolved things if(isJDK && !innerClass.isPublic()) continue; // convert it convertToDeclaration(module, klass, innerClass, DeclarationType.TYPE); // no need to set its container as that's now handled by convertToDeclaration } } private Function addMethod(ClassOrInterface klass, MethodMirror methodMirror, ClassMirror classMirror, boolean isCeylon, boolean isOverloaded, boolean isNativeHeader, boolean isCoercedMethod) { JavaMethod method = new JavaMethod(methodMirror); String methodName = methodMirror.getName(); method.setCoercionPoint(isCoercedMethod); method.setContainer(klass); method.setScope(klass); method.setRealName(methodName); method.setUnit(klass.getUnit()); method.setOverloaded(isOverloaded); method.setVariadic(methodMirror.isVariadic()); Type type = null; try{ setMethodOrValueFlags(klass, methodMirror, method, isCeylon); }catch(ModelResolutionException x){ // collect an error in its type type = logModelResolutionException(x, klass, "method '"+methodMirror.getName()+"' (checking if it is an overriding method)"); } if(methodName.equals("hash") || methodName.equals("string")) method.setName(methodName+"_method"); else{ String ceylonName = JvmBackendUtil.strip(methodName, isCeylon, method.isShared()); if(!isCeylon && !JvmBackendUtil.isInitialLowerCase(ceylonName)) ceylonName = NamingBase.getJavaBeanName(ceylonName); method.setName(ceylonName); } method.setDefaultedAnnotation(methodMirror.isDefault()); // type params first try{ setTypeParameters(method, methodMirror, isCeylon); }catch(ModelResolutionException x){ if(type == null){ type = logModelResolutionException(x, klass, "method '"+methodMirror.getName()+"' (loading type parameters)"); } } Module module = ModelUtil.getModuleContainer(method); // and its return type // do not log an additional error if we had one from checking if it was overriding if(type == null) type = obtainType(methodMirror.getReturnType(), methodMirror, method, module, VarianceLocation.COVARIANT, "method '"+methodMirror.getName()+"'", klass); switch(getUncheckedNullPolicy(isCeylon, methodMirror.getReturnType(), methodMirror)){ case Optional: if(!isCeylon){ type = makeOptionalTypePreserveUnderlyingType(type, module); } break; case UncheckedNull: method.setUncheckedNullType(true); break; } if (type.isCached()) { type = type.clone(); } method.setType(type); // now its parameters if(isEqualsMethod(methodMirror)) setEqualsParameters(method, methodMirror); else setParameters(method, classMirror, methodMirror, isCeylon, klass, isCoercedMethod); type.setRaw(isRaw(module, methodMirror.getReturnType())); markDeclaredVoid(method, methodMirror); markUnboxed(method, methodMirror, methodMirror.getReturnType()); markSmall(method, methodMirror.getReturnType()); markTypeErased(method, methodMirror, methodMirror.getReturnType()); markUntrustedType(method, methodMirror, methodMirror.getReturnType()); method.setDeprecated(isDeprecated(methodMirror)); setAnnotations(method, methodMirror, isNativeHeader); klass.addMember(method); ModelUtil.setVisibleScope(method); addLocalDeclarations(method, classMirror, methodMirror); return method; } private List getSignature(Declaration decl) { List result = null; if (decl instanceof Functional) { Functional func = (Functional)decl; if (func.getParameterLists().size() > 0) { List params = func.getFirstParameterList().getParameters(); result = new ArrayList(params.size()); for (Parameter p : params) { result.add(p.getType()); } } } return result; } protected boolean isStartOfJavaBeanPropertyName(int codepoint){ return (codepoint == Character.toUpperCase(codepoint)) || codepoint == '_'; } private boolean isNonGenericMethod(MethodMirror methodMirror){ return !methodMirror.isConstructor() && methodMirror.getTypeParameters().isEmpty(); } private boolean allReifiedTypeParameters(List list) { for (VariableMirror v : list) { if ("com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor".equals(v.getType().getQualifiedName())) { continue; } else { return false; } } return true; } private boolean isGetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror) && !methodMirror.isStatic()) return false; String name = methodMirror.getName(); boolean matchesGet = name.length() > 3 && name.startsWith("get") && isStartOfJavaBeanPropertyName(name.codePointAt(3)) && !"getString".equals(name) && !"getHash".equals(name) && !"getEquals".equals(name); boolean matchesIs = name.length() > 2 && name.startsWith("is") && isStartOfJavaBeanPropertyName(name.codePointAt(2)) && !"isString".equals(name) && !"isHash".equals(name) && !"isEquals".equals(name); boolean hasNoParams = methodMirror.getParameters().size() == 0 || (methodMirror.isStatic() && allReifiedTypeParameters(methodMirror.getParameters())); boolean hasNonVoidReturn = (methodMirror.getReturnType().getKind() != TypeKind.VOID); boolean hasBooleanReturn = (methodMirror.getReturnType().getKind() == TypeKind.BOOLEAN); return (matchesGet && hasNonVoidReturn || matchesIs && hasBooleanReturn) && hasNoParams; } private boolean isStringGetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror)) return false; String name = methodMirror.getName(); boolean matchesGet = "getString".equals(name); boolean matchesIs = "isString".equals(name); boolean hasNoParams = methodMirror.getParameters().size() == 0; boolean hasNonVoidReturn = (methodMirror.getReturnType().getKind() != TypeKind.VOID); boolean hasBooleanReturn = (methodMirror.getReturnType().getKind() == TypeKind.BOOLEAN); return (matchesGet && hasNonVoidReturn || matchesIs && hasBooleanReturn) && hasNoParams; } private boolean isHashGetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror)) return false; String name = methodMirror.getName(); boolean matchesGet = "getHash".equals(name); boolean matchesIs = "isHash".equals(name); boolean hasNoParams = methodMirror.getParameters().size() == 0; boolean hasNonVoidReturn = (methodMirror.getReturnType().getKind() != TypeKind.VOID); boolean hasBooleanReturn = (methodMirror.getReturnType().getKind() == TypeKind.BOOLEAN); return (matchesGet && hasNonVoidReturn || matchesIs && hasBooleanReturn) && hasNoParams; } private boolean isSetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror)) return false; String name = methodMirror.getName(); boolean matchesSet = name.length() > 3 && name.startsWith("set") && isStartOfJavaBeanPropertyName(name.codePointAt(3)) && !"setString".equals(name) && !"setHash".equals(name) && !"setEquals".equals(name); boolean hasOneParam = methodMirror.getParameters().size() == 1; boolean hasVoidReturn = (methodMirror.getReturnType().getKind() == TypeKind.VOID); return matchesSet && hasOneParam && hasVoidReturn; } private boolean isStringSetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror)) return false; String name = methodMirror.getName(); boolean matchesSet = name.equals("setString"); boolean hasOneParam = methodMirror.getParameters().size() == 1; boolean hasVoidReturn = (methodMirror.getReturnType().getKind() == TypeKind.VOID); return matchesSet && hasOneParam && hasVoidReturn; } private boolean isHashSetter(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror)) return false; String name = methodMirror.getName(); boolean matchesSet = name.equals("setHash"); boolean hasOneParam = methodMirror.getParameters().size() == 1; boolean hasVoidReturn = (methodMirror.getReturnType().getKind() == TypeKind.VOID); return matchesSet && hasOneParam && hasVoidReturn; } private boolean isHashAttribute(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror) || methodMirror.isStatic()) return false; String name = methodMirror.getName(); boolean matchesName = "hashCode".equals(name); boolean hasNoParams = methodMirror.getParameters().size() == 0; return matchesName && hasNoParams; } private boolean isStringAttribute(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror) || methodMirror.isStatic()) return false; String name = methodMirror.getName(); boolean matchesName = "toString".equals(name); boolean hasNoParams = methodMirror.getParameters().size() == 0; return matchesName && hasNoParams; } private boolean isEqualsMethod(MethodMirror methodMirror) { if(!isNonGenericMethod(methodMirror) || methodMirror.isStatic()) return false; String name = methodMirror.getName(); if(!"equals".equals(name) || methodMirror.getParameters().size() != 1) return false; VariableMirror param = methodMirror.getParameters().get(0); return sameType(param.getType(), OBJECT_TYPE); } private boolean isCoercedMethod(MethodMirror methodMirror) { List parameters = methodMirror.getParameters(); for (int i = 0; i < parameters.size(); i++) { VariableMirror param = parameters.get(i); TypeMirror type = param.getType(); if(methodMirror.isVariadic() && i == parameters.size()-1){ type = type.getComponentType(); } if(isCoercedType(type)) return true; } return false; } private boolean isCoercedType(TypeMirror type) { if(sameType(type, CHAR_SEQUENCE_TYPE)) return true; if(sameType(type, CLASS_TYPE)) return true; if(isFunctionCercion(type)) return true; return false; } private void setEqualsParameters(Function decl, MethodMirror methodMirror) { ParameterList parameters = new ParameterList(); decl.addParameterList(parameters); Parameter parameter = new Parameter(); Value value = new Value(); parameter.setModel(value); value.setInitializerParameter(parameter); value.setUnit(decl.getUnit()); Scope scope = (Scope) decl; value.setContainer(scope); value.setScope(scope); parameter.setName("that"); value.setName("that"); value.setType(getNonPrimitiveType(getLanguageModule(), CEYLON_OBJECT_TYPE, decl, VarianceLocation.INVARIANT)); parameter.setDeclaration((Declaration) decl); parameters.getParameters().add(parameter); decl.addMember(value); } private String getJavaAttributeName(MethodMirror methodMirror) { String name = getAnnotationStringValue(methodMirror, CEYLON_NAME_ANNOTATION); if(name != null) return name; return NamingBase.getJavaAttributeName(methodMirror.getName()); } private Value addValue(ClassOrInterface klass, String ceylonName, FieldMirror fieldMirror, boolean isCeylon, boolean isNativeHeader) { // make sure it's a FieldValue so we can figure it out in the backend Value value = new FieldValue(fieldMirror.getName()); value.setContainer(klass); value.setScope(klass); // use the name annotation if present (used by Java arrays) String nameAnnotation = getAnnotationStringValue(fieldMirror, CEYLON_NAME_ANNOTATION); value.setName(nameAnnotation != null ? nameAnnotation : ceylonName); value.setUnit(klass.getUnit()); value.setShared(fieldMirror.isPublic() || fieldMirror.isProtected() || fieldMirror.isDefaultAccess()); value.setProtectedVisibility(fieldMirror.isProtected()); value.setPackageVisibility(fieldMirror.isDefaultAccess()); value.setStatic(fieldMirror.isStatic()); setDeclarationAliases(value, fieldMirror); // field can't be abstract or interface, so not formal // can we override fields? good question. Not really, but from an external point of view? // FIXME: figure this out: (default) // FIXME: for the same reason, can it be an overriding field? (actual) value.setVariable(!fieldMirror.isFinal()); // figure out if it's an enum subtype in a final static field if(fieldMirror.getType().getKind() == TypeKind.DECLARED && fieldMirror.getType().getDeclaredClass() != null && fieldMirror.getType().getDeclaredClass().isEnum() && fieldMirror.isFinal() && fieldMirror.isStatic()) value.setEnumValue(true); Module module = ModelUtil.getModuleContainer(klass); Type type = obtainType(fieldMirror.getType(), fieldMirror, klass, module, VarianceLocation.INVARIANT, "field '"+value.getName()+"'", klass); if (type.isCached()) { type = type.clone(); } if (value.isEnumValue()) { Class enumValueType = new Class(); enumValueType.setValueConstructor(true); enumValueType.setJavaEnum(true); enumValueType.setAnonymous(true); enumValueType.setExtendedType(type); Scope scope = value.getContainer(); enumValueType.setContainer(scope); enumValueType.setScope(scope); enumValueType.setDeprecated(value.isDeprecated()); enumValueType.setName(value.getName()); enumValueType.setFinal(true); enumValueType.setUnit(value.getUnit()); enumValueType.setStatic(value.isStatic()); value.setType(enumValueType.getType()); value.setUncheckedNullType(false); } else { switch(getUncheckedNullPolicy(isCeylon, fieldMirror.getType(), fieldMirror)){ case Optional: if(!isCeylon){ type = makeOptionalTypePreserveUnderlyingType(type, module); } break; case UncheckedNull: value.setUncheckedNullType(true); break; } value.setType(type); } type.setRaw(isRaw(module, fieldMirror.getType())); markUnboxed(value, null, fieldMirror.getType()); markSmall(value, fieldMirror.getType()); markTypeErased(value, fieldMirror, fieldMirror.getType()); markUntrustedType(value, fieldMirror, fieldMirror.getType()); value.setDeprecated(isDeprecated(fieldMirror)); setAnnotations(value, fieldMirror, isNativeHeader); klass.addMember(value); ModelUtil.setVisibleScope(value); return value; } private boolean isRaw(Module module, TypeMirror type) { // dirty hack to get rid of bug where calling type.isRaw() on a ceylon type we are going to compile would complete() it, which // would try to parse its file. For ceylon types we don't need the class file info we can query it // See https://github.com/ceylon/ceylon-compiler/issues/1085 switch(type.getKind()){ case ARRAY: // arrays are never raw case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case ERROR: case FLOAT: case INT: case LONG: case NULL: case SHORT: case TYPEVAR: case VOID: case WILDCARD: return false; case DECLARED: ClassMirror klass = type.getDeclaredClass(); if(klass.isJavaSource()){ // I suppose this should work return type.isRaw(); } List path = new LinkedList(); String pkgName = klass.getPackage().getQualifiedName(); String unquotedPkgName = unquotePackageName(klass.getPackage()); String qualifiedName = klass.getQualifiedName(); String relativeName = pkgName.isEmpty() ? qualifiedName : qualifiedName.substring(pkgName.length()+1); for(String name : relativeName.split("[\\$\\.]")){ if(!name.isEmpty()){ path.add(name); } } if(path.size() > 1){ // find the proper class mirror for the container klass = loadClass(module, pkgName, new StringBuilder(pkgName) .append('.') .append(path.get(0)).toString()); if(klass == null) return false; } if(!path.isEmpty() && klass.isLoadedFromSource()){ // we need to find its model Scope scope = packagesByName.get(cacheKeyByModule(module, unquotedPkgName)); if(scope == null) return false; for(String name : path){ Declaration decl = scope.getDirectMember(name, null, false); if(decl == null) return false; // if we get a value, we want its type if(JvmBackendUtil.isValue(decl) && ((Value)decl).getTypeDeclaration().getName().equals(name)) decl = ((Value)decl).getTypeDeclaration(); if(decl instanceof TypeDeclaration == false) return false; scope = (TypeDeclaration)decl; } TypeDeclaration typeDecl = (TypeDeclaration) scope; return !typeDecl.getTypeParameters().isEmpty() && type.getTypeArguments().isEmpty(); } try{ return type.isRaw(); }catch(Exception x){ // ignore this exception, it's likely to be due to missing module imports and an unknown type and // it will be logged somewhere else return false; } default: return false; } } private JavaBeanValue addValue(ClassOrInterface klass, MethodMirror methodMirror, String methodName, boolean isCeylon, boolean isNativeHeader) { JavaBeanValue value = new JavaBeanValue(methodMirror); value.setGetterName(methodMirror.getName()); value.setContainer(klass); value.setScope(klass); value.setUnit(klass.getUnit()); Type type = null; try{ setMethodOrValueFlags(klass, methodMirror, value, isCeylon); }catch(ModelResolutionException x){ // collect an error in its type type = logModelResolutionException(x, klass, "getter '"+methodName+"' (checking if it is an overriding method"); } value.setName(JvmBackendUtil.strip(methodName, isCeylon, value.isShared())); Module module = ModelUtil.getModuleContainer(klass); // do not log an additional error if we had one from checking if it was overriding if(type == null) type = obtainType(methodMirror.getReturnType(), methodMirror, klass, module, VarianceLocation.INVARIANT, "getter '"+methodName+"'", klass); if (type.isCached()) { type = type.clone(); } // special case for hash attributes which we want to pretend are of type long internally if(value.isShared() && methodName.equals("hash")) type.setUnderlyingType("long"); switch(getUncheckedNullPolicy(isCeylon, methodMirror.getReturnType(), methodMirror)){ case Optional: if(!isCeylon){ type = makeOptionalTypePreserveUnderlyingType(type, module); } break; case UncheckedNull: value.setUncheckedNullType(true); break; } value.setType(type); type.setRaw(isRaw(module, methodMirror.getReturnType())); markUnboxed(value, methodMirror, methodMirror.getReturnType()); markSmall(value, methodMirror.getReturnType()); markTypeErased(value, methodMirror, methodMirror.getReturnType()); markUntrustedType(value, methodMirror, methodMirror.getReturnType()); value.setDeprecated(isDeprecated(methodMirror)); setAnnotations(value, methodMirror, isNativeHeader); klass.addMember(value); ModelUtil.setVisibleScope(value); return value; } private enum NullStatus { UncheckedNull, Optional, NonOptional, NoCheck; } private NullStatus getUncheckedNullPolicy(boolean isCeylon, TypeMirror typeMirror, AnnotatedMirror annotatedMirror) { if(!isCeylon){ if(typeMirror.isPrimitive()) return NullStatus.NonOptional; for(String name : annotatedMirror.getAnnotationNames()){ String lcName = name.toLowerCase(); if(lcName.endsWith(".nullable")){ return NullStatus.Optional; } if(lcName.endsWith(".notnull") || lcName.endsWith(".nonnull")){ return NullStatus.NonOptional; } } } Boolean unchecked = getAnnotationBooleanValue(annotatedMirror, CEYLON_TYPE_INFO_ANNOTATION, "uncheckedNull"); if(unchecked != null) return unchecked.booleanValue() ? NullStatus.UncheckedNull : NullStatus.NonOptional; return isCeylon ? NullStatus.NoCheck : NullStatus.UncheckedNull; } private void setMethodOrValueFlags(final ClassOrInterface klass, final MethodMirror methodMirror, final FunctionOrValue decl, boolean isCeylon) { decl.setShared(methodMirror.isPublic() || methodMirror.isProtected() || methodMirror.isDefaultAccess()); decl.setProtectedVisibility(methodMirror.isProtected()); decl.setPackageVisibility(methodMirror.isDefaultAccess()); setDeclarationAliases(decl, methodMirror); if(decl instanceof Value){ setValueTransientLateFlags((Value)decl, methodMirror, isCeylon); } if(// for class members we rely on abstract bit (klass instanceof Class && methodMirror.isAbstract()) // For Ceylon interfaces we rely on annotation || methodMirror.getAnnotation(CEYLON_LANGUAGE_FORMAL_ANNOTATION) != null) { decl.setFormal(true); } else if(// for class members we rely on abstract bit (klass instanceof Interface && !((LazyInterface)klass).isCeylon())) { // In Java 8, you can have static and default methods in interfaces and for some reason they have their // abstract bit set, but we don't want that in our model decl.setFormal(methodMirror.isAbstract() && !methodMirror.isDefaultMethod() && !methodMirror.isStatic()); decl.setDefault(methodMirror.isDefaultMethod()); } else { if (// for class members we rely on final/static bits (klass instanceof Class && !klass.isFinal() // a final class necessarily has final members && !(methodMirror.isFinal() || methodMirror.getAnnotation(CEYLON_FINAL_ANNOTATION) != null) && !methodMirror.isStatic()) // Java interfaces are never final || (klass instanceof Interface && !((LazyInterface)klass).isCeylon()) // For Ceylon interfaces we rely on annotation || methodMirror.getAnnotation(CEYLON_LANGUAGE_DEFAULT_ANNOTATION) != null){ decl.setDefault(true); } } decl.setStatic(methodMirror.isStatic() && methodMirror.getAnnotation(CEYLON_ENUMERATED_ANNOTATION) == null); decl.setActualCompleter(this); } @Override public void completeActual(Declaration decl){ Scope container = decl.getContainer(); if(container instanceof ClassOrInterface){ ClassOrInterface klass = (ClassOrInterface) container; decl.setRefinedDeclaration(decl); // we never consider Interface and other stuff, since we never register the actualCompleter for them if(decl instanceof Class){ // Java member classes are never actual if(!JvmBackendUtil.isCeylon((Class)decl)) return; // we already set the actual bit for member classes, we just need the refined decl if(decl.isActual()){ Declaration refined = klass.getRefinedMember(decl.getName(), getSignature(decl), klass.isVariadic()); decl.setRefinedDeclaration(refined); } }else{ // Function or Value MethodMirror methodMirror; if(decl instanceof JavaBeanValue) methodMirror = ((JavaBeanValue) decl).mirror; else if(decl instanceof JavaMethod) methodMirror = ((JavaMethod) decl).mirror; else throw new ModelResolutionException("Unknown type of declaration: "+decl+": "+decl.getClass().getName()); decl.setRefinedDeclaration(decl); // For Ceylon interfaces we rely on annotation if(klass instanceof LazyInterface && ((LazyInterface)klass).isCeylon()){ boolean actual = methodMirror.getAnnotation(CEYLON_LANGUAGE_ACTUAL_ANNOTATION) != null; decl.setActual(actual); if(actual){ Declaration refined = klass.getRefinedMember(decl.getName(), getSignature(decl), decl.isVariadic()); decl.setRefinedDeclaration(refined); } }else{ if(isOverridingMethod(methodMirror)){ Declaration refined = klass.getRefinedMember(decl.getName(), getSignature(decl), decl.isVariadic()); if(refined instanceof FieldValue == false){ decl.setActual(true); decl.setRefinedDeclaration(refined); } } } // now that we know the refined declaration, we can check for reified type param support // for Ceylon methods if(decl instanceof JavaMethod && JvmBackendUtil.isCeylon(klass)){ if(!methodMirror.getTypeParameters().isEmpty() // because this requires the refined decl, we defer this check until we've set it, to not trigger // lazy loading just to check. && JvmBackendUtil.supportsReified(decl)){ checkReifiedTypeDescriptors(methodMirror.getTypeParameters().size(), container.getQualifiedNameString(), methodMirror, false); } } } } completeVariable(decl); } private void completeVariable(Declaration value) { if (value instanceof JavaBeanValue) { Declaration refined = value != null ? value.getRefinedDeclaration() : null; if (refined instanceof Value && ((Value)refined).isVariable() && value instanceof Value && !((Value)value).isVariable()) { ((Value)value).setVariable(true); } } } private void setValueTransientLateFlags(Value decl, MethodMirror methodMirror, boolean isCeylon) { if(isCeylon) decl.setTransient(methodMirror.getAnnotation(CEYLON_TRANSIENT_ANNOTATION) != null); else // all Java getters are transient, fields are not decl.setTransient(decl instanceof FieldValue == false); decl.setLate(methodMirror.getAnnotation(CEYLON_LANGUAGE_LATE_ANNOTATION) != null); } private void setExtendedType(ClassOrInterface klass, ClassMirror classMirror) { // look at its super type TypeMirror superClass = classMirror.getSuperclass(); Type extendedType; if(klass instanceof Interface){ // interfaces need to have their superclass set to Object if(superClass == null || superClass.getKind() == TypeKind.NONE) extendedType = getNonPrimitiveType(getLanguageModule(), CEYLON_OBJECT_TYPE, klass, VarianceLocation.INVARIANT); else extendedType = getNonPrimitiveType(ModelUtil.getModule(klass), superClass, klass, VarianceLocation.INVARIANT); }else if(klass instanceof Class && ((Class) klass).isOverloaded()){ // if the class is overloaded we already have it stored extendedType = klass.getExtendedType(); }else{ String className = classMirror.getQualifiedName(); String superClassName = superClass == null ? null : superClass.getQualifiedName(); if(className.equals("ceylon.language.Anything")){ // ceylon.language.Anything has no super type extendedType = null; }else if(className.equals("java.lang.Object")){ // we pretend its superclass is something else, but note that in theory we shouldn't // be seeing j.l.Object at all due to unerasure extendedType = getNonPrimitiveType(getLanguageModule(), CEYLON_BASIC_TYPE, klass, VarianceLocation.INVARIANT); }else{ // read it from annotation first String annotationSuperClassName = getAnnotationStringValue(classMirror, CEYLON_CLASS_ANNOTATION, "extendsType"); if(annotationSuperClassName != null && !annotationSuperClassName.isEmpty()){ extendedType = decodeType(annotationSuperClassName, klass, ModelUtil.getModuleContainer(klass), "extended type"); }else{ // read it from the Java super type // now deal with type erasure, avoid having Object as superclass if("java.lang.Object".equals(superClassName)){ extendedType = getNonPrimitiveType(getLanguageModule(), CEYLON_BASIC_TYPE, klass, VarianceLocation.INVARIANT); } else if(superClass != null){ try{ extendedType = getNonPrimitiveType(ModelUtil.getModule(klass), superClass, klass, VarianceLocation.INVARIANT); }catch(ModelResolutionException x){ extendedType = logModelResolutionException(x, klass, "Error while resolving extended type of "+klass.getQualifiedNameString()); } }else{ // FIXME: should this be UnknownType? extendedType = null; } } } } if(extendedType != null) klass.setExtendedType(extendedType); } private Type getJavaAnnotationExtendedType(ClassOrInterface klass, ClassMirror classMirror) { TypeDeclaration constrainedAnnotation = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_CONSTRAINED_ANNOTATION_TYPE, klass, DeclarationType.TYPE); AnnotationMirror target = classMirror.getAnnotation("java.lang.annotation.Target"); Set types = new HashSet(); if(target != null){ @SuppressWarnings("unchecked") List values = (List) target.getValue(); for(String value : values){ switch(value){ case "TYPE": TypeDeclaration decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_CLASS_OR_INTERFACE_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_ALIAS_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); break; case "ANNOTATION_TYPE": decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_CLASS_OR_INTERFACE_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); break; case "CONSTRUCTOR": decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_CONSTRUCTOR_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); if (!values.contains("TYPE")) { decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_CLASS_WITH_INIT_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); } break; case "METHOD": // method annotations may be applied to shared members which are turned into getter methods case "PARAMETER": decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_FUNCTION_OR_VALUE_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); break; case "FIELD": case "LOCAL_VARIABLE": decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_VALUE_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); break; case "PACKAGE": decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_PACKAGE_DECLARATION_TYPE, klass, DeclarationType.TYPE); types.add(decl.getType()); break; default: // all other values are ambiguous or have no mapping } } } Module module = ModelUtil.getModuleContainer(klass); Type annotatedType; if(types.size() == 1) annotatedType = types.iterator().next(); else if(types.isEmpty()){ TypeDeclaration decl; if(target == null){ // default is anything decl = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(getLanguageModule(), CEYLON_ANNOTATED_TYPE, klass, DeclarationType.TYPE); }else{ // we either had an empty set which means cannot be used as annotation in Java (only as annotation member) // or that we only had unmappable targets decl = typeFactory.getNothingDeclaration(); } annotatedType = decl.getType(); }else{ List list = new ArrayList(types.size()); list.addAll(types); annotatedType = union(list, getUnitForModule(module)); } Type constrainedType = constrainedAnnotation.appliedType(null, Arrays.asList(klass.getType(), getOptionalType(klass.getType(), module), annotatedType)); return constrainedType; } private void setParameters(Functional decl, ClassMirror classMirror, MethodMirror methodMirror, boolean isCeylon, Scope container, boolean isCoercedMethod) { ParameterList parameters = new ParameterList(); parameters.setNamedParametersSupported(isCeylon); decl.addParameterList(parameters); int parameterCount = methodMirror.getParameters().size(); int parameterIndex = 0; for(VariableMirror paramMirror : methodMirror.getParameters()){ // ignore some parameters if(paramMirror.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null) continue; boolean isLastParameter = parameterIndex == parameterCount - 1; boolean isVariadic = isLastParameter && methodMirror.isVariadic(); String paramName = getAnnotationStringValue(paramMirror, CEYLON_NAME_ANNOTATION); // use whatever param name we find as default if(paramName == null) paramName = paramMirror.getName(); Parameter parameter = new Parameter(); parameter.setName(paramName); TypeMirror typeMirror = paramMirror.getType(); Scope scope = (Scope) decl; Module module = ModelUtil.getModuleContainer(scope); Type type; boolean coercedParameter = false; if(isVariadic){ // possibly make it optional TypeMirror variadicType = typeMirror.getComponentType(); // we pretend it's toplevel because we want to get magic string conversion for variadic methods if(isCoercedMethod && isCoercedType(variadicType)){ type = applyTypeCoercion(variadicType, paramMirror, methodMirror, paramName, (Declaration)decl, module, scope); coercedParameter = true; }else{ type = obtainType(module, variadicType, scope, TypeLocation.TOPLEVEL, VarianceLocation.CONTRAVARIANT); } if(!isCeylon){ // Java parameters are all optional unless primitives or annotated as such if(getUncheckedNullPolicy(isCeylon, variadicType, paramMirror) != NullStatus.NonOptional){ type = makeOptionalTypePreserveUnderlyingType(type, module); } } // turn it into a Sequential type = typeFactory.getSequentialType(type); }else{ if(isCoercedMethod && isCoercedType(typeMirror)){ type = applyTypeCoercion(typeMirror, paramMirror, methodMirror, paramName, (Declaration)decl, module, scope); coercedParameter = true; }else{ type = obtainType(typeMirror, paramMirror, scope, module, VarianceLocation.CONTRAVARIANT, "parameter '"+paramName+"' of method '"+methodMirror.getName()+"'", (Declaration)decl); } if(!isCeylon){ // Java parameters are all optional unless primitives or annotated as such if(getUncheckedNullPolicy(isCeylon, typeMirror, paramMirror) != NullStatus.NonOptional){ type = makeOptionalTypePreserveUnderlyingType(type, module); } } } if (type.isCached()) { type = type.clone(); } type.setRaw(isRaw(ModelUtil.getModuleContainer(container), typeMirror)); FunctionOrValue value = null; boolean lookedup = false; if (isCeylon && decl instanceof Class){ // For a functional parameter to a class, we can just lookup the member value = (FunctionOrValue)((Class)decl).getDirectMember(paramName, null, false); lookedup = value != null; } if (value == null) { // So either decl is not a Class, // or the method or value member of decl is not shared AnnotationMirror functionalParameterAnnotation = paramMirror.getAnnotation(CEYLON_FUNCTIONAL_PARAMETER_ANNOTATION); if (functionalParameterAnnotation != null) { // A functional parameter to a method Function method = loadFunctionalParameter((Declaration)decl, paramName, type, (String)functionalParameterAnnotation.getValue()); value = method; parameter.setDeclaredAnything(method.isDeclaredVoid()); } else if(coercedParameter && isFunctionCercion(typeMirror)){ Function method = loadFunctionCoercionParameter((Declaration) decl, paramName, typeMirror, module, scope); value = method; parameter.setDeclaredAnything(method.isDeclaredVoid()); } else { // A value parameter to a method value = isCeylon ? new Value() : new JavaParameterValue(); value.setType(type); } value.setContainer(scope); value.setScope(scope); ModelUtil.setVisibleScope(value); value.setUnit(scope.getUnit()); value.setName(paramName); }else{ // Ceylon 1.1 had a bug where TypeInfo for functional parameters included the full CallableType on the method // rather than just the method return type, so we try to detect this and fix it if(value instanceof Function && isCeylon1Dot1(classMirror)){ Type newType = getSimpleCallableReturnType(value.getType()); if(!newType.isUnknown()) value.setType(newType); } } value.setInitializerParameter(parameter); value.setCoercionPoint(coercedParameter); parameter.setModel(value); if(paramMirror.getAnnotation(CEYLON_SEQUENCED_ANNOTATION) != null || isVariadic) parameter.setSequenced(true); if(paramMirror.getAnnotation(CEYLON_DEFAULTED_ANNOTATION) != null) parameter.setDefaulted(true); if (parameter.isSequenced() && // FIXME: store info in Sequenced typeFactory.isNonemptyIterableType(parameter.getType())) { parameter.setAtLeastOne(true); } // unboxed is already set if it's a real method if(!lookedup){ // if it's variadic, consider the array element type (T[] == T...) for boxing rules markUnboxed(value, null, isVariadic ? paramMirror.getType().getComponentType() : paramMirror.getType()); markSmall(value, paramMirror.getType()); } parameter.setDeclaration((Declaration) decl); value.setDeprecated(value.isDeprecated() || isDeprecated(paramMirror)); setAnnotations(value, paramMirror, false); parameters.getParameters().add(parameter); if (!lookedup) { parameter.getDeclaration().getMembers().add(parameter.getModel()); } parameterIndex++; } if (decl instanceof Function) { // Multiple parameter lists AnnotationMirror functionalParameterAnnotation = methodMirror.getAnnotation(CEYLON_FUNCTIONAL_PARAMETER_ANNOTATION); if (functionalParameterAnnotation != null) { parameterNameParser.parseMpl((String)functionalParameterAnnotation.getValue(), ((Function)decl).getType().getFullType(), (Function)decl); } } } @SuppressWarnings("incomplete-switch") private Function loadFunctionCoercionParameter(Declaration decl, String paramName, TypeMirror typeMirror, Module moduleScope, Scope scope) { Function method = new Function(); method.setName(paramName); method.setUnit(decl.getUnit()); try{ FunctionalInterfaceType functionalInterfaceType = getFunctionalInterfaceType(typeMirror); MethodMirror functionalMethod = functionalInterfaceType.getMethod(); Type returnType = obtainType(moduleScope, functionalInterfaceType.getReturnType(), scope, TypeLocation.TOPLEVEL, VarianceLocation.COVARIANT); switch(getUncheckedNullPolicy(false, functionalInterfaceType.getReturnType(), functionalMethod)){ case Optional: case UncheckedNull: returnType = makeOptionalTypePreserveUnderlyingType(returnType, moduleScope); break; } method.setType(returnType); ParameterList pl = new ParameterList(); int count = 0; List functionalParameters = functionalMethod.getParameters(); for(TypeMirror parameterType : functionalInterfaceType.getParameterTypes()){ Type modelParameterType = obtainType(moduleScope, parameterType, scope, TypeLocation.TOPLEVEL, VarianceLocation.CONTRAVARIANT); Parameter p = new Parameter(); Value v = new Value(); String name = "arg" + count++; p.setName(name); v.setName(name); v.setContainer(method); v.setScope(method); p.setModel(v); v.setInitializerParameter(p); // Java parameters are all optional unless primitives or annotated as such switch(getUncheckedNullPolicy(false, parameterType, functionalMethod)){ case Optional: modelParameterType = makeOptionalTypePreserveUnderlyingType(modelParameterType, moduleScope); break; case UncheckedNull: v.setUncheckedNullType(true); break; } v.setType(modelParameterType); pl.getParameters().add(p); method.addMember(v); } method.addParameterList(pl); }catch(ModelResolutionException x){ method.setType(logModelResolutionException(x, scope, "Failure to turn functional interface to Callable type")); } return method; } private boolean isFunctionCercion(TypeMirror type) { if(type.getKind() == TypeKind.DECLARED){ ClassMirror paramClass = type.getDeclaredClass(); return isFunctionalInterfaceWithExceptions(paramClass) != null; } return false; } private Type applyTypeCoercion(TypeMirror type, AnnotatedMirror annotatedMirror, MethodMirror methodMirror, String paramName, Declaration decl, Module module, Scope scope) { if(sameType(type, CHAR_SEQUENCE_TYPE)) return typeFactory.getStringType(); if(sameType(type, CLASS_TYPE)){ // It's much easier to obtain the java.lang.Class<...> type and extract its TP than // just obtain its TP, because wildcards are dealt with in obtainTypeArguments, not // for "toplevel" wildcards Type classType = obtainType(type, annotatedMirror, scope, module, VarianceLocation.CONTRAVARIANT, "parameter '"+paramName+"' of method '"+methodMirror.getName()+"'", (Declaration)decl); // gracefully give up with the original type in case of errors if(classType.isUnknown() || classType.getTypeArgumentList().isEmpty()) return classType; return typeFactory.getClassOrInterfaceModelType(classType.getTypeArgumentList().get(0)); } return getFunctionalInterfaceAsCallable(module, scope, type); } private Type makeOptionalTypePreserveUnderlyingType(Type type, Module module) { Type optionalType = getOptionalType(type, module); if (optionalType.isCached()) { optionalType = optionalType.clone(); } optionalType.setUnderlyingType(type.getUnderlyingType()); return optionalType; } private boolean isCeylon1Dot1(ClassMirror classMirror) { AnnotationMirror annotation = classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION); if(annotation == null) return false; Integer major = (Integer) annotation.getValue("major"); if(major == null) major = 0; Integer minor = (Integer) annotation.getValue("minor"); if(minor == null) minor = 0; return major == Versions.V1_1_BINARY_MAJOR_VERSION && minor == Versions.V1_1_BINARY_MINOR_VERSION; } private Function loadFunctionalParameter(Declaration decl, String paramName, Type type, String parameterNames) { Function method = new Function(); method.setName(paramName); method.setUnit(decl.getUnit()); if (parameterNames == null || parameterNames.isEmpty()) { // This branch is broken, but it deals with old code which lacked // the encoding of parameter names of functional parameters, so we'll keep it until 1.2 method.setType(getSimpleCallableReturnType(type)); ParameterList pl = new ParameterList(); int count = 0; for (Type pt : getSimpleCallableArgumentTypes(type)) { Parameter p = new Parameter(); Value v = new Value(); String name = "arg" + count++; p.setName(name); v.setName(name); v.setType(pt); v.setContainer(method); v.setScope(method); p.setModel(v); v.setInitializerParameter(p); pl.getParameters().add(p); method.addMember(v); } method.addParameterList(pl); } else { try { parameterNameParser.parse(parameterNames, type, method); } catch(Exception x){ logError(x.getClass().getSimpleName() + " while parsing parameter names of "+decl+": " + x.getMessage()); return method; } } return method; } List getSimpleCallableArgumentTypes(Type type) { if(type != null && type.isClassOrInterface() && type.getDeclaration().getQualifiedNameString().equals(CEYLON_LANGUAGE_CALLABLE_TYPE_NAME) && type.getTypeArgumentList().size() >= 2) return flattenCallableTupleType(type.getTypeArgumentList().get(1)); return Collections.emptyList(); } List flattenCallableTupleType(Type tupleType) { if(tupleType != null && tupleType.isClassOrInterface()){ String declName = tupleType.getDeclaration().getQualifiedNameString(); if(declName.equals(CEYLON_LANGUAGE_TUPLE_TYPE_NAME)){ List tal = tupleType.getTypeArgumentList(); if(tal.size() >= 3){ List ret = flattenCallableTupleType(tal.get(2)); ret.add(0, tal.get(1)); return ret; } }else if(declName.equals(CEYLON_LANGUAGE_EMPTY_TYPE_NAME)){ return new LinkedList(); }else if(declName.equals(CEYLON_LANGUAGE_SEQUENTIAL_TYPE_NAME)){ LinkedList ret = new LinkedList(); ret.add(tupleType); return ret; }else if(declName.equals(CEYLON_LANGUAGE_SEQUENCE_TYPE_NAME)){ LinkedList ret = new LinkedList(); ret.add(tupleType); return ret; } } return Collections.emptyList(); } Type getSimpleCallableReturnType(Type type) { if(type != null && type.isClassOrInterface() && type.getDeclaration().getQualifiedNameString().equals(CEYLON_LANGUAGE_CALLABLE_TYPE_NAME) && !type.getTypeArgumentList().isEmpty()) return type.getTypeArgumentList().get(0); return newUnknownType(); } private Type getOptionalType(Type type, Module moduleScope) { if(type.isUnknown()) return type; // we do not use Unit.getOptionalType because it causes lots of lazy loading that ultimately triggers the typechecker's // infinite recursion loop List list = new ArrayList(2); list.add(typeFactory.getNullType()); list.add(type); return union(list, getUnitForModule(moduleScope)); } private Type logModelResolutionError(Scope container, String message) { return logModelResolutionException((String)null, container, message); } private Type logModelResolutionException(ModelResolutionException x, Scope container, String message) { String exceptionMessage = x.getMessage(); Throwable causer = x; while(causer.getCause() != null){ exceptionMessage = exceptionMessage + " caused by: " + causer.getCause(); causer = causer.getCause(); } return logModelResolutionException(exceptionMessage, container, message); } private Type logModelResolutionException(final String exceptionMessage, Scope container, final String message) { final Module module = ModelUtil.getModuleContainer(container); return logModelResolutionException(exceptionMessage, module, message); } private Type logModelResolutionException(final String exceptionMessage, Module module, final String message) { UnknownType.ErrorReporter errorReporter; if(module != null && !module.isDefaultModule()){ final StringBuilder sb = new StringBuilder(); sb.append("Error while loading the ").append(module.getNameAsString()).append("/").append(module.getVersion()); sb.append(" module:\n "); sb.append(message); if(exceptionMessage != null) sb.append(":\n ").append(exceptionMessage); errorReporter = makeModelErrorReporter(module, sb.toString()); }else if(exceptionMessage == null){ errorReporter = makeModelErrorReporter(message); }else{ errorReporter = makeModelErrorReporter(message+": "+exceptionMessage); } UnknownType ret = new UnknownType(typeFactory); ret.setErrorReporter(errorReporter); return ret.getType(); } /** * To be overridden by subclasses */ protected UnknownType.ErrorReporter makeModelErrorReporter(String message) { return new LogErrorRunnable(this, message); } /** * To be overridden by subclasses */ protected abstract UnknownType.ErrorReporter makeModelErrorReporter(Module module, String message); private static class LogErrorRunnable extends UnknownType.ErrorReporter { private AbstractModelLoader modelLoader; public LogErrorRunnable(AbstractModelLoader modelLoader, String message) { super(message); this.modelLoader = modelLoader; } @Override public void reportError() { modelLoader.logError(getMessage()); } } private void markTypeErased(TypedDeclaration decl, AnnotatedMirror typedMirror, TypeMirror type) { if (BooleanUtil.isTrue(getAnnotationBooleanValue(typedMirror, CEYLON_TYPE_INFO_ANNOTATION, "erased"))) { decl.setTypeErased(true); } else { decl.setTypeErased(sameType(type, OBJECT_TYPE)); } } private void markUntrustedType(TypedDeclaration decl, AnnotatedMirror typedMirror, TypeMirror type) { boolean untrusted = BooleanUtil.isTrue(getAnnotationBooleanValue(typedMirror, CEYLON_TYPE_INFO_ANNOTATION, "untrusted")); if ("com.redhat.ceylon.compiler.java.test.structure.klass::IterableSequence.sequence".equals(decl.getQualifiedNameString()) || "ceylon.language::Iterable.sequence".equals(decl.getQualifiedNameString())) { untrusted = true; } decl.setUntrustedType(untrusted); } private void markDeclaredVoid(Function decl, MethodMirror methodMirror) { if (methodMirror.isDeclaredVoid() || BooleanUtil.isTrue(getAnnotationBooleanValue(methodMirror, CEYLON_TYPE_INFO_ANNOTATION, "declaredVoid"))) { decl.setDeclaredVoid(true); } } /*private boolean hasTypeParameterWithConstraints(TypeMirror type) { switch(type.getKind()){ case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT: case INT: case LONG: case SHORT: case VOID: case WILDCARD: return false; case ARRAY: return hasTypeParameterWithConstraints(type.getComponentType()); case DECLARED: for(TypeMirror ta : type.getTypeArguments()){ if(hasTypeParameterWithConstraints(ta)) return true; } return false; case TYPEVAR: TypeParameterMirror typeParameter = type.getTypeParameter(); return typeParameter != null && hasNonErasedBounds(typeParameter); default: return false; } }*/ private void markUnboxed(TypedDeclaration decl, MethodMirror methodMirror, TypeMirror type) { boolean unboxed = false; if(type.isPrimitive() || isPrimitiveArray(type) || sameType(type, STRING_TYPE) || (methodMirror != null && methodMirror.isDeclaredVoid())) { unboxed = true; } decl.setUnboxed(unboxed); } private boolean isPrimitiveArray(TypeMirror type) { if(type.getKind() == TypeKind.ARRAY){ TypeMirror componentType = type.getComponentType(); // byte[][] is not primitive, it's a ObjectArray return componentType.isPrimitive() || sameType(type, STRING_TYPE); } return false; } private void markSmall(FunctionOrValue value, TypeMirror type) { if (!(value.isMember() && value.getName().equals("hash"))) { value.setSmall(sameType(type, PRIM_INT_TYPE) && !value.getType().isCharacter() ||sameType(type, PRIM_FLOAT_TYPE) ||sameType(type, PRIM_CHAR_TYPE)); } } @Override public void complete(final LazyValue value) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); try{ MethodMirror meth = getGetterMethodMirror(value, value.classMirror, value.isToplevel()); if(meth == null || meth.getReturnType() == null){ value.setType(logModelResolutionError(value.getContainer(), "Error while resolving toplevel attribute "+value.getQualifiedNameString()+": getter method missing")); return; } value.setDeprecated(value.isDeprecated() | isDeprecated(meth)); value.setType(obtainType(meth.getReturnType(), meth, null, ModelUtil.getModuleContainer(value.getContainer()), VarianceLocation.INVARIANT, "toplevel attribute", value)); markVariable(value); setValueTransientLateFlags(value, meth, true); setAnnotations(value, meth, value.isNativeHeader()); markUnboxed(value, meth, meth.getReturnType()); markSmall(value, meth.getReturnType()); markTypeErased(value, meth, meth.getReturnType()); TypeMirror setterClass = (TypeMirror) getAnnotationValue(value.classMirror, CEYLON_ATTRIBUTE_ANNOTATION, "setterClass"); // void.class is the default value, I guess it's a primitive? if(setterClass != null && !setterClass.isPrimitive()){ ClassMirror setterClassMirror = setterClass.getDeclaredClass(); value.setVariable(true); SetterWithLocalDeclarations setter = makeSetter(value, setterClassMirror); // adding local scopes should be done last, when we have the setter, because it may be needed by container chain addLocalDeclarations(value, value.classMirror, value.classMirror); addLocalDeclarations(setter, setterClassMirror, setterClassMirror); }else if(value.isToplevel() && value.isTransient() && value.isVariable()){ makeSetter(value, value.classMirror); // all local scopes for getter/setter are declared in the same class // adding local scopes should be done last, when we have the setter, because it may be needed by container chain addLocalDeclarations(value, value.classMirror, value.classMirror); }else{ // adding local scopes should be done last, when we have the setter, because it may be needed by container chain addLocalDeclarations(value, value.classMirror, value.classMirror); } }finally{ timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } } }); } private MethodMirror getGetterMethodMirror(Declaration value, ClassMirror classMirror, boolean toplevel) { MethodMirror meth = null; String getterName; if (toplevel) { // We do this to prevent calling complete() unnecessarily getterName = NamingBase.Unfix.get_.name(); } else { getterName = NamingBase.getGetterName(value); } for (MethodMirror m : classMirror.getDirectMethods()) { // Do not skip members marked with @Ignore, because the getter is supposed to be ignored if (m.getName().equals(getterName) && (!toplevel || m.isStatic()) && m.getParameters().size() == 0) { meth = m; break; } } return meth; } private void markVariable(LazyValue value) { String setterName = NamingBase.getSetterName(value); boolean toplevel = value.isToplevel(); for (MethodMirror m : value.classMirror.getDirectMethods()) { // Do not skip members marked with @Ignore, because the getter is supposed to be ignored if (m.getName().equals(setterName) && (!toplevel || m.isStatic()) && m.getParameters().size() == 1) { value.setVariable(true); } } } private SetterWithLocalDeclarations makeSetter(Value value, ClassMirror classMirror) { SetterWithLocalDeclarations setter = new SetterWithLocalDeclarations(classMirror); Scope scope = value.getContainer(); setter.setContainer(scope); setter.setScope(scope); setter.setType(value.getType()); setter.setName(value.getName()); Parameter p = new Parameter(); p.setHidden(true); Value v = new Value(); v.setType(value.getType()); v.setUnboxed(value.getUnboxed()); v.setInitializerParameter(p); v.setContainer(setter); v.setScope(setter); p.setModel(v); v.setName(setter.getName()); p.setName(setter.getName()); p.setDeclaration(setter); setter.setParameter(p); value.setSetter(setter); setter.setGetter(value); return setter; } @Override public void complete(final LazyFunction method) { synchronizedRun(new Runnable() { @Override public void run() { timer.startIgnore(TIMER_MODEL_LOADER_CATEGORY); try{ MethodMirror meth = getFunctionMethodMirror(method); if(meth == null || meth.getReturnType() == null){ method.setType(logModelResolutionError(method.getContainer(), "Error while resolving toplevel method "+method.getQualifiedNameString()+": static method missing")); return; } // only check the static mod for toplevel classes if(!method.classMirror.isLocalClass() && !meth.isStatic()){ method.setType(logModelResolutionError(method.getContainer(), "Error while resolving toplevel method "+method.getQualifiedNameString()+": method is not static")); return; } method.setDeprecated(method.isDeprecated() | isDeprecated(meth)); // save the method name method.setRealMethodName(meth.getName()); // save the method method.setMethodMirror(meth); // type params first setTypeParameters(method, meth, true); method.setType(obtainType(meth.getReturnType(), meth, method, ModelUtil.getModuleContainer(method), VarianceLocation.COVARIANT, "toplevel method", method)); method.setDeclaredVoid(meth.isDeclaredVoid()); markDeclaredVoid(method, meth); markUnboxed(method, meth, meth.getReturnType()); markSmall(method, meth.getReturnType()); markTypeErased(method, meth, meth.getReturnType()); markUntrustedType(method, meth, meth.getReturnType()); // now its parameters setParameters(method, method.classMirror, meth, true /* toplevel methods are always Ceylon */, method, false); method.setAnnotation(meth.getAnnotation(CEYLON_LANGUAGE_ANNOTATION_ANNOTATION) != null); setAnnotations(method, meth, method.isNativeHeader()); setAnnotationConstructor(method, meth); addLocalDeclarations(method, method.classMirror, method.classMirror); }finally{ timer.stopIgnore(TIMER_MODEL_LOADER_CATEGORY); } } }); } private MethodMirror getFunctionMethodMirror(LazyFunction method) { MethodMirror meth = null; String lookupName = method.getName(); for(MethodMirror m : method.classMirror.getDirectMethods()){ // We skip members marked with @Ignore if(m.getAnnotation(CEYLON_IGNORE_ANNOTATION) != null) continue; if(NamingBase.stripLeadingDollar(m.getName()).equals(lookupName)){ meth = m; break; } } return meth; } // for subclasses protected abstract void setAnnotationConstructor(LazyFunction method, MethodMirror meth); public AnnotationProxyMethod makeInteropAnnotationConstructor(LazyInterface iface, AnnotationProxyClass klass, OutputElement oe, Scope scope){ String ctorName = oe == null ? NamingBase.getJavaBeanName(iface.getName()) : NamingBase.getDisambigAnnoCtorName(iface, oe); AnnotationProxyMethod ctor = new AnnotationProxyMethod(this, klass, oe); ctor.setAnnotationTarget(oe); ctor.setContainer(scope); ctor.setScope(scope); if (!(scope instanceof Package)) { ctor.setStatic(true); } ctor.setAnnotation(true); ctor.setName(ctorName); ctor.setShared(iface.isShared()); Annotation annotationAnnotation2 = new Annotation(); annotationAnnotation2.setName("annotation"); ctor.getAnnotations().add(annotationAnnotation2); ctor.setType(((TypeDeclaration)iface).getType()); ctor.setUnit(iface.getUnit()); return ctor; } /** * For subclasses that provide compilation to Java annotations. */ protected abstract void makeInteropAnnotationConstructorInvocation(AnnotationProxyMethod ctor, AnnotationProxyClass klass, List ctorParams); /** *
     *   annotation class Annotation$Proxy(...) satisfies Annotation {
     *       // a `shared` class parameter for each method of Annotation
     *   }
     * 
* @param iface The model of the annotation @interface * @return The annotation class for the given interface */ public AnnotationProxyClass makeInteropAnnotationClass( LazyInterface iface, Scope scope) { AnnotationProxyClass klass = new AnnotationProxyClass(this, iface); klass.setContainer(scope); klass.setScope(scope); if (!(scope instanceof Package)) { klass.setStatic(true); } klass.setName(iface.getName()+"$Proxy"); klass.setShared(iface.isShared()); klass.setAnnotation(true); Annotation annotationAnnotation = new Annotation(); annotationAnnotation.setName("annotation"); klass.getAnnotations().add(annotationAnnotation); klass.getSatisfiedTypes().add(iface.getType()); klass.setUnit(iface.getUnit()); klass.setScope(scope); return klass; } private Type annotationParameterType(Unit unit, JavaMethod m) { Type type = m.getType(); if (JvmBackendUtil.isJavaArray(type.getDeclaration())) { String name = type.getDeclaration().getQualifiedNameString(); Type elementType; String underlyingType = null; if(name.equals("java.lang::ObjectArray")){ Type eType = type.getTypeArgumentList().get(0); String elementTypeName = eType.getDeclaration().getQualifiedNameString(); if ("java.lang::String".equals(elementTypeName)) { elementType = unit.getStringType(); } else if ("java.lang::Class".equals(elementTypeName) || "java.lang.Class".equals(eType.getUnderlyingType())) { // Two cases because the types // Class[] and Class[] are treated differently by // AbstractModelLoader.obtainType() // TODO Replace with metamodel ClassOrInterface type // once we have support for metamodel references elementType = unit.getAnythingType(); underlyingType = "java.lang.Class"; } else { elementType = eType; if (elementType.isCached()) { elementType = type.clone(); type.getTypeArgumentList().set(0, elementType); } } // TODO Enum elements } else if(name.equals("java.lang::LongArray")) { elementType = unit.getIntegerType(); } else if (name.equals("java.lang::ByteArray")) { elementType = unit.getByteType(); } else if (name.equals("java.lang::ShortArray")) { elementType = unit.getIntegerType(); underlyingType = "short"; } else if (name.equals("java.lang::IntArray")){ elementType = unit.getIntegerType(); underlyingType = "int"; } else if(name.equals("java.lang::BooleanArray")){ elementType = unit.getBooleanType(); } else if(name.equals("java.lang::CharArray")){ elementType = unit.getCharacterType(); underlyingType = "char"; } else if(name.equals("java.lang::DoubleArray")) { elementType = unit.getFloatType(); } else if (name.equals("java.lang::FloatArray")){ elementType = unit.getFloatType(); underlyingType = "float"; } else { throw new RuntimeException(); } if (elementType.isCached()) { elementType = elementType.clone(); } elementType.setUnderlyingType(underlyingType); Type iterableType = unit.getIterableType(elementType); return iterableType; } else if ("java.lang::Class".equals(type.getDeclaration().getQualifiedNameString())) { // Note we lose the upper bound (method had type Class), see #5918 return typeFactory.getClassOrInterfaceDeclarationType(); } else { return type; } } // // Satisfied Types private List getSatisfiedTypesFromAnnotations(AnnotatedMirror symbol) { return getAnnotationArrayValue(symbol, CEYLON_SATISFIED_TYPES_ANNOTATION); } private void setSatisfiedTypes(ClassOrInterface klass, ClassMirror classMirror) { List satisfiedTypes = getSatisfiedTypesFromAnnotations(classMirror); if(satisfiedTypes != null){ klass.getSatisfiedTypes().addAll(getTypesList(satisfiedTypes, klass, ModelUtil.getModuleContainer(klass), "satisfied types", klass.getQualifiedNameString())); }else{ if(classMirror.isAnnotationType()) // this only happens for Java annotations since Ceylon annotations are ignored // turn @Target into a subtype of ConstrainedAnnotation klass.getSatisfiedTypes().add(getJavaAnnotationExtendedType(klass, classMirror)); for(TypeMirror iface : classMirror.getInterfaces()){ // ignore generated interfaces if(sameType(iface, CEYLON_REIFIED_TYPE_TYPE) || sameType(iface, CEYLON_SERIALIZABLE_TYPE) || (classMirror.getAnnotation(CEYLON_CEYLON_ANNOTATION) != null && sameType(iface, JAVA_IO_SERIALIZABLE_TYPE_TYPE))) continue; try{ klass.getSatisfiedTypes().add(getNonPrimitiveType(ModelUtil.getModule(klass), iface, klass, VarianceLocation.INVARIANT)); }catch(ModelResolutionException x){ String classPackageName = unquotePackageName(classMirror.getPackage()); if(jdkProvider.isJDKPackage(classPackageName)){ if(iface.getKind() == TypeKind.DECLARED){ // check if it's a JDK thing ClassMirror ifaceClass = iface.getDeclaredClass(); String ifacePackageName = unquotePackageName(ifaceClass.getPackage()); if(jdkProvider.isJDKPackage(ifacePackageName)){ // just log and ignore it logMissingOracleType(iface.getQualifiedName()); continue; } } } } } } } // // Case Types private List getCaseTypesFromAnnotations(AnnotatedMirror symbol) { return getAnnotationArrayValue(symbol, CEYLON_CASE_TYPES_ANNOTATION); } private String getSelfTypeFromAnnotations(AnnotatedMirror symbol) { return getAnnotationStringValue(symbol, CEYLON_CASE_TYPES_ANNOTATION, "of"); } private void setCaseTypes(ClassOrInterface klass, ClassMirror classMirror) { if (classMirror.isEnum()) { ArrayList caseTypes = new ArrayList(); for (Declaration member : klass.getMembers()) { if (member instanceof FieldValue && ((FieldValue) member).isEnumValue()) { caseTypes.add(((FieldValue)member).getType()); } } klass.setCaseTypes(caseTypes); } else { String selfType = getSelfTypeFromAnnotations(classMirror); Module moduleScope = ModelUtil.getModuleContainer(klass); if(selfType != null && !selfType.isEmpty()){ Type type = decodeType(selfType, klass, moduleScope, "self type"); if(!type.isTypeParameter()){ logError("Invalid type signature for self type of "+klass.getQualifiedNameString()+": "+selfType+" is not a type parameter"); }else{ klass.setSelfType(type); List caseTypes = new LinkedList(); caseTypes.add(type); klass.setCaseTypes(caseTypes); } } else { List caseTypes = getCaseTypesFromAnnotations(classMirror); if(caseTypes != null && !caseTypes.isEmpty()){ klass.setCaseTypes(getTypesList(caseTypes, klass, moduleScope, "case types", klass.getQualifiedNameString())); } } } } private List getTypesList(List caseTypes, Scope scope, Module moduleScope, String targetType, String targetName) { List producedTypes = new LinkedList(); for(String type : caseTypes){ producedTypes.add(decodeType(type, scope, moduleScope, targetType)); } return producedTypes; } // // Type parameters loading @SuppressWarnings("unchecked") private List getTypeParametersFromAnnotations(AnnotatedMirror symbol) { return (List) getAnnotationValue(symbol, CEYLON_TYPE_PARAMETERS); } // from our annotation private void setTypeParametersFromAnnotations(Scope scope, List params, AnnotatedMirror mirror, List typeParameterAnnotations, List typeParameterMirrors) { // We must first add every type param, before we resolve the bounds, which can // refer to type params. String selfTypeName = getSelfTypeFromAnnotations(mirror); int i=0; for(AnnotationMirror typeParamAnnotation : typeParameterAnnotations){ TypeParameter param = new TypeParameter(); param.setUnit(scope.getUnit()); param.setContainer(scope); param.setScope(scope); ModelUtil.setVisibleScope(param); param.setDeclaration((Declaration) scope); // let's not trigger the lazy-loading if we're completing a LazyClass/LazyInterface if(scope instanceof LazyContainer) ((LazyContainer)scope).addMember(param); else // must be a method scope.addMember(param); param.setName((String)typeParamAnnotation.getValue("value")); param.setExtendedType(typeFactory.getAnythingType()); if(i < typeParameterMirrors.size()){ TypeParameterMirror typeParameterMirror = typeParameterMirrors.get(i); param.setNonErasedBounds(hasNonErasedBounds(typeParameterMirror)); } String varianceName = (String) typeParamAnnotation.getValue("variance"); if(varianceName != null){ if(varianceName.equals("IN")){ param.setContravariant(true); }else if(varianceName.equals("OUT")) param.setCovariant(true); } // If this is a self type param then link it to its type's declaration if (param.getName().equals(selfTypeName)) { param.setSelfTypedDeclaration((TypeDeclaration)scope); } params.add(param); i++; } Module moduleScope = ModelUtil.getModuleContainer(scope); // Now all type params have been set, we can resolve the references parts Iterator paramsIterator = params.iterator(); for(AnnotationMirror typeParamAnnotation : typeParameterAnnotations){ TypeParameter param = paramsIterator.next(); @SuppressWarnings("unchecked") List satisfiesAttribute = (List)typeParamAnnotation.getValue("satisfies"); setListOfTypes(param.getSatisfiedTypes(), satisfiesAttribute, scope, moduleScope, "type parameter '"+param.getName()+"' satisfied types"); @SuppressWarnings("unchecked") List caseTypesAttribute = (List)typeParamAnnotation.getValue("caseTypes"); if(caseTypesAttribute != null && !caseTypesAttribute.isEmpty()) param.setCaseTypes(new LinkedList()); setListOfTypes(param.getCaseTypes(), caseTypesAttribute, scope, moduleScope, "type parameter '"+param.getName()+"' case types"); String defaultValueAttribute = (String)typeParamAnnotation.getValue("defaultValue"); if(defaultValueAttribute != null && !defaultValueAttribute.isEmpty()){ Type decodedType = decodeType(defaultValueAttribute, scope, moduleScope, "type parameter '"+param.getName()+"' defaultValue"); param.setDefaultTypeArgument(decodedType); param.setDefaulted(true); } } } private boolean hasNonErasedBounds(TypeParameterMirror typeParameterMirror) { List bounds = typeParameterMirror.getBounds(); // if we have at least one bound and not a single Object one return bounds.size() > 0 && (bounds.size() != 1 || !sameType(bounds.get(0), OBJECT_TYPE)); } private void setListOfTypes(List destinationTypeList, List serialisedTypes, Scope scope, Module moduleScope, String targetType) { if(serialisedTypes != null){ for (String serialisedType : serialisedTypes) { Type decodedType = decodeType(serialisedType, scope, moduleScope, targetType); destinationTypeList.add(decodedType); } } } // from java type info private void setTypeParameters(Scope scope, List params, List typeParameters, boolean isCeylon) { // We must first add every type param, before we resolve the bounds, which can // refer to type params. for(TypeParameterMirror typeParam : typeParameters){ TypeParameter param = new TypeParameter(); param.setUnit(scope.getUnit()); param.setContainer(scope); param.setScope(scope); ModelUtil.setVisibleScope(param); param.setDeclaration((Declaration) scope); // let's not trigger the lazy-loading if we're completing a LazyClass/LazyInterface if(scope instanceof LazyContainer) ((LazyContainer)scope).addMember(param); else // must be a method scope.addMember(param); param.setName(typeParam.getName()); param.setExtendedType(typeFactory.getAnythingType()); params.add(param); } boolean needsObjectBounds = !isCeylon && scope instanceof Function; // Now all type params have been set, we can resolve the references parts Iterator paramsIterator = params.iterator(); for(TypeParameterMirror typeParam : typeParameters){ TypeParameter param = paramsIterator.next(); List bounds = typeParam.getBounds(); for(TypeMirror bound : bounds){ Type boundType; // we turn java's default upper bound java.lang.Object into ceylon.language.Object if(sameType(bound, OBJECT_TYPE)){ // avoid adding java's default upper bound if it's just there with no meaning, // especially since we do not want it for types if(bounds.size() == 1) break; boundType = getNonPrimitiveType(getLanguageModule(), CEYLON_OBJECT_TYPE, scope, VarianceLocation.INVARIANT); }else boundType = getNonPrimitiveType(ModelUtil.getModuleContainer(scope), bound, scope, VarianceLocation.INVARIANT); param.getSatisfiedTypes().add(boundType); } if(needsObjectBounds && param.getSatisfiedTypes().isEmpty()){ Type boundType = getNonPrimitiveType(getLanguageModule(), CEYLON_OBJECT_TYPE, scope, VarianceLocation.INVARIANT); param.getSatisfiedTypes().add(boundType); } } } // method private void setTypeParameters(Function method, MethodMirror methodMirror, boolean isCeylon) { List params = new LinkedList(); method.setTypeParameters(params); List typeParameters = getTypeParametersFromAnnotations(methodMirror); if(typeParameters != null) { setTypeParametersFromAnnotations(method, params, methodMirror, typeParameters, methodMirror.getTypeParameters()); } else { List jtp = methodMirror.getTypeParameters(); if (method.isStatic() && (methodMirror.isPublic() || methodMirror.isProtected() || methodMirror.isDefaultAccess()) && isCeylon) { jtp = jtp.subList(((ClassOrInterface)method.getContainer()).getTypeParameters().size(), jtp.size()); } setTypeParameters(method, params, jtp, isCeylon); } } // class private void setTypeParameters(TypeDeclaration klass, ClassMirror classMirror, boolean isCeylon) { List typeParameters = getTypeParametersFromAnnotations(classMirror); List mirrorTypeParameters = classMirror.getTypeParameters(); if(typeParameters != null) { if(typeParameters.isEmpty()) return; List params = new ArrayList(typeParameters.size()); klass.setTypeParameters(params); setTypeParametersFromAnnotations(klass, params, classMirror, typeParameters, mirrorTypeParameters); } else { if(mirrorTypeParameters.isEmpty()) return; List params = new ArrayList(mirrorTypeParameters.size()); klass.setTypeParameters(params); setTypeParameters(klass, params, mirrorTypeParameters, isCeylon); } } // // TypeParsing and ModelLoader private Type decodeType(String value, Scope scope, Module moduleScope, String targetType) { return decodeType(value, scope, moduleScope, targetType, null); } private Type decodeType(String value, Scope scope, Module moduleScope, String targetType, Declaration target) { try{ return typeParser.decodeType(value, scope, moduleScope, getUnitForModule(moduleScope)); }catch(TypeParserException x){ String text = formatTypeErrorMessage("Error while parsing type of", targetType, target, scope); return logModelResolutionException(x.getMessage(), scope, text); }catch(ModelResolutionException x){ String text = formatTypeErrorMessage("Error while resolving type of", targetType, target, scope); return logModelResolutionException(x, scope, text); } } private Unit getUnitForModule(Module module) { List packages = module.getPackages(); if(packages.isEmpty()){ System.err.println("No package for module "+module.getNameAsString()); return null; } Package pkg = packages.get(0); if(pkg instanceof LazyPackage == false){ System.err.println("No lazy package for module "+module.getNameAsString()); return null; } Unit unit = getCompiledUnit((LazyPackage) pkg, null); if(unit == null){ System.err.println("No unit for module "+module.getNameAsString()); return null; } return unit; } private String formatTypeErrorMessage(String prefix, String targetType, Declaration target, Scope scope) { String forTarget; if(target != null) forTarget = " for "+target.getQualifiedNameString(); else if(scope != null) forTarget = " for "+scope.getQualifiedNameString(); else forTarget = ""; return prefix+" "+targetType+forTarget; } /** Warning: only valid for toplevel types, not for type parameters */ private Type obtainType(TypeMirror type, AnnotatedMirror symbol, Scope scope, Module moduleScope, VarianceLocation variance, String targetType, Declaration target) { String typeName = getAnnotationStringValue(symbol, CEYLON_TYPE_INFO_ANNOTATION); if (typeName != null) { Type ret = decodeType(typeName, scope, moduleScope, targetType, target); // even decoded types need to fit with the reality of the underlying type if (ret.isCached()) { ret = ret.clone(); } ret.setUnderlyingType(getUnderlyingType(type, TypeLocation.TOPLEVEL)); return ret; } else { try{ return obtainType(moduleScope, type, scope, TypeLocation.TOPLEVEL, variance); }catch(ModelResolutionException x){ String text = formatTypeErrorMessage("Error while resolving type of", targetType, target, scope); return logModelResolutionException(x, scope, text); } } } private enum TypeLocation { TOPLEVEL, TYPE_PARAM; } private enum VarianceLocation { /** * Used in parameter */ CONTRAVARIANT, /** * Used in method return value */ COVARIANT, /** * For field */ INVARIANT; } private String getUnderlyingType(TypeMirror type, TypeLocation location){ // don't erase to c.l.String if in a type param location if ((sameType(type, STRING_TYPE) && location != TypeLocation.TYPE_PARAM) || sameType(type, PRIM_BYTE_TYPE) || sameType(type, PRIM_SHORT_TYPE) || sameType(type, PRIM_INT_TYPE) || sameType(type, PRIM_FLOAT_TYPE) || sameType(type, PRIM_CHAR_TYPE)) { return type.getQualifiedName(); } return null; } public Type obtainType(Module moduleScope, TypeMirror type, Scope scope, TypeLocation location, VarianceLocation variance) { TypeMirror originalType = type; // ERASURE type = applyTypeMapping(type, location); Type ret = getNonPrimitiveType(moduleScope, type, scope, variance); if (ret.isCached()) { ret = ret.clone(); } if (ret.getUnderlyingType() == null) { ret.setUnderlyingType(getUnderlyingType(originalType, location)); } return ret; } protected boolean isFunctionalInterfaceType(TypeMirror typeMirror) { return false; } protected String isFunctionalInterfaceWithExceptions(ClassMirror klass){ if(klass.getQualifiedName().equals("java.util.Iterable") || klass.getQualifiedName().equals("java.lang.annotation.Annotation")) return null; return isFunctionalInterface(klass); } protected String isFunctionalInterface(ClassMirror klass){ return null; } protected FunctionalInterfaceType getFunctionalInterfaceType(TypeMirror typeMirror) throws ModelResolutionException { return null; } private Type getFunctionalInterfaceAsCallable(Module moduleScope, Scope scope, TypeMirror typeMirror) { try{ FunctionalInterfaceType functionalInterfaceType = getFunctionalInterfaceType(typeMirror); Type returnType = obtainType(moduleScope, functionalInterfaceType.getReturnType(), scope, TypeLocation.TOPLEVEL, VarianceLocation.COVARIANT); java.util.List modelParameterTypes = new ArrayList(functionalInterfaceType.getParameterTypes().size()); for(TypeMirror parameterType : functionalInterfaceType.getParameterTypes()){ Type modelParameterType = obtainType(moduleScope, parameterType, scope, TypeLocation.TOPLEVEL, VarianceLocation.CONTRAVARIANT); modelParameterTypes.add(modelParameterType); } com.redhat.ceylon.model.typechecker.model.Type parameterTuple = typeFactory.getTupleType(modelParameterTypes, functionalInterfaceType.isVariadic(), false, -1); com.redhat.ceylon.model.typechecker.model.Type callableType = typeFactory.getCallableDeclaration().appliedType(null, Arrays.asList(returnType, parameterTuple)); return callableType; }catch(ModelResolutionException x){ return logModelResolutionException(x, scope, "Failure to turn functional interface to Callable type"); } } private TypeMirror applyTypeMapping(TypeMirror type, TypeLocation location) { // don't erase to c.l.String if in a type param location if (sameType(type, STRING_TYPE) && location != TypeLocation.TYPE_PARAM) { return CEYLON_STRING_TYPE; } else if (sameType(type, PRIM_BOOLEAN_TYPE)) { return CEYLON_BOOLEAN_TYPE; } else if (sameType(type, PRIM_BYTE_TYPE)) { return CEYLON_BYTE_TYPE; } else if (sameType(type, PRIM_SHORT_TYPE)) { return CEYLON_INTEGER_TYPE; } else if (sameType(type, PRIM_INT_TYPE)) { return CEYLON_INTEGER_TYPE; } else if (sameType(type, PRIM_LONG_TYPE)) { return CEYLON_INTEGER_TYPE; } else if (sameType(type, PRIM_FLOAT_TYPE)) { return CEYLON_FLOAT_TYPE; } else if (sameType(type, PRIM_DOUBLE_TYPE)) { return CEYLON_FLOAT_TYPE; } else if (sameType(type, PRIM_CHAR_TYPE)) { return CEYLON_CHARACTER_TYPE; } else if (sameType(type, OBJECT_TYPE)) { return CEYLON_OBJECT_TYPE; } else if (sameType(type, THROWABLE_TYPE)) { return CEYLON_THROWABLE_TYPE; } else if (sameType(type, EXCEPTION_TYPE)) { return CEYLON_EXCEPTION_TYPE; } else if (sameType(type, ANNOTATION_TYPE)) { // here we prefer Annotation over ConstrainedAnnotation but that's fine return CEYLON_ANNOTATION_TYPE; } else if (type.getKind() == TypeKind.ARRAY) { TypeMirror ct = type.getComponentType(); if (sameType(ct, PRIM_BOOLEAN_TYPE)) { return JAVA_BOOLEAN_ARRAY_TYPE; } else if (sameType(ct, PRIM_BYTE_TYPE)) { return JAVA_BYTE_ARRAY_TYPE; } else if (sameType(ct, PRIM_SHORT_TYPE)) { return JAVA_SHORT_ARRAY_TYPE; } else if (sameType(ct, PRIM_INT_TYPE)) { return JAVA_INT_ARRAY_TYPE; } else if (sameType(ct, PRIM_LONG_TYPE)) { return JAVA_LONG_ARRAY_TYPE; } else if (sameType(ct, PRIM_FLOAT_TYPE)) { return JAVA_FLOAT_ARRAY_TYPE; } else if (sameType(ct, PRIM_DOUBLE_TYPE)) { return JAVA_DOUBLE_ARRAY_TYPE; } else if (sameType(ct, PRIM_CHAR_TYPE)) { return JAVA_CHAR_ARRAY_TYPE; } else { // object array return new SimpleReflType(JAVA_LANG_OBJECT_ARRAY, SimpleReflType.Module.JDK, TypeKind.DECLARED, ct); } } return type; } private boolean sameType(TypeMirror t1, TypeMirror t2) { // make sure we deal with arrays which can't have a qualified name if(t1.getKind() == TypeKind.ARRAY){ if(t2.getKind() != TypeKind.ARRAY) return false; return sameType(t1.getComponentType(), t2.getComponentType()); } if(t2.getKind() == TypeKind.ARRAY) return false; // the rest should be OK return t1.getQualifiedName().equals(t2.getQualifiedName()); } @Override public Declaration getDeclaration(Module module, String typeName, DeclarationType declarationType) { return convertToDeclaration(module, typeName, declarationType); } private Type getNonPrimitiveType(Module moduleScope, TypeMirror type, Scope scope, VarianceLocation variance) { TypeDeclaration declaration = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(moduleScope, type, scope, DeclarationType.TYPE); if(declaration == null){ throw new ModelResolutionException("Failed to find declaration for "+type.getQualifiedName()); } return applyTypeArguments(moduleScope, declaration, type, scope, variance, TypeMappingMode.NORMAL, null); } private enum TypeMappingMode { NORMAL, GENERATOR } @SuppressWarnings("serial") private static class RecursiveTypeParameterBoundException extends RuntimeException {} private Type applyTypeArguments(Module moduleScope, TypeDeclaration declaration, TypeMirror type, Scope scope, VarianceLocation variance, TypeMappingMode mode, Set rawDeclarationsSeen) { List javacTypeArguments = type.getTypeArguments(); boolean hasTypeParameters = !declaration.getTypeParameters().isEmpty(); boolean hasTypeArguments = !javacTypeArguments.isEmpty(); boolean isRaw = !hasTypeArguments && hasTypeParameters; // if we have type arguments or type parameters (raw) if(hasTypeArguments || isRaw){ // if it's raw we will need the map anyways if(rawDeclarationsSeen == null) rawDeclarationsSeen = new HashSet(); // detect recursive bounds that we can't possibly satisfy, such as Foo> if(rawDeclarationsSeen != null && !rawDeclarationsSeen.add(declaration)) throw new RecursiveTypeParameterBoundException(); try{ List typeArguments = new ArrayList(javacTypeArguments.size()); List typeParameters = declaration.getTypeParameters(); List typeParameterMirrors = null; // SimpleReflType for Object and friends don't have a type, but don't need one if(type.getDeclaredClass() != null) typeParameterMirrors = type.getDeclaredClass().getTypeParameters(); Map siteVarianceMap = null; int len = hasTypeArguments ? javacTypeArguments.size() : typeParameters.size(); for(int i=0 ; i(); // detect recursive bounds that we can't possibly satisfy, such as Foo> if(!rawDeclarationsSeen.add(declaration)) throw new RecursiveTypeParameterBoundException(); } TypeMirror bound = typeParameterMirror.getBounds().get(0); try{ producedTypeArgument = obtainTypeParameterBound(moduleScope, bound, declaration, rawDeclarationsSeen); siteVariance = SiteVariance.OUT; }catch(RecursiveTypeParameterBoundException x){ // damnit, go for Object later } } } // if we have no type argument, or it was a wildcard with no bounds and we could not use the type parameter bounds, // let's fall back to "out Object" if(typeArgument == null && producedTypeArgument == null){ producedTypeArgument = typeFactory.getObjectType(); siteVariance = SiteVariance.OUT; } // record use-site variance if required if(!JvmBackendUtil.isCeylon(declaration) && siteVariance != null){ // lazy alloc if(siteVarianceMap == null) siteVarianceMap = new HashMap(); siteVarianceMap.put(typeParameter, siteVariance); } // in some cases we may already have a produced type argument we can use. if not let's fetch it if(producedTypeArgument == null){ if(mode == TypeMappingMode.NORMAL) producedTypeArgument = obtainType(moduleScope, typeArgument, scope, TypeLocation.TYPE_PARAM, variance); else producedTypeArgument = obtainTypeParameterBound(moduleScope, typeArgument, scope, rawDeclarationsSeen); } typeArguments.add(producedTypeArgument); } Type qualifyingType = null; if(type.getQualifyingType() != null){ qualifyingType = getNonPrimitiveType(moduleScope, type.getQualifyingType(), scope, variance); } Type ret = declaration.appliedType(qualifyingType, typeArguments); if(siteVarianceMap != null){ ret.setVarianceOverrides(siteVarianceMap); } if (ret.isCached()) { ret = ret.clone(); } ret.setUnderlyingType(type.getQualifiedName()); ret.setRaw(isRaw); return ret; }finally{ if(rawDeclarationsSeen != null) rawDeclarationsSeen.remove(declaration); } } // we have no type args, but perhaps we have a qualifying type which has some? if(type.getQualifyingType() != null){ // that one may have type arguments Type qualifyingType = getNonPrimitiveType(moduleScope, type.getQualifyingType(), scope, variance); Type ret = declaration.appliedType(qualifyingType, Collections.emptyList()); if (ret.isCached()) { ret = ret.clone(); } ret.setUnderlyingType(type.getQualifiedName()); ret.setRaw(isRaw); return ret; } // no type arg and no qualifying type return declaration.getType(); } private Type obtainTypeParameterBound(Module moduleScope, TypeMirror type, Scope scope, Set rawDeclarationsSeen) { // type variables are never mapped if(type.getKind() == TypeKind.TYPEVAR){ TypeParameterMirror typeParameter = type.getTypeParameter(); if(!typeParameter.getBounds().isEmpty()){ List bounds = new ArrayList(typeParameter.getBounds().size()); for(TypeMirror bound : typeParameter.getBounds()){ Type boundModel = obtainTypeParameterBound(moduleScope, bound, scope, rawDeclarationsSeen); bounds.add(boundModel); } return intersection(bounds, getUnitForModule(moduleScope)); }else // no bound is Object return typeFactory.getObjectType(); }else{ TypeMirror mappedType = applyTypeMapping(type, TypeLocation.TYPE_PARAM); TypeDeclaration declaration = (TypeDeclaration) convertNonPrimitiveTypeToDeclaration(moduleScope, mappedType, scope, DeclarationType.TYPE); if(declaration == null){ throw new RuntimeException("Failed to find declaration for "+type); } if(declaration instanceof UnknownType) return declaration.getType(); Type ret = applyTypeArguments(moduleScope, declaration, type, scope, VarianceLocation.CONTRAVARIANT, TypeMappingMode.GENERATOR, rawDeclarationsSeen); if (ret.isCached()) { ret = ret.clone(); } if (ret.getUnderlyingType() == null) { ret.setUnderlyingType(getUnderlyingType(type, TypeLocation.TYPE_PARAM)); } return ret; } } /*private Type getQualifyingType(TypeDeclaration declaration) { // As taken from Type.getType(): if (declaration.isMember()) { return((ClassOrInterface) declaration.getContainer()).getType(); } return null; }*/ @Override public Type getType(Module module, String pkgName, String name, Scope scope) { Declaration decl = getDeclaration(module, pkgName, name, scope); if(decl == null) return null; if(decl instanceof TypeDeclaration) return ((TypeDeclaration) decl).getType(); // it's a method or non-object value, but it's not a type return null; } @Override public Declaration getDeclaration(final Module module, final String pkgName, final String name, final Scope scope) { return synchronizedCall(new Callable() { @Override public Declaration call() throws Exception { if(scope != null){ TypeParameter typeParameter = lookupTypeParameter(scope, name); if(typeParameter != null) return typeParameter; } if(!isBootstrap || !name.startsWith(CEYLON_LANGUAGE)) { if(scope != null && pkgName != null){ Package containingPackage = ModelUtil.getPackageContainer(scope); Package pkg = containingPackage.getModule().getPackage(pkgName); String relativeName = null; String unquotedName = name.replace("$", ""); if(!pkgName.isEmpty()){ if(unquotedName.startsWith(pkgName+".")) relativeName = unquotedName.substring(pkgName.length()+1); // else we don't try it's not in this package }else relativeName = unquotedName; if(relativeName != null && pkg != null){ Declaration declaration = pkg.getDirectMember(relativeName, null, false); // if we get a value, we want its type if(JvmBackendUtil.isValue(declaration) && ((Value)declaration).getTypeDeclaration().getName().equals(relativeName)) declaration = ((Value)declaration).getTypeDeclaration(); if(declaration != null) return declaration; } } return convertToDeclaration(module, name, DeclarationType.TYPE); } return findLanguageModuleDeclarationForBootstrap(name); } }); } private Declaration findLanguageModuleDeclarationForBootstrap(String name) { // make sure we don't return anything for ceylon.language if(name.equals(CEYLON_LANGUAGE)) return null; // we're bootstrapping ceylon.language so we need to return the ProducedTypes straight from the model we're compiling Module languageModule = modules.getLanguageModule(); int lastDot = name.lastIndexOf("."); if(lastDot == -1) return null; String pkgName = name.substring(0, lastDot); String simpleName = name.substring(lastDot+1); // Nothing is a special case with no real decl if(name.equals("ceylon.language.Nothing")) return typeFactory.getNothingDeclaration(); // find the right package Package pkg = languageModule.getDirectPackage(pkgName); if(pkg != null){ Declaration member = pkg.getDirectMember(simpleName, null, false); // if we get a value, we want its type if(JvmBackendUtil.isValue(member) && ((Value)member).getTypeDeclaration().getName().equals(simpleName)){ member = ((Value)member).getTypeDeclaration(); } if(member != null) return member; } throw new ModelResolutionException("Failed to look up given type in language module while bootstrapping: "+name); } public ClassMirror[] getClassMirrorsToRemove(com.redhat.ceylon.model.typechecker.model.Declaration declaration) { if(declaration instanceof LazyClass){ return new ClassMirror[] { ((LazyClass) declaration).classMirror }; } if(declaration instanceof LazyInterface){ LazyInterface lazyInterface = (LazyInterface) declaration; if (lazyInterface.companionClass != null) { return new ClassMirror[] { lazyInterface.classMirror, lazyInterface.companionClass }; } else { return new ClassMirror[] { lazyInterface.classMirror }; } } if(declaration instanceof LazyFunction){ return new ClassMirror[] { ((LazyFunction) declaration).classMirror }; } if(declaration instanceof LazyValue){ return new ClassMirror[] { ((LazyValue) declaration).classMirror }; } if (declaration instanceof LazyClassAlias) { return new ClassMirror[] { ((LazyClassAlias) declaration).classMirror }; } if (declaration instanceof LazyInterfaceAlias) { return new ClassMirror[] { ((LazyInterfaceAlias) declaration).classMirror }; } if (declaration instanceof LazyTypeAlias) { return new ClassMirror[] { ((LazyTypeAlias) declaration).classMirror }; } return new ClassMirror[0]; } public void removeDeclarations(final List declarations) { synchronizedRun(new Runnable() { @Override public void run() { Set qualifiedNames = new HashSet<>(declarations.size() * 2); // keep in sync with getOrCreateDeclaration for (Declaration decl : declarations) { try { ClassMirror[] classMirrors = getClassMirrorsToRemove(decl); if (classMirrors == null || classMirrors.length == 0) { continue; } Scope container = decl.getContainer(); boolean isNativeHeaderMember = container instanceof Declaration && ((Declaration) container).isNativeHeader(); Map firstCache = null; Map secondCache = null; if(decl.isToplevel()){ if(JvmBackendUtil.isValue(decl)){ firstCache = valueDeclarationsByName; TypeDeclaration typeDeclaration = ((Value)decl).getTypeDeclaration(); if (typeDeclaration != null) { if(typeDeclaration.isAnonymous()) { secondCache = typeDeclarationsByName; } } else { // The value declaration has probably not been fully loaded yet. // => still try to clean the second cache also, just in case it is an anonymous object secondCache = typeDeclarationsByName; } }else if(JvmBackendUtil.isMethod(decl)) { firstCache = valueDeclarationsByName; } } if(decl instanceof TypeDeclaration) { firstCache = typeDeclarationsByName; } Module module = ModelUtil.getModuleContainer(decl.getContainer()); // ignore declarations which we do not cache, like member method/attributes for (ClassMirror classMirror : classMirrors) { qualifiedNames.add(classMirror.getQualifiedName()); String key = classMirror.getCacheKey(module); key = isNativeHeaderMember ? key + "$header" : key; if(firstCache != null) { if (firstCache.remove(key) == null) { // System.out.println("No non-null declaration removed from the first cache for key : " + key); } if(secondCache != null) { if (secondCache.remove(key) == null) { // System.out.println("No non-null declaration removed from the second cache for key : " + key); } } } } } catch(Exception e) { e.printStackTrace(); } } List keysToRemove = new ArrayList<>(qualifiedNames.size()); for (Map.Entry entry : classMirrorCache.entrySet()) { ClassMirror mirror = entry.getValue(); if (mirror == null || qualifiedNames.contains(mirror.getQualifiedName())) { keysToRemove.add(entry.getKey()); } } for (String keyToRemove : keysToRemove) { classMirrorCache.remove(keyToRemove); } } }); } private static class Stats{ int loaded, total; } private int inspectForStats(Map cache, Map loadedByPackage){ int loaded = 0; for(Declaration decl : cache.values()){ if(decl instanceof LazyElement){ Package pkg = getPackage(decl); if(pkg == null){ logVerbose("[Model loader stats: declaration "+decl.getName()+" has no package. Skipping.]"); continue; } Stats stats = loadedByPackage.get(pkg); if(stats == null){ stats = new Stats(); loadedByPackage.put(pkg, stats); } stats.total++; if(((LazyElement)decl).isLoaded()){ loaded++; stats.loaded++; } } } return loaded; } public void printStats() { synchronizedRun(new Runnable() { @Override public void run() { Map loadedByPackage = new HashMap(); int loaded = inspectForStats(typeDeclarationsByName, loadedByPackage) + inspectForStats(valueDeclarationsByName, loadedByPackage); logVerbose("[Model loader: "+loaded+"(loaded)/"+(typeDeclarationsByName.size()+valueDeclarationsByName.size())+"(total) declarations]"); for(Entry packageEntry : loadedByPackage.entrySet()){ logVerbose("[ Package "+packageEntry.getKey().getNameAsString()+": " +packageEntry.getValue().loaded+"(loaded)/"+packageEntry.getValue().total+"(total) declarations]"); } } }); } private static Package getPackage(Object decl) { if(decl == null) return null; if(decl instanceof Package) return (Package) decl; return getPackage(((Declaration)decl).getContainer()); } protected void logMissingOracleType(String type) { logVerbose("Hopefully harmless completion failure in model loader: "+type +". This is most likely when the JDK depends on Oracle private classes that we can't find." +" As a result some model information will be incomplete."); } public void setupSourceFileObjects(List treeHolders) { } @Override public Module getLoadedModule(String moduleName, String version) { return findModule(moduleName, version); } public Module getLanguageModule() { return modules.getLanguageModule(); } public Module findModule(String name, String version){ return moduleManager.findLoadedModule(name, version); } public Module getJdkProviderModule() { String jdkModuleSpec = getAlternateJdkModuleSpec(); if(jdkModuleSpec != null){ ModuleSpec spec = ModuleSpec.parse(jdkModuleSpec); return findModule(spec.getName(), spec.getVersion()); } return null; } public Module getJDKBaseModule() { return findModule(JAVA_BASE_MODULE_NAME, jdkProvider.getJDKVersion()); } public Module findModuleForFile(File file){ File path = file.getParentFile(); while (path != null) { String name = path.getPath().replaceAll("[\\\\/]", "."); // FIXME: this would load any version of this module Module m = getLoadedModule(name, null); if (m != null) { return m; } path = path.getParentFile(); } return modules.getDefaultModule(); } public abstract Module findModuleForClassMirror(ClassMirror classMirror); protected boolean isTypeHidden(Module module, String qualifiedName){ return module.getNameAsString().equals(JAVA_BASE_MODULE_NAME) && qualifiedName.equals("java.lang.Object"); } public Package findPackage(final String quotedPkgName) { return synchronizedCall(new Callable() { @Override public Package call() throws Exception { String pkgName = quotedPkgName.replace("$", ""); // in theory we only have one package with the same name per module in javac for(Package pkg : packagesByName.values()){ if(pkg.getNameAsString().equals(pkgName)) return pkg; } return null; } }); } /** * See explanation in cacheModulelessPackages() below. This is called by LanguageCompiler during loadCompiledModules(). */ public LazyPackage findOrCreateModulelessPackage(final String pkgName) { return synchronizedCall(new Callable(){ @Override public LazyPackage call() throws Exception { LazyPackage pkg = modulelessPackages.get(pkgName); if(pkg != null) return pkg; pkg = new LazyPackage(AbstractModelLoader.this); // FIXME: some refactoring needed pkg.setName(pkgName == null ? Collections.emptyList() : Arrays.asList(pkgName.split("\\."))); modulelessPackages.put(pkgName, pkg); return pkg; } }); } /** * Stef: this sucks balls, but the typechecker wants Packages created before we have any Module set up, including for parsing a module * file, and because the model loader looks up packages and caches them using their modules, we can't really have packages before we * have modules. Rather than rewrite the typechecker, we create moduleless packages during parsing, which means they are not cached with * their modules, and after the loadCompiledModules step above, we fix the package modules. Remains to be done is to move the packages * created from their cache to the right per-module cache. */ public void cacheModulelessPackages() { synchronizedRun(new Runnable() { @Override public void run() { for(LazyPackage pkg : modulelessPackages.values()){ String quotedPkgName = JVMModuleUtil.quoteJavaKeywords(pkg.getQualifiedNameString()); if (pkg.getModule() != null) { packagesByName.put(cacheKeyByModule(pkg.getModule(), quotedPkgName), pkg); } } modulelessPackages.clear(); } }); } /** * Stef: after a lot of attempting, I failed to make the CompilerModuleManager produce a LazyPackage when the ModuleManager.initCoreModules * is called for the default package. Because it is called by the PhasedUnits constructor, which is called by the ModelLoader constructor, * which means the model loader is not yet in the context, so the CompilerModuleManager can't obtain it to pass it to the LazyPackage * constructor. A rewrite of the logic of the typechecker scanning would fix this, but at this point it's just faster to let it create * the wrong default package and fix it before we start parsing anything. */ public void fixDefaultPackage() { synchronizedRun(new Runnable() { @Override public void run() { Module defaultModule = modules.getDefaultModule(); Package defaultPackage = defaultModule.getDirectPackage(""); if(defaultPackage instanceof LazyPackage == false){ LazyPackage newPkg = findOrCreateModulelessPackage(""); List defaultModulePackages = defaultModule.getPackages(); if(defaultModulePackages.size() != 1) throw new RuntimeException("Assertion failed: default module has more than the default package: "+defaultModulePackages.size()); defaultModulePackages.clear(); defaultModulePackages.add(newPkg); newPkg.setModule(defaultModule); defaultPackage.setModule(null); } } }); } public boolean isImported(Module moduleScope, Module importedModule) { if(ModelUtil.equalModules(moduleScope, importedModule)) return true; if(isImportedSpecialRules(moduleScope, importedModule)) return true; boolean isMavenAutoExport = // export to a Maven module (isAutoExportMavenDependencies() && isMavenModule(moduleScope)) // export to any module || isFullyExportMavenDependencies(); // FIXME: this is dubious actually, that's more like a flat classpath since it does // not check for imports if(isMavenAutoExport && isMavenModule(importedModule)) return true; Set visited = new HashSet(); visited.add(moduleScope); for(ModuleImport imp : moduleScope.getImports()){ if(ModelUtil.equalModules(imp.getModule(), importedModule)) return true; if((imp.isExport() || isMavenAutoExport) && isImportedTransitively(imp.getModule(), importedModule, visited)) return true; } return false; } private boolean isMavenModule(Module moduleScope) { return moduleScope.isJava() && ModuleUtil.isMavenModule(moduleScope.getNameAsString()); } private boolean isImportedSpecialRules(Module moduleScope, Module importedModule) { String importedModuleName = importedModule.getNameAsString(); // every Java module imports the JDK // ceylon.language imports the JDK if((moduleScope.isJava() || ModelUtil.equalModules(moduleScope, getLanguageModule())) && (jdkProvider.isJDKModule(importedModuleName))) return true; // everyone imports the language module if(ModelUtil.equalModules(importedModule, getLanguageModule())) return true; if(ModelUtil.equalModules(moduleScope, getLanguageModule())){ // this really sucks, I suppose we should set that up better somewhere else if((importedModuleName.equals("com.redhat.ceylon.compiler.java") || importedModuleName.equals("com.redhat.ceylon.typechecker") || importedModuleName.equals("com.redhat.ceylon.common") || importedModuleName.equals("com.redhat.ceylon.model") || importedModuleName.equals("com.redhat.ceylon.module-resolver")) && importedModule.getVersion().equals(Versions.CEYLON_VERSION_NUMBER)) return true; if(importedModuleName.equals("org.jboss.modules") && importedModule.getVersion().equals(Versions.DEPENDENCY_JBOSS_MODULES_VERSION)) return true; } return false; } private boolean isImportedTransitively(Module moduleScope, Module importedModule, Set visited) { if(!visited.add(moduleScope)) return false; boolean isMavenAutoExport = // export to a Maven module (isAutoExportMavenDependencies() && isMavenModule(moduleScope)) // export to any module || isFullyExportMavenDependencies(); for(ModuleImport imp : moduleScope.getImports()){ // only consider exported transitive deps if(!imp.isExport() && !isMavenAutoExport) continue; if(ModelUtil.equalModules(imp.getModule(), importedModule)) return true; if(isImportedSpecialRules(imp.getModule(), importedModule)) return true; if(isImportedTransitively(imp.getModule(), importedModule, visited)) return true; } return false; } protected boolean isModuleOrPackageDescriptorName(String name) { return name.equals(NamingBase.MODULE_DESCRIPTOR_CLASS_NAME) || name.equals(NamingBase.PACKAGE_DESCRIPTOR_CLASS_NAME) // Java 9 module descriptors, to not load as declarations || name.equals("module-info"); } protected void loadJavaBaseArrays(){ convertToDeclaration(getJDKBaseModule(), JAVA_LANG_OBJECT_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_BOOLEAN_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_BYTE_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_SHORT_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_INT_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_LONG_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_FLOAT_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_DOUBLE_ARRAY, DeclarationType.TYPE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_CHAR_ARRAY, DeclarationType.TYPE); } protected void loadJavaBaseAnnotations() { convertToDeclaration(getJDKBaseModule(), JAVA_LANG_NATIVE_ANNOTATION, DeclarationType.VALUE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_TRANSIENT_ANNOTATION, DeclarationType.VALUE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_VOLATILE_ANNOTATION, DeclarationType.VALUE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_SYNCHRONIZED_ANNOTATION, DeclarationType.VALUE); convertToDeclaration(getJDKBaseModule(), JAVA_LANG_NATIVE_ANNOTATION, DeclarationType.VALUE); } protected void loadJavaBaseExtras() { loadJavaBaseArrays(); loadJavaBaseAnnotations(); } /** * To be overridden by subclasses, defaults to false. */ protected boolean isAutoExportMavenDependencies(){ return false; } /** * To be overridden by subclasses, defaults to false. */ public boolean isFullyExportMavenDependencies(){ return false; } /** * To be overridden by subclasses, defaults to false. */ protected boolean isFlatClasspath(){ return false; } private static void setDeclarationAliases(Declaration decl, AnnotatedMirror mirror){ AnnotationMirror annot = mirror.getAnnotation(AbstractModelLoader.CEYLON_LANGUAGE_ALIASES_ANNOTATION); if (annot != null) { @SuppressWarnings("unchecked") List value = (List) annot.getValue("aliases"); if(value != null && !value.isEmpty()) decl.setAliases(value); } } public void loadJava9Module(LazyModule module, File jar) { List exportedPackages = Java9ModuleReader.getExportedPackages(jar); if(exportedPackages != null){ module.setExportedJavaPackages(exportedPackages); } } public void setupAlternateJdk(Module module, ArtifactResult artifact) { try { jdkProvider = new JdkProvider(module.getNameAsString(), module.getVersion(), module, artifact.artifact()); } catch (RepositoryException | IOException e) { throw new RuntimeException("Failed to load alternate Jdk provider "+module, e); } } public JdkProvider getJdkProvider(){ return jdkProvider; } public Interface getRepeatableContainer(Class c) { if (c instanceof AnnotationProxyClass) { AnnotationMirror mirror = ((AnnotationProxyClass)c).iface.classMirror.getAnnotation("java.lang.annotation.Repeatable"); if (mirror != null) { TypeMirror m = (TypeMirror)mirror.getValue(); Module module = findModuleForClassMirror(m.getDeclaredClass()); return (Interface)convertDeclaredTypeToDeclaration(module, m, DeclarationType.TYPE); } } return null; } /** * Override in sub-classes */ @Override public boolean isDynamicMetamodel() { return false; } }