mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Specs: Element registration in the Dart world
Review URL: https://codereview.chromium.org/908983002
This commit is contained in:
parent
50527025ca
commit
99be7bd226
21
specs/dom.md
21
specs/dom.md
@ -79,16 +79,26 @@ class Attr {
|
||||
|
||||
// @tagname annotation for registering elements
|
||||
// only useful when placed on classes that inherit from Element
|
||||
class tagname {
|
||||
const tagname(this.value);
|
||||
@nonnull final String value;
|
||||
class tagname extends AutomaticMetadata {
|
||||
const tagname(this.name);
|
||||
@nonnull final String name;
|
||||
void init(DeclarationMirror target, Module module) {
|
||||
assert(target is ClassMirror);
|
||||
if (!target.isSubclassOf(reflectClass(Element)))
|
||||
throw Error('@tagname can only be used on descendants of Element');
|
||||
module.registerElement(name, (target as ClassMirror).reflectedType);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FindRoot { }
|
||||
|
||||
abstract class Element extends ParentNode with ChildNode implements FindRoot {
|
||||
Element({Map</*@nonnull*/ String, /*@nonnull*/ String> attributes: null,
|
||||
List</*nonnull*/ ChildNode> nodes: null}); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants
|
||||
external Element({Map</*@nonnull*/ String, /*@nonnull*/ String> attributes: null,
|
||||
List</*nonnull*/ ChildNode> nodes: null,
|
||||
Module hostModule: null}); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants
|
||||
// initialises the internal attributes table
|
||||
// appends the given child nodes
|
||||
// if this.needsShadow, creates a shadow tree
|
||||
|
||||
@nonnull String get tagName { // O(N) in number of annotations on the class
|
||||
// throws a StateError if the class doesn't have an @tagname annotation
|
||||
@ -104,6 +114,7 @@ abstract class Element extends ParentNode with ChildNode implements FindRoot {
|
||||
// Returns a new Array and new Attr instances every time.
|
||||
@nonnull external List<Attr> getAttributes(); // O(N) in number of attributes
|
||||
|
||||
get bool needsShadow => false; // O(1)
|
||||
external ShadowRoot get shadowRoot; // O(1)
|
||||
// returns the shadow root
|
||||
// TODO(ianh): Should this be mutable? It would help explain how it gets set...
|
||||
|
||||
117
specs/modules.md
117
specs/modules.md
@ -55,98 +55,65 @@ that were declared by that module except the aforementioned element
|
||||
tree library.
|
||||
|
||||
At the end of the ``<script>`` block's source, if it parsed correctly
|
||||
and completely, the following code is appended:
|
||||
and completely, the conceptual equivalent of the following code is
|
||||
appended (but without affecting the library's list of declarations and
|
||||
without any possibility of it clashing with identifiers described in
|
||||
the library itself):
|
||||
|
||||
```dart
|
||||
class _ { }
|
||||
module.registerElements(reflectClass(_).owner);
|
||||
void main() {
|
||||
LibraryMirror library = reflectClass(_).owner as LibraryMirror;
|
||||
if (library.declarations.containsKey(#init) && library.declarations[#init] is MethodMirror)
|
||||
init();
|
||||
module.init(library);
|
||||
}
|
||||
```
|
||||
|
||||
Then, that ``main()`` function is called.
|
||||
|
||||
TODO(ianh): decide what URL and name we should give the libraries, as
|
||||
exposed in MirrorSystem.getName(libraryMirror.qualifiedName) etc
|
||||
|
||||
The ``Module`` class is defined in ``sky:core`` as follows:
|
||||
|
||||
```dart
|
||||
|
||||
TODO(ianh): dartification of the rest of this file
|
||||
abstract class AbstractModule extends EventTarget {
|
||||
AbstractModule({this.document, this.url});
|
||||
|
||||
### Exporting element definitions ###
|
||||
final Document document; // O(1)
|
||||
// the Document of the module or application
|
||||
|
||||
When importing a module into another, Sky runs the following steps:
|
||||
- let export be the imported module's ``exports`` value
|
||||
- try to import export
|
||||
- if that fails:
|
||||
- try to import each property of export
|
||||
final String url;
|
||||
|
||||
"Try to import" a value means to run the following steps:
|
||||
- if the value is an element constructor (generated by
|
||||
``registerElement()``), call this importer module's
|
||||
``registerElement()`` with the value
|
||||
@nonnull external Future<@nonnull Module> import(String url); // O(Yikes)
|
||||
// load and return the URL at the given Module
|
||||
// if it's already loaded, the future will resolve immediately
|
||||
// if loading fails, the future will have an error
|
||||
|
||||
### IDL ###
|
||||
@nonnull List</*@nonnull*/ Module> getImports(); // O(N)
|
||||
// returns the Module objects of all the imported modules
|
||||
|
||||
```javascript
|
||||
dictionary InternalElementOptions {
|
||||
String tagName;
|
||||
Boolean shadow = false;
|
||||
Object prototype = Element;
|
||||
}
|
||||
interface InternalElementConstructorWithoutShadow {
|
||||
constructor (Module hostModule);
|
||||
attribute String tagName;
|
||||
}
|
||||
interface InternalElementConstructorWithShadow {
|
||||
constructor (Module hostModule);
|
||||
attribute String tagName;
|
||||
attribute Boolean shadow;
|
||||
}
|
||||
typedef ElementRegistrationOptions (InternalElementOptions or
|
||||
InternalElementConstructorWithoutShadow or
|
||||
InternalElementConstructorWithShadow);
|
||||
external registerElement(@nonnull String tagname, @nonnull Type elementClass); // O(1)
|
||||
// registers a tag name with the parser
|
||||
// only useful during parse time
|
||||
// verify that tagname isn't null or empty
|
||||
// verify that elementClass is the Type of a class that extends Element (directly or indirectly, but not via "implements" or "with")
|
||||
// (see the @tagname code for an example of how to verify that from dart)
|
||||
// verify that there's not already a class registered for this tag name
|
||||
// if there is, then mark this tagname is broken, so that it acts as if it's not registered in the parser,
|
||||
// and, if this is the first time it was marked broken, log a console message regarding the issue
|
||||
// (mention the tag name but not the classes, so that it's not observable that this currently happens out of order)
|
||||
|
||||
abstract class AbstractModule : EventTarget {
|
||||
readonly attribute Document document; // O(1) // the Document of the module or application
|
||||
Promise<any> import(String url); // O(Yikes) // returns the module's exports
|
||||
private Array<Module> getImports(); O(N) // returns the Module objects of all the imported modules
|
||||
|
||||
readonly attribute String url;
|
||||
|
||||
ElementConstructor registerElement(Object options); // O(1)
|
||||
// if you call registerElement() with an object that was created by
|
||||
// registerElement(), it just returns the object after registering it,
|
||||
// rather than creating a new constructor
|
||||
// otherwise, it proceeds as follows:
|
||||
// - if options is a Function (i.e. it is either an
|
||||
// InternalElementConstructorWithoutShadow object or an
|
||||
// InternalElementConstructorWithShadow object), then let
|
||||
// constructor be that function, and let prototype be that
|
||||
// functions's prototype; otherwise, let constructor be a no-op
|
||||
// function and let prototype be the prototype property of the
|
||||
// object passed in (the InternalElementOptions; prototype
|
||||
// defaults to Element).
|
||||
// - let shadow be option's shadow property's value coerced to a
|
||||
// boolean, if the property is present, or else the value false.
|
||||
// - let tagName be option's tagName property's value.
|
||||
// - create a new Function that acts as if it had the signature of
|
||||
// the constructors in the ElementConstructor interface, and that
|
||||
// runs the follows steps when called:
|
||||
// - throw if not called as a constructor
|
||||
// - create an actual element object (the C++-backed object)
|
||||
// called tagName, along with the specified attributes
|
||||
// - initialise the shadow tree if shadow is true
|
||||
// - call constructor, if it's not null, with the module
|
||||
// within which the new element is being constructed as the
|
||||
// argument
|
||||
// - append all the specified children
|
||||
// - mark that new Function as created by registerElement() so that
|
||||
// it can be recognised if used as an argument to
|
||||
// registerElement()
|
||||
// - let that new Function's prototype be the aforementioned prototype
|
||||
// - let that new Function have tagName and shadow properties set to
|
||||
// the aforementioned tagName and shadow
|
||||
// - register the new tagName with this constructor
|
||||
// - return the new Function (which is, not coincidentally, an
|
||||
// InternalElementConstructorWithShadow)
|
||||
void init(LibraryMirror library) {
|
||||
library.declarations.forEach((Symbol s, DeclarationMirror d) {
|
||||
d.metadata.forEach((InstanceMirror i) {
|
||||
if (i.reflectee is AutomaticMetadata)
|
||||
i.reflectee.init(d, this);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Module : AbstractModule {
|
||||
|
||||
@ -30,3 +30,78 @@ The ``@nonnull`` annotation does nothing in code not marked
|
||||
``external``, but it has been included anyway for documentation
|
||||
purposes. It indicates places where providing a null is a contract
|
||||
violation and that results are therefore likely to be poor.
|
||||
|
||||
The following definitions are exposed in ``sky:core``:
|
||||
|
||||
```dart
|
||||
abstract class AutomaticMetadata {
|
||||
const AutomaticMetadata();
|
||||
void init(DeclarationMirror target) { }
|
||||
}
|
||||
|
||||
/*
|
||||
class AutomaticFunction extends AutomaticMetadata {
|
||||
const AutomaticFunction();
|
||||
void init(DeclarationMirror target) {
|
||||
assert(target is MethodMirror);
|
||||
MethodMirror f = target as MethodMirror;
|
||||
assert(!f.isAbstract);
|
||||
assert(f.isRegularMethod);
|
||||
assert(f.isTopLevel);
|
||||
assert(f.isStatic);
|
||||
assert(f.parameters.length == 0);
|
||||
assert(f.returnType == currentMirrorSystem().voidType);
|
||||
(f.owner as LibraryMirror).invoke(f.simpleName, []);
|
||||
}
|
||||
}
|
||||
const autorun = const AutomaticFunction();
|
||||
*/
|
||||
```
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
The following as-yet unimplemented features of the Dart language are
|
||||
assumed to exist:
|
||||
|
||||
* It is assumed that a subclass can define a constructor by reference
|
||||
to a superclass' constructor, wherein the subclass' constructor has
|
||||
the same arguments as the superclass' constructor and does nothing
|
||||
but invoke that superclass' constructor with the same arguments. The
|
||||
syntax for defining this is, within the class body for a class
|
||||
called ClassName:
|
||||
|
||||
```dart
|
||||
ClassName = SuperclassName;
|
||||
ClassName.namedConstructor = SuperclassName.otherNamedConstructor;
|
||||
```
|
||||
|
||||
* It is assumed that the standard library includes something that
|
||||
matches this pattern:
|
||||
|
||||
```dart
|
||||
class DispatcherController<T> {
|
||||
Dispatcher<T> _dispatcher;
|
||||
Dispatcher<T> get dispatcher => _dispatcher;
|
||||
|
||||
void add(T data) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
typedef bool Filter<T>(T t);
|
||||
typedef void Handler<T>(T t);
|
||||
class Dispatcher<T> {
|
||||
Dispatcher<T> where(Filter<T> filter) { /*...*/ return this; }
|
||||
void listen(Handler<T> handler) { /* ... */ }
|
||||
}
|
||||
class ExceptionListException<T> extends Exception with IterableMixin<T> {
|
||||
List<T> _exceptions;
|
||||
void add(T exception) {
|
||||
if (_exceptions == null)
|
||||
_exceptions = new List<T>();
|
||||
_exceptions.add(exception);
|
||||
}
|
||||
int get length => _exceptions == null ? 0 : _exceptions.length;
|
||||
Iterator<T> iterator() => _exceptions.iterator();
|
||||
}
|
||||
```
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user