OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 fletchc.incremental.scope_information_visitor; | |
6 | |
7 import 'package:compiler/src/elements/modelx.dart' as modelx; | |
8 | |
9 import 'package:compiler/src/elements/visitor.dart' show | |
10 ElementVisitor; | |
11 | |
12 import 'package:compiler/src/compiler.dart' show | |
13 Compiler; | |
14 | |
15 import 'package:compiler/src/elements/elements.dart' show | |
16 AbstractFieldElement, | |
17 ClassElement, | |
18 CompilationUnitElement, | |
19 Element, | |
20 ElementCategory, | |
21 FunctionElement, | |
22 LibraryElement, | |
23 MemberElement, | |
24 ScopeContainerElement; | |
25 | |
26 import 'package:compiler/src/dart_types.dart' show | |
27 DartType; | |
28 | |
29 /** | |
30 * Serializes scope information about an element. This is accomplished by | |
31 * calling the [serialize] method on each element. Some elements need special | |
32 * treatment, as their enclosing scope must also be serialized. | |
33 */ | |
34 class ScopeInformationVisitor extends ElementVisitor/* <void> */ { | |
35 // TODO(ahe): Include function parameters and local variables. | |
36 | |
37 final Compiler compiler; | |
38 final Element currentElement; | |
39 final int position; | |
40 final StringBuffer buffer = new StringBuffer(); | |
41 int indentationLevel = 0; | |
42 ClassElement currentClass; | |
43 | |
44 bool sortMembers = false; | |
45 | |
46 bool ignoreImports = false; | |
47 | |
48 ScopeInformationVisitor(this.compiler, this.currentElement, this.position); | |
49 | |
50 String get indentation => ' ' * indentationLevel; | |
51 | |
52 StringBuffer get indented => buffer..write(indentation); | |
53 | |
54 void visitElement(Element e) { | |
55 serialize(e, omitEnclosing: false); | |
56 } | |
57 | |
58 void visitLibraryElement(LibraryElement e, _) { | |
59 bool isFirst = true; | |
60 forEach(Element member) { | |
61 if (!isFirst) { | |
62 buffer.write(','); | |
63 } | |
64 buffer.write('\n'); | |
65 indented; | |
66 serialize(member); | |
67 isFirst = false; | |
68 } | |
69 serialize( | |
70 e, | |
71 // TODO(ahe): We omit the import scope if there is no current | |
72 // class. That's wrong. | |
73 omitEnclosing: ignoreImports || currentClass == null, | |
74 name: e.libraryName, | |
75 serializeEnclosing: () { | |
76 // The enclosing scope of a library is a scope which contains all the | |
77 // imported names. | |
78 isFirst = true; | |
79 buffer.write('{\n'); | |
80 indentationLevel++; | |
81 indented.write('"kind": "imports",\n'); | |
82 indented.write('"members": ['); | |
83 indentationLevel++; | |
84 sortElements(importScope(e).importScope.values).forEach(forEach); | |
85 indentationLevel--; | |
86 buffer.write('\n'); | |
87 indented.write('],\n'); | |
88 // The enclosing scope of the imported names scope is the superclass | |
89 // scope of the current class. | |
90 indented.write('"enclosing": '); | |
91 serializeClassSide( | |
92 currentClass.superclass, isStatic: false, includeSuper: true); | |
93 buffer.write('\n'); | |
94 indentationLevel--; | |
95 indented.write('}'); | |
96 }, | |
97 serializeMembers: () { | |
98 isFirst = true; | |
99 sortElements(localScope(e).values).forEach(forEach); | |
100 }); | |
101 } | |
102 | |
103 void visitClassElement(ClassElement e, _) { | |
104 currentClass = e; | |
105 serializeClassSide(e, isStatic: true); | |
106 } | |
107 | |
108 /// Serializes one of the "sides" a class. The sides of a class are "instance | |
109 /// side" and "class side". These terms are from Smalltalk. The instance side | |
110 /// is all the local instance members of the class (the members of the | |
111 /// mixin), and the class side is the equivalent for static members and | |
112 /// constructors. | |
113 /// The scope chain is ordered so that the "class side" is searched before | |
114 /// the "instance side". | |
115 void serializeClassSide( | |
116 ClassElement e, | |
117 {bool isStatic: false, | |
118 bool omitEnclosing: false, | |
119 bool includeSuper: false}) { | |
120 e.ensureResolved(compiler.resolution); | |
121 bool isFirst = true; | |
122 var serializeEnclosing; | |
123 String kind; | |
124 if (isStatic) { | |
125 kind = 'class side'; | |
126 serializeEnclosing = () { | |
127 serializeClassSide(e, isStatic: false, omitEnclosing: omitEnclosing); | |
128 }; | |
129 } else { | |
130 kind = 'instance side'; | |
131 } | |
132 if (includeSuper) { | |
133 assert(!omitEnclosing && !isStatic); | |
134 if (e.superclass == null) { | |
135 omitEnclosing = true; | |
136 } else { | |
137 // Members of the superclass are represented as a separate scope. | |
138 serializeEnclosing = () { | |
139 serializeClassSide( | |
140 e.superclass, isStatic: false, omitEnclosing: false, | |
141 includeSuper: true); | |
142 }; | |
143 } | |
144 } | |
145 serialize( | |
146 e, omitEnclosing: omitEnclosing, serializeEnclosing: serializeEnclosing, | |
147 kind: kind, serializeMembers: () { | |
148 localMembersSorted(e).forEach((Element member) { | |
149 // Filter out members that don't belong to this "side". | |
150 if (member.isConstructor) { | |
151 // In dart2js, some constructors aren't static, but that isn't | |
152 // convenient here. | |
153 if (!isStatic) return; | |
154 } else if (member.isStatic != isStatic) { | |
155 return; | |
156 } | |
157 if (!isFirst) { | |
158 buffer.write(','); | |
159 } | |
160 buffer.write('\n'); | |
161 indented; | |
162 serialize(member); | |
163 isFirst = false; | |
164 }); | |
165 }); | |
166 } | |
167 | |
168 void visitScopeContainerElement(ScopeContainerElement e) { | |
169 bool isFirst = true; | |
170 serialize(e, omitEnclosing: false, serializeMembers: () { | |
171 localMembersSorted(e).forEach((Element member) { | |
172 if (!isFirst) { | |
173 buffer.write(','); | |
174 } | |
175 buffer.write('\n'); | |
176 indented; | |
177 serialize(member); | |
178 isFirst = false; | |
179 }); | |
180 }); | |
181 } | |
182 | |
183 void visitCompilationUnitElement(CompilationUnitElement e, _) { | |
184 e.library.accept(this, _); | |
185 } | |
186 | |
187 void visitAbstractFieldElement(AbstractFieldElement e, _) { | |
188 throw new UnsupportedError('AbstractFieldElement cannot be serialized.'); | |
189 } | |
190 | |
191 void serialize( | |
192 Element element, | |
193 {bool omitEnclosing: true, | |
194 void serializeMembers(), | |
195 void serializeEnclosing(), | |
196 String kind, | |
197 String name}) { | |
198 if (element.isAbstractField) { | |
199 AbstractFieldElement field = element; | |
200 FunctionElement getter = field.getter; | |
201 FunctionElement setter = field.setter; | |
202 if (getter != null) { | |
203 serialize( | |
204 getter, | |
205 omitEnclosing: omitEnclosing, | |
206 serializeMembers: serializeMembers, | |
207 serializeEnclosing: serializeEnclosing, | |
208 kind: kind, | |
209 name: name); | |
210 } | |
211 if (setter != null) { | |
212 if (getter != null) { | |
213 buffer.write(',\n'); | |
214 indented; | |
215 } | |
216 serialize( | |
217 getter, | |
218 omitEnclosing: omitEnclosing, | |
219 serializeMembers: serializeMembers, | |
220 serializeEnclosing: serializeEnclosing, | |
221 kind: kind, | |
222 name: name); | |
223 } | |
224 return; | |
225 } | |
226 DartType type; | |
227 int category = element.kind.category; | |
228 if (category == ElementCategory.FUNCTION || | |
229 category == ElementCategory.VARIABLE || | |
230 element.isConstructor) { | |
231 type = (element as MemberElement).type; | |
232 } | |
233 if (name == null) { | |
234 name = element.name; | |
235 } | |
236 if (kind == null) { | |
237 kind = '${element.kind}'; | |
238 } | |
239 buffer.write('{\n'); | |
240 indentationLevel++; | |
241 if (name != '') { | |
242 indented | |
243 ..write('"name": "') | |
244 ..write(name) | |
245 ..write('",\n'); | |
246 } | |
247 indented | |
248 ..write('"kind": "') | |
249 ..write(kind) | |
250 ..write('"'); | |
251 if (type != null) { | |
252 buffer.write(',\n'); | |
253 indented | |
254 ..write('"type": "') | |
255 ..write(type) | |
256 ..write('"'); | |
257 } | |
258 if (serializeMembers != null) { | |
259 buffer.write(',\n'); | |
260 indented.write('"members": ['); | |
261 indentationLevel++; | |
262 serializeMembers(); | |
263 indentationLevel--; | |
264 buffer.write('\n'); | |
265 indented.write(']'); | |
266 } | |
267 if (!omitEnclosing) { | |
268 buffer.write(',\n'); | |
269 indented.write('"enclosing": '); | |
270 if (serializeEnclosing != null) { | |
271 serializeEnclosing(); | |
272 } else { | |
273 element.enclosingElement.accept(this, null); | |
274 } | |
275 } | |
276 indentationLevel--; | |
277 buffer.write('\n'); | |
278 indented.write('}'); | |
279 } | |
280 | |
281 List<Element> localMembersSorted(ScopeContainerElement element) { | |
282 List<Element> result = <Element>[]; | |
283 element.forEachLocalMember((Element member) { | |
284 result.add(member); | |
285 }); | |
286 return sortElements(result); | |
287 } | |
288 | |
289 List<Element> sortElements(Iterable<Element> elements) { | |
290 List<Element> result = new List<Element>.from(elements); | |
291 if (sortMembers) { | |
292 result.sort((Element a, Element b) => a.name.compareTo(b.name)); | |
293 } | |
294 return result; | |
295 } | |
296 } | |
297 | |
298 modelx.ScopeX localScope(modelx.LibraryElementX element) => element.localScope; | |
299 | |
300 modelx.ImportScope importScope(modelx.LibraryElementX element) { | |
301 return element.importScope; | |
302 } | |
OLD | NEW |