[As opposed to for JavaScript which was already done]
I just merged the longest-standing branch in history and we have reified generics support for Ceylon on the JVM :) Yay! Now let's talk about what happened:
- All the checks in the typechecker that bitched about non-reified generics got commented out
- Every constructor/method that has type parameters now accepts the same number of extra method parameters that represent the generics at runtime, of the form: @Ignore TypeDescriptor $reifiedT
- Every call to a type-parameterised constructor/method now passes reified generics information (unless the constructor/method in question does not support reified generics, such as java methods), most of the time by passing a TypeDescriptor instance that we already have: "void foo<T>(){ bar<T>(); }", or by using constant TypeDescriptors: "bar<Integer>();" (one such constant is created per non-type-parameterised Ceylon type, plus one for the Nothing type), or by pretty cheap/fast construction at runtime: "bar<Integer&T>();" or "bar<List<T>>();"
- I intend to create constants for constant TypeDescriptors such as "List<Integer|Character>", as an additional optimisation
- These TypeDescriptor objects are created by static factory methods in TypeDescriptor and are cheap/lightweight and might be cacheable in the future.
- I intend to add a runtime option to disable reified generics, if only for timing its costs by returning null to all TypeDescriptor factory methods.
- When we encounter an "is" operator that requires reified generics "is T" or "is List<Integer>", we replace it with a "Util.isReified(val, TypeDescriptor)" call that is expensive ATM: it transforms the TypeDescriptor into a ProducedType using the typechecker and reflection model loader, and delegates the complex operation to "ProducedType.isSubtypeOf(ProducedType)"
- The previous point means that we are now embarking the typechecker, java compiler, common and CMR at runtime, in order to build a typechecker model of the Ceylon modules at runtime
- This kicks ass because it means we have most of the pieces ready to implement the metadata
- This is great because it means we have robust well-tested technology to implement the metadata at runtime, rather than reimplement it
- This also means we have metadata at runtime for Java modules
- In the future we might (or not) be able to speed up the metadata for Ceylon modules by generating static code to instantiate it, but that will be less tested and non-lazy so potentially slower than what we have ATM.
- Because we now require Ceylon modules to have a model at runtime, we need the systems invoking the Ceylon code to tell us which Ceylon modules are installed and running and where their car/jar files are, as well as their dependencies in the case of Maven modules (we can't figure that out from within the language module, or rather that'd be a duplicate of the work done by the system invoking the Ceylon code so it'd be pretty stupid)
- The systems invoking the Ceylon code now need to call "Util.loadModule" for each Ceylon module required at runtime, which is done automatically by the ceylon-runtime system, semi-automatically by the compiler runtime tests, and automatically via a crass temporary hack by the Ceylon IDE (that will go away as soon as it uses the ceylon-runtime system)
- The runtime module system is cheap to initialise, as it only loads the package/module descriptors on init, the rest being only loaded when we use reified generics with "is"
- The runtime module system currently logs a few things I intend to remove ASAP but it also helps in noticing buggy behaviour: I've fixed quite a number of bugs by noticing nonsensical logs in the past days, so bear with them and report nonsence ;)
- There are various open issues remaining that I'll log in github.
--
Stéphane Épardaud