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