Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: a reflection system that can still enable tree shaking in dart2js #21654

Closed
sethladd opened this issue Nov 18, 2014 · 10 comments
Closed
Labels
type-enhancement A request for a change that isn't a bug

Comments

@sethladd
Copy link
Contributor

A tracking feature request for a reflection system that is "tree shaking aware", and does not prohibit dart2js from generating small and fast JavaScript code.

@lrhn
Copy link
Member

lrhn commented Nov 20, 2014

Added Area-LanguageFeature label.

@DartBot
Copy link

DartBot commented Feb 16, 2015

This comment was originally written by corneli...@pcornelissen.de


Are there any documents or timelines for an improved reflection toolchain?
This would make a lot of things easier

@sethladd
Copy link
Contributor Author

Perhaps with https://github.com/dart-lang/reflectable we can close this issue.

Eric, your thoughts?


cc @eernstg.

@eernstg
Copy link
Member

eernstg commented Feb 17, 2015

Yes, the package 'reflectable' is intended to enable replacing general, dynamic reflection based code by generated code that does not rely on reflection. As a consequence, tree shaking (that is, on-demand tree building) will have much better static information available and hence produce better results. I'm working on that package right now (Feb 2015), and it is expected to be usable within a few months. Seth gave the github url where the code resides, and for the basic ideas there is also Florian Loitsch's design document: https://docs.google.com/a/google.com/document/d/1YKH77JvwdhIAxEyK9Zl2fOmOO-btI5iPvroQ48XYEmo/edit?usp=sharing. Note that the core term was renamed from 'MirrorTag' to 'Reflectable' since that document was written, and many other elements are under active development, but the core ideas are still the same.

@DartBot
Copy link

DartBot commented Feb 17, 2015

This comment was originally written by @pjako


Will this code generation actually play nicely together with with dart2js incremental compiling?

@eernstg
Copy link
Member

eernstg commented Feb 17, 2015

Being based on a pub transformer (which does whole-program source to source translation), it is not trivial to achieve that goal. We will keep it in mind, of course.

@eernstg
Copy link
Member

eernstg commented Feb 17, 2015

Here is a possible workaround, though: If you work on your program which uses 'package:reflectable/reflectable.dart' without performing any transformation, then it will simply rely on dart:mirrors and you may be able to develop and the program as if the transformation did not exist (but this program will of course carry the costs of using dart:mirrors as usual). This would make the program just as incrementally compilable as other programs. You could then apply the transformer in a batch mode (e.g., when running a set of tests) in order to check that the transformation does not introduce any bugs, and to deploy the program.

@DartBot
Copy link

DartBot commented Feb 17, 2015

This comment was originally written by tobias.weste...@gmail.com


Please use the following link for the design document mentioned in comment #­4:
https://docs.google.com/document/d/1ag1aqx2hklqQM4a-orwaX9ZJEYvDBvalucCEN7vHXSQ/edit?usp=sharing

@ltackmann
Copy link

Its unclear for me if reflectable will be a full replacement for mirrors or it will always require annotation or tag modification to the code reflected.

For example in log4dart we use mirrors simply because we need access to the fully qualified type name of the class we provide logging for. Currently this requires a expensive call to

  static Logger getLoggerFor(Type type) {
    var cm = reflectClass(type);
    var loggerName = MirrorSystem.getName(cm.qualifiedName);
    return getLogger(loggerName);
  }

It seams plausible that a transformation should be able to figure out the qualified name of class by inspecting its library declaration, and hopefully without the need of annotations or tags on the classes whose qualified name we need.

@eernstg
Copy link
Member

eernstg commented Aug 19, 2015

First, you can certainly write a specialized transformer that would add a method to each class (or somehow specify a set of classes) that returns its qualified name, and then you can call that method to get the qualified name, with no dependency on reflection of any kind.

In response to the other issues you raise, the relationship between package reflectable and 'dart:mirrors' is slightly complicated.

The package reflectable isn't a 100% transparent replacement for 'dart:mirrors', because 'dart:mirrors' uses reflection by default and uses MirrorsUsed to introduce constraints on that, whereas package reflectable has been designed to provide the empty set of features by default, and use metadata to request specific reflection features for specific kinds of entities.

On top of that, package reflectable also offers a slightly different API. E.g., InstanceMirror.invoke returns the result of the invocation, not a mirror of the result as in 'dart:mirrors'. Similarly, reflect in package reflectable is a method on class Reflectable (such that each "reflector" can be a mirror system), whereas reflect is a top-level function in 'dart:mirrors' (which yields one, fixed mirror system). These changes to the API (that is, the difference between 'dart:mirrors' and package reflectable) were made with open eyes. It is not unlikely that 'dart:mirrors' will evolve such that the difference is eliminated, but from a practical point of view, for now, the slightly different APIs are a fact that must be taken into account.

However, there is another point of view that lets you think of 'dart:mirrors' and package reflectable as transparently interchangeable: You can make the choice to use package reflectable, and then you can run the code in two different ways --- if you just run the code as-is then you'll get an implementation where every reflectable mirror is a thin wrapper around a 'dart:mirrors' mirror, and if you choose to run the transformer you will get code that relies entirely on static techniques (with no dependence on 'dart:mirrors', neither directly nor indirectly).

Finally, you can make code based on package reflectable look very much like code that you would write if you had no plans to use reflection at all. Of course, the explicitly reflective code would reveal that you are using reflection, but the target classes whose instances you are accessing reflectively wouldn't have to reveal it. The point is that the notion of capabilities in package reflectable (see this document) allows you to indicate that you want something specific (say, the ability to invoke methods and constructors reflectively) for a given set of classes, and there are other ways to construct that set of classes than the main one, which is to add an annotation, @myReflector, as metadata on each class (surely, that's what you are referring to).

In particular, you can use globalQuantify(RegExp, reflector) to indicate that each class whose name matches the given regular expression RegExp must be treated as if there were an @reflector among its metadata annotations, which again means that reflection will be supported to the extent requested by that reflector. If you want something more principled than a RegExp ;^) then you can use @subReflector as a metadata annotation on a class C where subReflector is a reflector whose capabilities are specified as something like subtypeQuantify(invokingCapability). With that you will get support for invoking methods and constructors on instances of C, as you might expect, but also on instances of any type which is a subtype of C. This doesn't come for free, because your program will need to contain information about many properties of a potentially large set of classes, but it does allow you to get reflection support without editing the source code around each of all those subclasses.

In log4dart, if you are willing to "mark" classes for logging by letting them implement a specific interface (quoting and adjusting a bit of code from that package's README.md):

class MyClass implements Logging {
  static final _logger = LoggerFactory.getLoggerFor(MyClass);

  someMethod() {
    _logger.info("a info message");
    // :
    _logger.warnFormat("%s %s", ["message", "formatting"]);
  }
}

then you could use subtypeQuantify on Logging to get a specific level of support for reflection on all the classes that implement Logging, and then you could request the minimum level of support for reflection that would give you those fully qualified names (nameCapability, certainly, and I haven't tested what else you would need, but it shouldn't be much).

I'll close the issue at this point. If further issues arise in the same topic area, please report them for package reflectable, or whatever is the best fit.

NB: Finally, note that package reflectable is still under construction; in particular, the implementation of quantifiers (like subtypeQuantify and globalQuantify) is not included in the published version. It shouldn't take very long, though, even though that always turns out to be less true than you'd think. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

6 participants