The Ceylon metamodel base package.

The Ceylon metamodel allows you to:

  • dynamically inspect modules, packages, functions, values and types,
  • invoke functions, constructors and class initialisers,
  • read and write values, and
  • inspect the annotations on program elements.

A little bit of terminology

There are a few concepts and terms in the Ceylon metamodel API that you should be familiar with:

  • A declaration is the definition of a Ceylon construct, such as a module, package, value, function or class. Declarations are singletons: there is only a single instance of a given class declaration, for example. You can inspect declarations to get information about how they were defined by their author. You cannot directly invoke function or class declarations, but you can apply them to get a model that you can invoke.
  • A model is a Ceylon definition that represents a declaration where all the type variables have been bound to closed type values. You can query models for their member models and you can directly invoke models.
  • A closed type is a type which does not contain any unbound type variables.
  • An open type is a type which may contain unbound type variables.

For example, given the following Ceylon program:

shared abstract class MyList<T>() satisfies List<T>{}

The declaration of MyList represents the class declaration and contains the information that it is abstract and that it satisfies the List<T> open type. That type is open because it contains an unbound type variable T, which is not bound when we inspect the MyList class declaration.

Given an particular instance of MyList, we can query its (closed) type with the type() function, and we obtain a closed type representing (for example) MyList<Integer>. Object instances necessarily have a closed type at runtime, since in order to instantiate an object, all type arguments must be provided and known during instantiation, so the type of an object instance at runtime is necessarily a closed type: they cannot contain unbound type variables.

Closed types that represent class or interfaces are also models. For example, the closed type of our MyList<Integer> instance is both a closed type and a class model: you can query its satisfied types and find that it satisfies List<Integer> closed type and model (as opposed to the class declaration of MyList which satisfies the List<T> open type). You can also invoke that model to obtain a new instance of MyList<Integer>.

Model and declaration literals

Ceylon supports getting declaration values using either the declaration API or using declaration literals:

  • `module ceylon.file` returns the Module declaration which corresponds to the ceylon.file module you imported in your module descriptor, or to the current module if it is ceylon.file. You can also obtain a reference to the current module with `module`.
  • `package ceylon.language.meta` returns the Package declaration from your current module or its imports. You can also obtain a reference to the current package with `package`.
  • `interface List` returns the InterfaceDeclaration for the List type. You can also obtain a reference to the current interface with `interface`.
  • `class Integer` returns the ClassDeclaration for the Integer type. You can also obtain a reference to the current class with `class`.
  • `new Array.ofSize` returns the CallableConstructorDeclaration for the Array.ofSize() constructor. Similarly `new Color.black` for value constructors.
  • `function type` returns the FunctionDeclaration for the type() function. Similarly `function List.shorterThan` for methods.
  • `value modules` returns the ValueDeclaration for the modules value. Similarly `function List.size` for attributes.
  • `alias AliasName` returns the AliasDeclaration for the AliasName type alias.
  • `given T` returns the TypeParameter for the T type parameter.

Note that declaration literals cannot have type arguments specified on types or methods, as declarations are not types.

You can also get access to closed types and model using either the model API or using literals:

Notice that all model and close type literals must be applied with all required type arguments.

Accessing the metamodel using the API

Aside from declaration and model literals there are several ways you can start using the metamodel API:

  • The modules object contains a list of all currently loaded Module declarations. Note that these contain even modules you did not import as it contains all transitive dependencies, and may contain multiple different versions of the same module.
  • The classDeclaration() function will return the ClassDeclaration of the given instance.
  • The type() function will return the closed type of the given instance, which can only be a ClassModel since only classes can be instantiated.
  • The typeLiteral() function is the functional equivalent to closed type literals: it turns a type argument value into a metamodel closed type.
  • The declaration package contains all the declaration and open types.
  • The model package contains all the model and closed types.

Inspecting annotations

Constrained annotations can be inspected using the annotations() function, like this:

// Does the process declaration have the Shared annotation?
value isShared = annotations(`SharedAnnotation`, `value process`) exists;

or the related optionalAnnotation() and sequencedAnnotations() functions.

Note that annotations are queried for via their Annotation type, not by the annotation constructor which was used to annotate the program element.

By: Gavin King, Stephane Epardaud, Tom Bentley
Subpackages
ceylon.language.meta.declaration

The Ceylon metamodel open type and declaration package.

ceylon.language.meta.model

The Ceylon metamodel closed type and model package.

Values
modulesshared modules modules

Represents the list of Ceylon modules currently loaded at runtime.

Note that this contains all loaded modules, including those that are not imported by your module.

Since Ceylon supports module isolation at runtime, it is possible that there are more than one version of a given module loaded at the same time.

Usage example

Here's how you would iterate all the loaded modules and print their name and version:

import ceylon.language.meta { modules }

for(mod in modules.list){
    print("Module: ``mod.name``/``mod.version``");
}
Functions
annotationsshared Values annotations<Value, Values, in ProgramElement>(Class<ConstrainedAnnotation<Value,Values,ProgramElement,Anything>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies ConstrainedAnnotation<Value,Values,ProgramElement,Anything>
given ProgramElement satisfies Annotated

The annotations of the given type applied to the given program element. For example:

// Does the process declaration have any annotations
// of type SharedAnnotation?
value isShared = annotations(`SharedAnnotation`, `value process`) exists;

The annotations may be returned in any order.

classDeclarationshared ClassDeclaration classDeclaration(Anything instance)

Returns the class declaration for a given instance. Since only classes can be instantiated, this will always be a ClassDeclaration model.

Using declaration can be more efficient than using type() and obtaining the ClassDeclaration from the returned ClassModel.

optionalAnnotationshared Value? optionalAnnotation<Value, in ProgramElement>(Class<OptionalAnnotation<Value,ProgramElement,Anything>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies OptionalAnnotation<Value,ProgramElement,Anything>
given ProgramElement satisfies Annotated

The value of given optional annotation type on the given program element, or null if the program element was not annotated with that annotation type. For example:

// Does the process declaration have the Shared annotation?
value isShared = optionalAnnotation(`SharedAnnotation`, `value process`) exists;
sequencedAnnotationsshared Value[] sequencedAnnotations<Value, in ProgramElement>(Class<SequencedAnnotation<Value,ProgramElement,Anything>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies SequencedAnnotation<Value,ProgramElement,Anything>
given ProgramElement satisfies Annotated

The values of given sequenced annotation type on the given program element, or empty if the program element was not annotated with that annotation type. For example:

// Does the sum declaration have any ThrownException annotations?
value throwsSomething = sequencedAnnotation(`ThrownException`, `function sum`) nonempty;

The annotations may be returned in any order.

typeshared ClassModel<Type,Nothing> type<out Type>(Type instance)
given Type satisfies Anything

Returns the closed type and model of a given instance. Since only classes can be instantiated, this will always be a ClassModel model.

typeLiteralshared ClosedType<Type> typeLiteral<out Type>()
given Type satisfies Anything

Functional equivalent to type literals. Allows you to get a closed type instance for a given type argument.

For example:

assert(is Interface<List<Integer>> listOfIntegers = typeLiteral<List<Integer>>());
Classes
modulesshared modules

Represents the list of Ceylon modules currently loaded at runtime.

Note that this contains all loaded modules, including those that are not imported by your module.

Since Ceylon supports module isolation at runtime, it is possible that there are more than one version of a given module loaded at the same time.

Usage example

Here's how you would iterate all the loaded modules and print their name and version:

import ceylon.language.meta { modules }

for(mod in modules.list){
    print("Module: ``mod.name``/``mod.version``");
}