Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.parser.classes; | 5 library dart2js.parser.classes; |
| 6 | 6 |
| 7 import '../compiler.dart' show | |
| 8 Compiler; | |
| 9 import '../diagnostics/diagnostic_listener.dart' show | |
| 10 DiagnosticListener; | |
| 11 import '../diagnostics/messages.dart' show | |
| 12 MessageKind; | |
| 13 import '../diagnostics/invariant.dart' show | |
| 14 invariant; | |
| 15 import '../elements/elements.dart' show | |
| 16 CompilationUnitElement, | |
| 17 Element, | |
| 18 ElementKind, | |
| 19 Elements, | |
| 20 MetadataAnnotation, | |
| 21 STATE_NOT_STARTED, | |
| 22 STATE_DONE; | |
| 23 import '../elements/modelx.dart' show | |
| 24 ClassElementX, | |
| 25 ElementX, | |
| 26 FieldElementX, | |
| 27 VariableList; | |
| 28 import '../elements/visitor.dart' show | |
| 29 ElementVisitor; | |
| 30 import '../tokens/token.dart' show | 7 import '../tokens/token.dart' show |
| 31 Token; | 8 Token; |
| 32 import '../tree/tree.dart'; | |
| 33 import '../util/util.dart' show | |
| 34 Link; | |
| 35 | 9 |
| 36 import 'listener.dart' show | 10 import 'listener.dart' show |
| 37 Listener, | 11 Listener; |
| 38 NodeListener, | |
| 39 ParserError, | |
| 40 PartialConstructorElement, | |
| 41 PartialElement, | |
| 42 PartialFunctionElement, | |
| 43 PartialMetadataAnnotation; | |
| 44 import 'parser.dart' show | |
| 45 Parser; | |
| 46 import 'partial_parser.dart' show | 12 import 'partial_parser.dart' show |
| 47 PartialParser; | 13 PartialParser; |
| 48 | 14 |
| 49 class ClassElementParser extends PartialParser { | 15 class ClassElementParser extends PartialParser { |
| 50 ClassElementParser(Listener listener) : super(listener); | 16 ClassElementParser(Listener listener) : super(listener); |
| 51 | 17 |
| 52 Token parseClassBody(Token token) => fullParseClassBody(token); | 18 Token parseClassBody(Token token) => fullParseClassBody(token); |
| 53 } | 19 } |
| 54 | |
| 55 class PartialClassElement extends ClassElementX with PartialElement { | |
|
Johnni Winther
2015/09/02 13:14:41
Moved to partial_elements.dart
| |
| 56 ClassNode cachedNode; | |
| 57 | |
| 58 PartialClassElement(String name, | |
| 59 Token beginToken, | |
| 60 Token endToken, | |
| 61 Element enclosing, | |
| 62 int id) | |
| 63 : super(name, enclosing, id, STATE_NOT_STARTED) { | |
| 64 this.beginToken = beginToken; | |
| 65 this.endToken = endToken; | |
| 66 } | |
| 67 | |
| 68 void set supertypeLoadState(int state) { | |
| 69 assert(state == STATE_NOT_STARTED || state == supertypeLoadState + 1); | |
| 70 assert(state <= STATE_DONE); | |
| 71 super.supertypeLoadState = state; | |
| 72 } | |
| 73 | |
| 74 void set resolutionState(int state) { | |
| 75 assert(state == STATE_NOT_STARTED || state == resolutionState + 1); | |
| 76 assert(state <= STATE_DONE); | |
| 77 super.resolutionState = state; | |
| 78 } | |
| 79 | |
| 80 bool get hasNode => cachedNode != null; | |
| 81 | |
| 82 ClassNode get node { | |
| 83 assert(invariant(this, cachedNode != null, | |
| 84 message: "Node has not been computed for $this.")); | |
| 85 return cachedNode; | |
| 86 } | |
| 87 | |
| 88 ClassNode parseNode(Compiler compiler) { | |
| 89 if (cachedNode != null) return cachedNode; | |
| 90 compiler.withCurrentElement(this, () { | |
| 91 compiler.parser.measure(() { | |
| 92 MemberListener listener = new MemberListener(compiler, this); | |
| 93 Parser parser = new ClassElementParser(listener); | |
| 94 try { | |
| 95 Token token = parser.parseTopLevelDeclaration(beginToken); | |
| 96 assert(identical(token, endToken.next)); | |
| 97 cachedNode = listener.popNode(); | |
| 98 assert( | |
| 99 invariant( | |
| 100 beginToken, listener.nodes.isEmpty, | |
| 101 message: "Non-empty listener stack: ${listener.nodes}")); | |
| 102 } on ParserError { | |
| 103 // TODO(ahe): Often, a ParserError is thrown while parsing the class | |
| 104 // body. This means that the stack actually contains most of the | |
| 105 // information synthesized below. Consider rewriting the parser so | |
| 106 // endClassDeclaration is called before parsing the class body. | |
| 107 Identifier name = new Identifier(findMyName(beginToken)); | |
| 108 NodeList typeParameters = null; | |
| 109 Node supertype = null; | |
| 110 NodeList interfaces = listener.makeNodeList(0, null, null, ","); | |
| 111 Token extendsKeyword = null; | |
| 112 NodeList body = listener.makeNodeList(0, beginToken, endToken, null); | |
| 113 cachedNode = new ClassNode( | |
| 114 Modifiers.EMPTY, name, typeParameters, supertype, interfaces, | |
| 115 beginToken, extendsKeyword, body, endToken); | |
| 116 hasParseError = true; | |
| 117 } | |
| 118 }); | |
| 119 compiler.patchParser.measure(() { | |
| 120 if (isPatched) { | |
| 121 // TODO(lrn): Perhaps extract functionality so it doesn't | |
| 122 // need compiler. | |
| 123 compiler.patchParser.parsePatchClassNode(patch); | |
| 124 } | |
| 125 }); | |
| 126 }); | |
| 127 return cachedNode; | |
| 128 } | |
| 129 | |
| 130 Token get position => beginToken; | |
| 131 | |
| 132 // TODO(johnniwinther): Ensure that modifiers are always available. | |
| 133 Modifiers get modifiers => | |
| 134 cachedNode != null ? cachedNode.modifiers : Modifiers.EMPTY; | |
| 135 | |
| 136 accept(ElementVisitor visitor, arg) { | |
| 137 return visitor.visitClassElement(this, arg); | |
| 138 } | |
| 139 | |
| 140 PartialClassElement copyWithEnclosing(CompilationUnitElement enclosing) { | |
| 141 return new PartialClassElement(name, beginToken, endToken, enclosing, id); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 class MemberListener extends NodeListener { | |
| 146 final ClassElementX enclosingClass; | |
| 147 | |
| 148 MemberListener(DiagnosticListener listener, | |
| 149 ClassElementX enclosingElement) | |
| 150 : this.enclosingClass = enclosingElement, | |
| 151 super(listener, enclosingElement.compilationUnit); | |
| 152 | |
| 153 bool isConstructorName(Node nameNode) { | |
| 154 if (enclosingClass == null || | |
| 155 enclosingClass.kind != ElementKind.CLASS) { | |
| 156 return false; | |
| 157 } | |
| 158 String name; | |
| 159 if (nameNode.asIdentifier() != null) { | |
| 160 name = nameNode.asIdentifier().source; | |
| 161 } else { | |
| 162 Send send = nameNode.asSend(); | |
| 163 name = send.receiver.asIdentifier().source; | |
| 164 } | |
| 165 return enclosingClass.name == name; | |
| 166 } | |
| 167 | |
| 168 // TODO(johnniwinther): Remove this method. | |
| 169 String getMethodNameHack(Node methodName) { | |
| 170 Send send = methodName.asSend(); | |
| 171 if (send == null) { | |
| 172 if (isConstructorName(methodName)) return ''; | |
| 173 return methodName.asIdentifier().source; | |
| 174 } | |
| 175 Identifier receiver = send.receiver.asIdentifier(); | |
| 176 Identifier selector = send.selector.asIdentifier(); | |
| 177 Operator operator = selector.asOperator(); | |
| 178 if (operator != null) { | |
| 179 assert(identical(receiver.source, 'operator')); | |
| 180 // TODO(ahe): It is a hack to compare to ')', but it beats | |
| 181 // parsing the node. | |
| 182 bool isUnary = identical(operator.token.next.next.stringValue, ')'); | |
| 183 return Elements.constructOperatorName(operator.source, isUnary); | |
| 184 } else { | |
| 185 if (receiver == null || receiver.source != enclosingClass.name) { | |
| 186 listener.reportError(send.receiver, | |
| 187 MessageKind.INVALID_CONSTRUCTOR_NAME, | |
| 188 {'name': enclosingClass.name}); | |
| 189 } | |
| 190 return selector.source; | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 void endMethod(Token getOrSet, Token beginToken, Token endToken) { | |
| 195 super.endMethod(getOrSet, beginToken, endToken); | |
| 196 FunctionExpression method = popNode(); | |
| 197 pushNode(null); | |
| 198 bool isConstructor = isConstructorName(method.name); | |
| 199 String name = getMethodNameHack(method.name); | |
| 200 Element memberElement; | |
| 201 if (isConstructor) { | |
| 202 if (getOrSet != null) { | |
| 203 recoverableError(getOrSet, 'illegal modifier'); | |
| 204 } | |
| 205 memberElement = new PartialConstructorElement( | |
| 206 name, beginToken, endToken, | |
| 207 ElementKind.GENERATIVE_CONSTRUCTOR, | |
| 208 method.modifiers, | |
| 209 enclosingClass); | |
| 210 } else { | |
| 211 memberElement = new PartialFunctionElement( | |
| 212 name, beginToken, getOrSet, endToken, | |
| 213 method.modifiers, enclosingClass, hasBody: method.hasBody()); | |
| 214 } | |
| 215 addMember(memberElement); | |
| 216 } | |
| 217 | |
| 218 void endFactoryMethod(Token beginToken, Token endToken) { | |
| 219 super.endFactoryMethod(beginToken, endToken); | |
| 220 FunctionExpression method = popNode(); | |
| 221 pushNode(null); | |
| 222 String name = getMethodNameHack(method.name); | |
| 223 Identifier singleIdentifierName = method.name.asIdentifier(); | |
| 224 if (singleIdentifierName != null && singleIdentifierName.source == name) { | |
| 225 if (name != enclosingClass.name) { | |
| 226 listener.reportError(singleIdentifierName, | |
| 227 MessageKind.INVALID_UNNAMED_CONSTRUCTOR_NAME, | |
| 228 {'name': enclosingClass.name}); | |
| 229 } | |
| 230 } | |
| 231 Element memberElement = new PartialConstructorElement( | |
| 232 name, beginToken, endToken, | |
| 233 ElementKind.FUNCTION, | |
| 234 method.modifiers, | |
| 235 enclosingClass); | |
| 236 addMember(memberElement); | |
| 237 } | |
| 238 | |
| 239 void endFields(int count, Token beginToken, Token endToken) { | |
| 240 bool hasParseError = memberErrors.head; | |
| 241 super.endFields(count, beginToken, endToken); | |
| 242 VariableDefinitions variableDefinitions = popNode(); | |
| 243 Modifiers modifiers = variableDefinitions.modifiers; | |
| 244 pushNode(null); | |
| 245 void buildFieldElement(Identifier name, VariableList fields) { | |
| 246 Element element = | |
| 247 new FieldElementX(name, enclosingClass, fields); | |
| 248 addMember(element); | |
| 249 } | |
| 250 buildFieldElements(modifiers, variableDefinitions.definitions, | |
| 251 enclosingClass, | |
| 252 buildFieldElement, beginToken, endToken, | |
| 253 hasParseError); | |
| 254 } | |
| 255 | |
| 256 void endInitializer(Token assignmentOperator) { | |
| 257 pushNode(null); // Super expects an expression, but | |
| 258 // ClassElementParser just skips expressions. | |
| 259 super.endInitializer(assignmentOperator); | |
| 260 } | |
| 261 | |
| 262 void endInitializers(int count, Token beginToken, Token endToken) { | |
| 263 pushNode(null); | |
| 264 } | |
| 265 | |
| 266 void addMetadata(ElementX memberElement) { | |
| 267 for (Link link = metadata; !link.isEmpty; link = link.tail) { | |
| 268 memberElement.addMetadata(link.head); | |
| 269 } | |
| 270 metadata = const Link<MetadataAnnotation>(); | |
| 271 } | |
| 272 | |
| 273 void addMember(ElementX memberElement) { | |
| 274 addMetadata(memberElement); | |
| 275 enclosingClass.addMember(memberElement, listener); | |
| 276 } | |
| 277 | |
| 278 void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { | |
| 279 popNode(); // Discard arguments. | |
| 280 if (periodBeforeName != null) { | |
| 281 popNode(); // Discard name. | |
| 282 } | |
| 283 popNode(); // Discard node (Send or Identifier). | |
| 284 pushMetadata(new PartialMetadataAnnotation(beginToken, endToken)); | |
| 285 } | |
| 286 } | |
| OLD | NEW |