Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.redhat.ceylon.model.loader.AbstractModelLoader Maven / Gradle / Ivy
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 extends Foo>), 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;
}
}