blob: 17412f731d145f3265644e421b6568f5dd0e08c8 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.parser.partial_elements;
import '../compiler.dart' show
Compiler;
import '../dart_types.dart' show DynamicType;
import '../diagnostics/diagnostic_listener.dart';
import '../diagnostics/invariant.dart' show
invariant;
import '../diagnostics/messages.dart';
import '../elements/elements.dart' show
CompilationUnitElement,
ConstructorElement,
Element,
ElementKind,
GetterElement,
LibraryElement,
MetadataAnnotation,
MethodElement,
SetterElement,
STATE_NOT_STARTED,
STATE_DONE;
import '../elements/modelx.dart' show
BaseFunctionElementX,
ClassElementX,
ConstructorElementX,
DeclarationSite,
ElementX,
FieldElementX,
GetterElementX,
MetadataAnnotationX,
MethodElementX,
SetterElementX,
TypedefElementX,
VariableList;
import '../elements/visitor.dart' show
ElementVisitor;
import '../tokens/token.dart' show
BadInputToken,
BeginGroupToken,
ErrorToken,
KeywordToken,
StringToken,
Token,
UnmatchedToken,
UnterminatedToken;
import '../tokens/token_constants.dart' as Tokens show
EOF_TOKEN;
import '../tree/tree.dart';
import 'class_element_parser.dart' show
ClassElementParser;
import 'parser.dart' show
Parser;
import 'listener.dart' show
ParserError;
import 'member_listener.dart' show
MemberListener;
import 'node_listener.dart' show
NodeListener;
abstract class PartialElement implements DeclarationSite {
Token beginToken;
Token endToken;
bool hasParseError = false;
bool get isErroneous => hasParseError;
DeclarationSite get declarationSite => this;
}
abstract class PartialFunctionMixin implements BaseFunctionElementX {
FunctionExpression cachedNode;
Modifiers get modifiers;
Token beginToken;
Token getOrSet;
Token endToken;
/**
* The position is computed in the constructor using [findMyName]. Computing
* it on demand fails in case tokens are GC'd.
*/
Token _position;
void init(Token beginToken, Token getOrSet, Token endToken) {
this.beginToken = beginToken;
this.getOrSet = getOrSet;
this.endToken = endToken;
_position = ElementX.findNameToken(
beginToken,
modifiers.isFactory || isGenerativeConstructor,
name, enclosingElement.name);
}
bool get hasNode => cachedNode != null;
FunctionExpression get node {
assert(invariant(this, cachedNode != null,
message: "Node has not been computed for $this."));
return cachedNode;
}
FunctionExpression parseNode(DiagnosticListener listener) {
if (cachedNode != null) return cachedNode;
parseFunction(Parser p) {
if (isClassMember && modifiers.isFactory) {
p.parseFactoryMethod(beginToken);
} else {
p.parseFunction(beginToken, getOrSet);
}
}
cachedNode = parse(listener, this, declarationSite, parseFunction);
return cachedNode;
}
Token get position => _position;
void reusePartialFunctionMixin() {
cachedNode = null;
}
DeclarationSite get declarationSite;
}
abstract class PartialFunctionElement
implements PartialElement, PartialFunctionMixin {
factory PartialFunctionElement(
String name,
Token beginToken,
Token getOrSet,
Token endToken,
Modifiers modifiers,
Element enclosingElement,
{bool hasBody: true}) {
if (getOrSet == null) {
return new PartialMethodElement(
name, beginToken, endToken, modifiers,
enclosingElement, hasBody: hasBody);
} else if (identical(getOrSet.stringValue, 'get')) {
return new PartialGetterElement(
name, beginToken, getOrSet, endToken, modifiers,
enclosingElement, hasBody: hasBody);
} else {
assert(identical(getOrSet.stringValue, 'set'));
return new PartialSetterElement(
name, beginToken, getOrSet, endToken, modifiers,
enclosingElement, hasBody: hasBody);
}
}
PartialFunctionElement copyWithEnclosing(Element enclosing);
}
class PartialMethodElement extends MethodElementX
with PartialElement, PartialFunctionMixin
implements PartialFunctionElement {
PartialMethodElement(String name,
Token beginToken,
Token endToken,
Modifiers modifiers,
Element enclosing,
{bool hasBody: true})
: super(name, ElementKind.FUNCTION, modifiers, enclosing, hasBody) {
init(beginToken, null, endToken);
}
void reuseElement() {
super.reuseElement();
reusePartialFunctionMixin();
}
PartialMethodElement copyWithEnclosing(Element enclosing) {
return new PartialMethodElement(
name, beginToken, endToken, modifiers, enclosing, hasBody: hasBody);
}
}
class PartialGetterElement extends GetterElementX
with PartialElement, PartialFunctionMixin
implements GetterElement, PartialFunctionElement {
PartialGetterElement(String name,
Token beginToken,
Token getToken,
Token endToken,
Modifiers modifiers,
Element enclosing,
{bool hasBody: true})
: super(name, modifiers, enclosing, hasBody) {
init(beginToken, getToken, endToken);
}
@override
SetterElement get setter => abstractField.setter;
void reuseElement() {
super.reuseElement();
reusePartialFunctionMixin();
}
PartialGetterElement copyWithEnclosing(Element enclosing) {
return new PartialGetterElement(
name, beginToken, getOrSet, endToken, modifiers, enclosing,
hasBody: hasBody);
}
}
class PartialSetterElement extends SetterElementX
with PartialElement, PartialFunctionMixin
implements SetterElement, PartialFunctionElement {
PartialSetterElement(String name,
Token beginToken,
Token setToken,
Token endToken,
Modifiers modifiers,
Element enclosing,
{bool hasBody: true})
: super(name, modifiers, enclosing, hasBody) {
init(beginToken, setToken, endToken);
}
@override
GetterElement get getter => abstractField.getter;
void reuseElement() {
super.reuseElement();
reusePartialFunctionMixin();
}
PartialSetterElement copyWithEnclosing(Element enclosing) {
return new PartialSetterElement(
name, beginToken, getOrSet, endToken, modifiers, enclosing,
hasBody: hasBody);
}
}
class PartialConstructorElement extends ConstructorElementX
with PartialElement, PartialFunctionMixin {
PartialConstructorElement(String name,
Token beginToken,
Token endToken,
ElementKind kind,
Modifiers modifiers,
Element enclosing)
: super(name, kind, modifiers, enclosing) {
init(beginToken, null, endToken);
}
void reuseElement() {
super.reuseElement();
reusePartialFunctionMixin();
}
}
class PartialFieldList extends VariableList with PartialElement {
PartialFieldList(Token beginToken,
Token endToken,
Modifiers modifiers,
bool hasParseError)
: super(modifiers) {
super.beginToken = beginToken;
super.endToken = endToken;
super.hasParseError = hasParseError;
}
VariableDefinitions parseNode(Element element, DiagnosticListener listener) {
if (definitions != null) return definitions;
listener.withCurrentElement(element, () {
definitions = parse(
listener, element, declarationSite,
(Parser parser) => parser.parseMember(beginToken));
if (!hasParseError &&
!definitions.modifiers.isVar &&
!definitions.modifiers.isFinal &&
!definitions.modifiers.isConst &&
definitions.type == null &&
!definitions.isErroneous) {
listener.reportErrorMessage(
definitions,
MessageKind.GENERIC,
{ 'text': 'A field declaration must start with var, final, '
'const, or a type annotation.' });
}
});
return definitions;
}
computeType(Element element, Compiler compiler) {
if (type != null) return type;
// TODO(johnniwinther): Compute this in the resolver.
compiler.withCurrentElement(element, () {
VariableDefinitions node = parseNode(element, compiler);
if (node.type != null) {
type = compiler.resolver.resolveTypeAnnotation(element, node.type);
} else {
type = const DynamicType();
}
});
assert(type != null);
return type;
}
}
class PartialTypedefElement extends TypedefElementX with PartialElement {
PartialTypedefElement(
String name,
Element enclosing,
Token beginToken,
Token endToken)
: super(name, enclosing) {
this.beginToken = beginToken;
this.endToken = endToken;
}
Token get token => beginToken;
Node parseNode(DiagnosticListener listener) {
if (cachedNode != null) return cachedNode;
cachedNode = parse(
listener, this, declarationSite,
(p) => p.parseTopLevelDeclaration(token));
return cachedNode;
}
Token get position => findMyName(token);
}
/// A [MetadataAnnotation] which is constructed on demand.
class PartialMetadataAnnotation extends MetadataAnnotationX
implements PartialElement {
Token beginToken; // TODO(ahe): Make this final when issue 22065 is fixed.
final Token tokenAfterEndToken;
Expression cachedNode;
bool hasParseError = false;
PartialMetadataAnnotation(this.beginToken, this.tokenAfterEndToken);
bool get isErroneous => hasParseError;
DeclarationSite get declarationSite => this;
Token get endToken {
Token token = beginToken;
while (token.kind != Tokens.EOF_TOKEN) {
if (identical(token.next, tokenAfterEndToken)) break;
token = token.next;
}
assert(token != null);
return token;
}
void set endToken(_) {
throw new UnsupportedError("endToken=");
}
Node parseNode(DiagnosticListener listener) {
if (cachedNode != null) return cachedNode;
var metadata = parse(listener,
annotatedElement,
declarationSite,
(p) => p.parseMetadata(beginToken));
if (metadata is Metadata) {
cachedNode = metadata.expression;
return cachedNode;
} else {
assert (metadata is ErrorNode);
return metadata;
}
}
bool get hasNode => cachedNode != null;
Node get node {
assert(invariant(this, hasNode));
return cachedNode;
}
}
class PartialClassElement extends ClassElementX with PartialElement {
ClassNode cachedNode;
PartialClassElement(String name,
Token beginToken,
Token endToken,
Element enclosing,
int id)
: super(name, enclosing, id, STATE_NOT_STARTED) {
this.beginToken = beginToken;
this.endToken = endToken;
}
void set supertypeLoadState(int state) {
assert(state == STATE_NOT_STARTED || state == supertypeLoadState + 1);
assert(state <= STATE_DONE);
super.supertypeLoadState = state;
}
void set resolutionState(int state) {
assert(state == STATE_NOT_STARTED || state == resolutionState + 1);
assert(state <= STATE_DONE);
super.resolutionState = state;
}
bool get hasNode => cachedNode != null;
ClassNode get node {
assert(invariant(this, cachedNode != null,
message: "Node has not been computed for $this."));
return cachedNode;
}
ClassNode parseNode(Compiler compiler) {
if (cachedNode != null) return cachedNode;
compiler.withCurrentElement(this, () {
compiler.parser.measure(() {
MemberListener listener = new MemberListener(compiler, this);
Parser parser = new ClassElementParser(listener);
try {
Token token = parser.parseTopLevelDeclaration(beginToken);
assert(identical(token, endToken.next));
cachedNode = listener.popNode();
assert(
invariant(
beginToken, listener.nodes.isEmpty,
message: "Non-empty listener stack: ${listener.nodes}"));
} on ParserError {
// TODO(ahe): Often, a ParserError is thrown while parsing the class
// body. This means that the stack actually contains most of the
// information synthesized below. Consider rewriting the parser so
// endClassDeclaration is called before parsing the class body.
Identifier name = new Identifier(findMyName(beginToken));
NodeList typeParameters = null;
Node supertype = null;
NodeList interfaces = listener.makeNodeList(0, null, null, ",");
Token extendsKeyword = null;
NodeList body = listener.makeNodeList(0, beginToken, endToken, null);
cachedNode = new ClassNode(
Modifiers.EMPTY, name, typeParameters, supertype, interfaces,
beginToken, extendsKeyword, body, endToken);
hasParseError = true;
}
});
compiler.patchParser.measure(() {
if (isPatched) {
// TODO(lrn): Perhaps extract functionality so it doesn't
// need compiler.
compiler.patchParser.parsePatchClassNode(patch);
}
});
});
return cachedNode;
}
Token get position => beginToken;
// TODO(johnniwinther): Ensure that modifiers are always available.
Modifiers get modifiers =>
cachedNode != null ? cachedNode.modifiers : Modifiers.EMPTY;
accept(ElementVisitor visitor, arg) {
return visitor.visitClassElement(this, arg);
}
PartialClassElement copyWithEnclosing(CompilationUnitElement enclosing) {
return new PartialClassElement(name, beginToken, endToken, enclosing, id);
}
}
Node parse(
DiagnosticListener diagnosticListener,
ElementX element,
PartialElement partial,
doParse(Parser parser)) {
CompilationUnitElement unit = element.compilationUnit;
NodeListener listener = new NodeListener(diagnosticListener, unit);
listener.memberErrors = listener.memberErrors.prepend(false);
try {
if (partial.hasParseError) {
listener.suppressParseErrors = true;
}
doParse(new Parser(listener));
} on ParserError catch (e) {
partial.hasParseError = true;
return new ErrorNode(element.position, e.reason);
}
Node node = listener.popNode();
assert(listener.nodes.isEmpty);
return node;
}