OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library dart2js.parser.classes; | |
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 '../tree/tree.dart'; | |
31 import '../util/util.dart' show | |
32 Link; | |
33 | |
34 import 'listener.dart' show | |
35 Listener, | |
36 NodeListener, | |
37 ParserError, | |
38 PartialConstructorElement, | |
39 PartialElement, | |
40 PartialFunctionElement, | |
41 PartialMetadataAnnotation; | |
42 import 'parser.dart' show | |
43 Parser; | |
44 import 'partial_parser.dart' show | |
45 PartialParser; | |
46 import 'token.dart' show | |
47 Token; | |
48 | |
49 class ClassElementParser extends PartialParser { | |
50 ClassElementParser(Listener listener) : super(listener); | |
51 | |
52 Token parseClassBody(Token token) => fullParseClassBody(token); | |
53 } | |
54 | |
55 class PartialClassElement extends ClassElementX with PartialElement { | |
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 |