Skip to main content

Draft spec changes for Metadata in Dart language

Posted by Gilad Bracha


This document fleshes out the specification for the metadata proposal given by Peter.

Spec Changes

Metadata gets its own section that describes what a valid annotation is either an identifier referring to a constant variable or a valid call to a constant constructor. Some discussion of reflective access. The rest of the changes are modifications to the grammar in the various places metadata can appear, and a change to the syntax of raw strings (which until now relied on the @ sign).

We might also want to add metadata to libraries, parts or imports, but we’ll hold off until the planned syntax revisions for those things are finalized.

Should we allow metadata to appear everywhere, like a comment? I think so. It would apply to the AST of the following expression, statement or declaration (this needs to be well-defined). For example, it has been suggested that attaching annotations to string literals is useful for internationalization. Or should that be a special case?


Dart supports metadata which is used to attach user defined annotations to program structures.  

metadata:      ('@' qualified (‘.’ identifier)? (arguments)?)*

Metadata consists of  series of annotations, each of which begin with the character @, followed by either a reference to compile-time constant variable, or a call to constant constructor.

Metadata is associated with abstract syntax tree of the program construct p that immediately follows the metadata, assuming p is not itself metadata or a comment . Metadata can be retrieved at runtime via a reflective call, provided the annotated program construct p is accessible via reflection.

Obviously, metadata can also be retrieved statically by parsing the program and evaluating the constants via a suitable interpreter. In fact many if not most uses of metadata are entirely static.

It is important that no runtime overhead be incurred by the introduction of metadata that is not actually used. Because metadata only involves constants, the time at which it is computed is irrelevant so that implementations may skip the metadata during ordinary parsing and execution and evaluate it lazily.

It is possible to associate metadata with constructs that may not be accessible via reflection, such as local variables (though it is conceivable that in the future, richer reflective libraries might provide access to these as well).  This is not as useless as it might seem. As noted above, the data can be retrieved statically if source code is available.

Metadata can appear before a class, typedef, type variable, constructor, factory, function, field, parameter, or variable declaration.

5. Variables

Variables are storage locations in memory.  

variableDeclaration:      declaredIdentifier (',' identifier)*
initializedVariableDeclaration:      declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
initializedIdentifierList:      initializedIdentifier (',' initializedIdentifier)*
initializedIdentifier:      identifier ('=' expression)?

declaredIdentifier:      metadata  finalConstVarOrType identifier    ;

finalConstVarOrType:      final type?
  | const type?
var    | type    ;

6. Functions

Functions abstract over executable actions.

functionSignature:     metadata  returnType? identifier formalParameterList    ;
returnType:      void    | type

functionBody:      '=>' expression ';'
block    ;

block:      '{' statements '}'

6.2.1 Positional Formals

A positional formal parameter is a simple variable declaration.

normalFormalParameter:      functionSignature
fieldFormalParameter    | simpleFormalParameter    ;

simpleFormalParameter:      declaredIdentifier    | metadata identifier    ;
fieldFormalParameter:   metadata finalConstVarOrType? this '.' identifier   ;

7. Classes

A class defines the form and behavior of a set of objects which are its instances.

classDefinition:       metadata  abstract? class identifier typeParameters? superclass? interfaces?
classMemberDefinition* '}'
classMemberDefinition:     metadata declaration ';'
metadata methodSignature functionBody    ;

methodSignature:      factoryConstructorSignature    | static? functionSignature    | getterSignature
  | setterSignature
  | operatorSignature    | constructorSignature initializers?
declaration:      constantConstructorSignature (redirection | initializers)?
constructorSignature (redirection | initializers)?
  | setterSignature
  | operatorSignature    | functionSignature    | static (final | const) type? staticFinalDeclarationList
  | const type? staticFinalDeclarationList  
  | final type? initializedIdentifierList    | static? (var | type) initializedIdentifierList    ;
staticFinalDeclarationList:    : staticFinalDeclaration (',' staticFinalDeclaration)*
staticFinalDeclaration:      identifier '=' expression    ;

10.5 Strings

A string is a sequence of valid unicode code points.

stringLiteral:      MULTI_LINE_STRING+

A string can be either a single line string or a multiline string.

   | ' ' ' STRING_CONTENT_SQ* ' ' '
   | '
r' ' ' ' (~( ' ' ' | NEWLINE ))* ' ' '
   | '
r' ' " ' (~( ' " ' | NEWLINE ))* ' " '

A single line string is delimited by either matching single quotes or matching double quotes.

Hence, ‘abc’ and “abc” are both legal strings, as are ‘He said “To be or not to be” did he not?’  and “He said ‘To be or not to be’ didn’t he?”. However “This ‘ is not a valid string, nor is ‘this”.

The grammar ensures that a single line string cannot span more than one line of source code, unless it includes an interpolated expression that spans multiple lines.

Adjacent single line strings are implicitly concatenated to form a single string literal.
Here is an example

print("A string" "and then another"); // prints: A stringand then another

Early versions of Dart used the operator + for string concatenation. However, this was  dropped, as it leads to puzzlers such as

print("A simple sum: 2 + 2 = " +
          2 + 2);

which this prints  'A simple sum: 2 + 2 = 22' rather than 'A simple sum: 2 + 2 = 4'.
Instead, the recommended Dart idiom is to use string interpolation.

print("A simple sum: 2 + 2 =  ${2+2}");

String interpolation work well for most cases. The main situation where it is not fully satisfactory is for string literals that are too large to fit on a line. Multiline strings can be useful, but in some cases, we want to visually align the code. This can be expressed by writing smaller strings separated by whitespace, as shown here:

'Imagine this is a very long string that does not fit on a line. What shall we do? '
'Oh what shall we do? '
'We shall split it into pieces '
'like so'

   | '
  | ‘r’ '"""'  (~("""))* '"""'
  | ‘r’ ''''' (~('''))* '''''

 | ‘\r’
 | ‘\f’
 | ‘\b’
| ‘\t’
| ‘\v’


Multiline strings are delimited by either matching triples of single quotes or matching triples of double quotes. If the first line of a multiline string consists solely of whitespace characters then that line is ignored, including the new line at its end.

Strings support escape sequences for special characters. The escapes are:
  • \n for newline, equivalent to \x0A.
  • \r for carriage return, equivalent to \x0D.
  • \f for form feed, equivalent to \x0C.
  • \b for backspace, equivalent to \x08.
  • \t for tab, equivalent to \x09.
  • \v for vertical tab, equivalent to \x0B.
  • \xHEX_DIGIT1 HEX_DIGIT2, equivalent to \u{ HEX_DIGIT1 HEX_DIGIT2}.
  • \u{HEX_DIGIT_SEQUENCE} is the unicode scalar value represented by the HEX_DIGIT_SEQUENCE. It is a compile-time error if the value of the HEX_DIGIT_SEQUENCE is not a valid unicode scalar value.
  • $ indicating the beginning of an interpolated expression.
  • Otherwise, \k indicates the character k for any k not in {n, r, f, b, t, v, x, u}.
It is a compile-time error if a non-raw string literal contains a character sequence of the form \x that is not followed by a sequence of two hexadecimal digits. It is a compile-time error if a non-raw string literal  contains a character sequence of the form \u that is not followed by either a sequence of four hexadecimal digits, or by curly brace delimited sequence of hexadecimal digits.

Any string may be prefixed with the character r, indicating that it is a raw string, in which case no escapes or interpolations are recognized.

STRING_CONTENT_DQ:      ~( '\' | '  "  ' | '$' | NEWLINE )
   | '
\' ~( NEWLINE )

STRING_CONTENT_SQ:      ~( '\' | '\'' | '$' | NEWLINE )
   | '\' ~( NEWLINE )

STRING_CONTENT_TDQ:      ~( '\' | '  "  ' | '$' )
   | '
\' ~( NEWLINE )

STRING_CONTENT_TSQ:      ~( '\' | '\'' | '$' )
   | '\' ~( NEWLINE )
NEWLINE:      \n
   | \r

All string literals implement the built-in interface String. It is a compile-time error for a class or interface to attempt to extend or implement String. The static type of a string literal is String.


String Interpolation

It is possible to embed expressions within non-raw string literals, such that the these expressions are evaluated, and the resulting values are converted into strings and concatenated with the enclosing string. This process is known as string interpolation.

   | '$' '{'
expression '}'

The reader will note that the expression inside the interpolation could itself include strings, which could again be interpolated recursively.

An unescaped $ character in a string signifies the beginning of an interpolated expression.  The $ sign may be followed by either:
  • A single identifier id that must not contain the $ character.
  • An expression e delimited by curly braces.

The form $id is equivalent to the form ${id}.  An interpolated string ‘s1${e}s2  is equivalent to the concatenation of the strings ‘s1, e.toString() and s2’. Likewise an interpolated string “s1${e}s2” is equivalent to the concatenation of  the strings “s1, e.toString() ands2. In both cases, it is a runtime error if e.toString() does not return an object of type String.

13.3.1 Typedef

A type alias declares a name for a type expression.

functionTypeAlias:      metadata typedef functionPrefix typeParameters? formalParameterList ';'
functionPrefix:    returnType? identifier    ;
The effect of a type alias of the form  typedef T id (T1 p1, .., Tn pn, [Tn+1 pn+1, …, Tn+k pn+k]) declared in a library L is is to introduce the name id into the scope of L, bound to the function type (T1, .., Tn, [ Tn+1 pn+1:, …,  Tn+k pn+k])  → T.  If no return type is specified, it is taken to be Dynamic. Likewise, if a type annotation is omitted on a formal parameter, it is taken to be Dynamic.

Currently, type aliases are restricted to function types. It is a compile-time error if any default values are specified in the signature of a function type alias. It is a compile-time error if a typedef refers to itself via a chain of references that does not include a class or interface type.


typedef F F(F f);

is illegal, as are

typedef B A();
typedef A B();


typedef D C();
class D { C foo(){}}

is legal, because the references goes through a class declaration.

Popular posts from this blog

A stronger Dart for everyone

We are constantly asking ourselves:
How do we make developers even more productive when writing Dart apps? We believe that a critical part of the answer to this question is to make strongmode – a sound static type system for Dart – the standard for all Dart developers.

Teams that use Dart to build apps like Soundtrap, AdWords, AdSense, and Greentea say they really enjoy using strong mode features, such as early error detection. In fact, teams that have switched completely to strong mode cite not only early error detection but also better code readability and maintainability as major benefits. We hear this both from small teams and Рeven more so Рfrom large teams with hundreds of developers writing and maintaining millions of lines of Dart code. As Björn Sperber from Soundtrap says,
Strong mode and the smooth integration with IntelliJ is a joy to use and a huge improvement. If you’ve tried out Flutter, you’ve already used strong mode checks from the Dart analyzer.

Given the benefits …

AngularDart 3.0: Easy upgrade, better performance

AngularDart 3.0 is now available. It brings better performance and smaller generated code, while also making you more productive.

Version 3.0 is an evolution: although it has some breaking changes (detailed below), it is a smooth upgrade due to minimal public API adjustments. Most of the progress is under the hood—in code quality, stability, generated code size, performance, and developer experience.

Code quality:
2731 instances of making the framework code more type safe (using sound Dart).The AngularDart framework code size is down by 12%.Many additional style updates to the codebase:Changed to use idiomatic <T> for generic methods.Removed NgZoneImpl, all the code exists in NgZone now.Stability:
Many CSS encapsulation fixes due to update with csslib package.Fixed bugs with IE11.

For the Mail sample app, we see 30% faster time-to-interactive (currently 3812 ms on a simulated 3G connection, measured via Lighthouse).Our large app benchmark shows 2x faster render times of…

AngularDart 4

AngularDart v4 is now available. We've been busy since the release angular2 v3.1.0 in May. Not only did we "drop the 2", but we also improved the compiler and tightened up the framework to give you smaller code, we updated the package structure to improve usability, and we added several new features. Check out the updated documentation to get started.
Just angular Upgrading to v4 will require more than updating your version constraint. The package has changed names (back) to angular – dropping the 2. You'll need to update your pubspec.yaml and the corresponding imports in your code. In most instances, find-and-replace should do the trick. Going forward, the package will be called package:angular. We'll just update the version number.
Smaller code The updated compiler in 4.0 allows type-based optimizations that not only improve runtime performance but generate better code because we are able to strongly type templates. A big result of the update is that many ap…