Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: pkg/compiler/lib/src/dump_info.dart

Issue 3008793002: Use the entity model for dump_info. (Closed)
Patch Set: Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 dump_info; 5 library dump_info;
6 6
7 import 'dart:convert' 7 import 'dart:convert'
8 show ChunkedConversionSink, JsonEncoder, StringConversionSink; 8 show ChunkedConversionSink, JsonEncoder, StringConversionSink;
9 9
10 import 'package:dart2js_info/info.dart'; 10 import 'package:dart2js_info/info.dart';
11 11
12 import '../compiler_new.dart'; 12 import '../compiler_new.dart';
13 import 'closure.dart'; 13 import 'closure.dart';
14 import 'common/tasks.dart' show CompilerTask; 14 import 'common/tasks.dart' show CompilerTask;
15 import 'common.dart'; 15 import 'common.dart';
16 import 'common_elements.dart';
16 import 'compiler.dart' show Compiler; 17 import 'compiler.dart' show Compiler;
17 import 'constants/values.dart' show ConstantValue, InterceptorConstantValue; 18 import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
18 import 'deferred_load.dart' show OutputUnit; 19 import 'deferred_load.dart' show OutputUnit;
19 import 'elements/elements.dart'; 20 import 'elements/elements.dart';
20 import 'elements/entities.dart'; 21 import 'elements/entities.dart';
21 import 'elements/visitor.dart';
22 import 'js/js.dart' as jsAst; 22 import 'js/js.dart' as jsAst;
23 import 'js_backend/js_backend.dart' show JavaScriptBackend; 23 import 'js_backend/js_backend.dart' show JavaScriptBackend;
24 import 'types/types.dart' show TypeMask; 24 import 'types/types.dart'
25 import 'universe/world_builder.dart' show ReceiverConstraint; 25 show
26 GlobalTypeInferenceElementResult,
27 GlobalTypeInferenceMemberResult,
28 TypeMask;
29 import 'universe/world_builder.dart'
30 show CodegenWorldBuilder, ReceiverConstraint;
26 import 'universe/world_impact.dart' 31 import 'universe/world_impact.dart'
27 show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl; 32 show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
28 import 'world.dart' show ClosedWorld; 33 import 'world.dart' show ClosedWorld;
29 34
30 class ElementInfoCollector extends BaseElementVisitor<Info, dynamic> { 35 class ElementInfoCollector {
31 final Compiler compiler; 36 final Compiler compiler;
32 final ClosedWorld closedWorld; 37 final ClosedWorld closedWorld;
33 38
39 ElementEnvironment get environment => closedWorld.elementEnvironment;
40 CodegenWorldBuilder get codegenWorldBuilder => compiler.codegenWorldBuilder;
41
34 final AllInfo result = new AllInfo(); 42 final AllInfo result = new AllInfo();
35 final Map<Entity, Info> _elementToInfo = <Entity, Info>{}; 43 final Map<Entity, Info> _entityToInfo = <Entity, Info>{};
36 final Map<ConstantValue, Info> _constantToInfo = <ConstantValue, Info>{}; 44 final Map<ConstantValue, Info> _constantToInfo = <ConstantValue, Info>{};
37 final Map<OutputUnit, OutputUnitInfo> _outputToInfo = {}; 45 final Map<OutputUnit, OutputUnitInfo> _outputToInfo = {};
38 46
39 ElementInfoCollector(this.compiler, this.closedWorld); 47 ElementInfoCollector(this.compiler, this.closedWorld);
40 48
41 void run() { 49 void run() {
42 compiler.dumpInfoTask._constantToNode.forEach((constant, node) { 50 compiler.dumpInfoTask._constantToNode.forEach((constant, node) {
43 // TODO(sigmund): add dependencies on other constants 51 // TODO(sigmund): add dependencies on other constants
44 var size = compiler.dumpInfoTask._nodeToSize[node]; 52 var size = compiler.dumpInfoTask._nodeToSize[node];
45 var code = jsAst.prettyPrint(node, compiler.options); 53 var code = jsAst.prettyPrint(node, compiler.options);
46 var info = new ConstantInfo( 54 var info = new ConstantInfo(
47 size: size, code: code, outputUnit: _unitInfoForConstant(constant)); 55 size: size, code: code, outputUnit: _unitInfoForConstant(constant));
48 _constantToInfo[constant] = info; 56 _constantToInfo[constant] = info;
49 result.constants.add(info); 57 result.constants.add(info);
50 }); 58 });
51 (compiler.libraryLoader.libraries as Iterable<LibraryElement>) 59 compiler.libraryLoader.libraries.forEach(visitLibrary);
Johnni Winther 2017/08/30 13:52:35 Change this to elementEnvironment.libraries.for
Harry Terkelsen 2017/08/30 21:28:50 Done.
52 .forEach(visit); 60 closedWorld.allTypedefs.forEach(visitTypedef);
53 } 61 }
54 62
55 Info visit(Element e, [_]) => e.accept(this, null); 63 /// Whether to emit information about [entity].
56
57 /// Whether to emit information about [element].
58 /// 64 ///
59 /// By default we emit information for any element that contributes to the 65 /// By default we emit information for any entity that contributes to the
60 /// output size. Either because the it is a function being emitted or inlined, 66 /// output size. Either because it is a function being emitted or inlined,
61 /// or because it is an element that holds dependencies to other elements. 67 /// or because it is an entity that holds dependencies to other entities.
62 bool shouldKeep(Element element) { 68 bool shouldKeep(Entity entity) {
63 return compiler.dumpInfoTask.impacts.containsKey(element) || 69 return compiler.dumpInfoTask.impacts.containsKey(entity) ||
64 compiler.dumpInfoTask.inlineCount.containsKey(element); 70 compiler.dumpInfoTask.inlineCount.containsKey(entity);
65 } 71 }
66 72
67 /// Visits [element] and produces it's corresponding info. 73 LibraryInfo visitLibrary(LibraryEntity lib) {
68 Info process(Entity element) { 74 String libname = environment.getLibraryName(lib);
69 // TODO(sigmund): change the visit order to eliminate the need to check 75 if (libname.isEmpty) {
Siggi Cherem (dart-lang) 2017/08/30 17:30:42 I wonder if we still need this. I recall we run i
Harry Terkelsen 2017/08/30 21:28:50 It shouldn't be possible since we are always visit
70 // whether or not an element has been processed. 76 libname = '<unnamed>';
71 return _elementToInfo.putIfAbsent(element, () => visit(element)); 77 }
72 } 78 int size = compiler.dumpInfoTask.sizeOf(lib);
79 LibraryInfo info = new LibraryInfo(libname, lib.canonicalUri, null, size);
80 _entityToInfo[lib] = info;
73 81
74 Info visitElement(Element element, _) => null; 82 environment.forEachLibraryMember(lib, (MemberEntity member) {
75 83 if (member.isFunction || member.isGetter || member.isSetter) {
76 FunctionInfo visitConstructorBodyElement(ConstructorBodyElement e, _) { 84 FunctionInfo functionInfo = visitFunction(member);
77 return visitFunctionElement(e.constructor, _); 85 if (functionInfo != null) {
78 } 86 info.topLevelFunctions.add(functionInfo);
79 87 functionInfo.parent = info;
80 LibraryInfo visitLibraryElement(LibraryElement element, _) { 88 }
81 String libname = element.hasLibraryName ? element.libraryName : "<unnamed>"; 89 } else if (member.isField) {
82 int size = compiler.dumpInfoTask.sizeOf(element); 90 FieldInfo fieldInfo = visitField(member);
83 LibraryInfo info = 91 if (fieldInfo != null) {
84 new LibraryInfo(libname, element.canonicalUri, null, size); 92 info.topLevelVariables.add(fieldInfo);
85 _elementToInfo[element] = info; 93 fieldInfo.parent = info;
86 94 }
87 LibraryElement realElement = element.isPatched ? element.patch : element;
88 realElement.forEachLocalMember((Element member) {
89 Info child = this.process(member);
90 if (child is ClassInfo) {
91 info.classes.add(child);
92 child.parent = info;
93 } else if (child is FunctionInfo) {
94 info.topLevelFunctions.add(child);
95 child.parent = info;
96 } else if (child is FieldInfo) {
97 info.topLevelVariables.add(child);
98 child.parent = info;
99 } else if (child is TypedefInfo) {
100 info.typedefs.add(child);
101 child.parent = info;
102 } else if (child != null) {
103 print('unexpected child of $info: $child ==> ${child.runtimeType}');
104 assert(false);
105 } 95 }
106 }); 96 });
107 97
108 if (info.isEmpty && !shouldKeep(element)) return null; 98 environment.forEachClass(lib, (ClassEntity clazz) {
99 ClassInfo classInfo = visitClass(clazz);
100 if (classInfo != null) {
101 info.classes.add(classInfo);
102 classInfo.parent = info;
103 }
104 });
105
106 if (info.isEmpty && !shouldKeep(lib)) return null;
109 result.libraries.add(info); 107 result.libraries.add(info);
110 return info; 108 return info;
111 } 109 }
112 110
113 TypedefInfo visitTypedefElement(TypedefElement element, _) { 111 TypedefInfo visitTypedef(TypedefEntity typdef) {
114 if (!element.isResolved) return null; 112 var type = environment.getFunctionTypeOfTypedef(typdef);
115 TypedefInfo info = new TypedefInfo( 113 TypedefInfo info =
116 element.name, '${element.alias}', _unitInfoForElement(element)); 114 new TypedefInfo(typdef.name, '$type', _unitInfoForEntity(typdef));
117 _elementToInfo[element] = info; 115 _entityToInfo[typdef] = info;
116 LibraryInfo lib = _entityToInfo[typdef.library];
117 lib.typedefs.add(info);
118 info.parent = lib;
118 result.typedefs.add(info); 119 result.typedefs.add(info);
119 return info; 120 return info;
120 } 121 }
121 122
122 _resultOfMember(MemberElement e) => 123 GlobalTypeInferenceMemberResult _resultOfMember(MemberEntity e) =>
123 compiler.globalInference.results.resultOfMember(e); 124 compiler.globalInference.results.resultOfMember(e);
124 125
125 _resultOfParameter(ParameterElement e) => 126 GlobalTypeInferenceElementResult _resultOfParameter(Local e) =>
126 compiler.globalInference.results.resultOfParameter(e); 127 compiler.globalInference.results.resultOfParameter(e);
127 128
128 FieldInfo visitFieldElement(FieldElement element, _) { 129 FieldInfo visitField(FieldEntity field) {
129 if (!compiler.resolution.hasBeenResolved(element)) return null; 130 if (!environment.hasBeenResolved(field)) return null;
130 TypeMask inferredType = _resultOfMember(element).type; 131 TypeMask inferredType = _resultOfMember(field).type;
131 // If a field has an empty inferred type it is never used. 132 // If a field has an empty inferred type it is never used.
132 if (inferredType == null || inferredType.isEmpty) return null; 133 if (inferredType == null || inferredType.isEmpty) return null;
133 134
134 int size = compiler.dumpInfoTask.sizeOf(element); 135 int size = compiler.dumpInfoTask.sizeOf(field);
135 String code = compiler.dumpInfoTask.codeOf(element); 136 String code = compiler.dumpInfoTask.codeOf(field);
136 if (code != null) size += code.length; 137 if (code != null) size += code.length;
137 138
138 FieldInfo info = new FieldInfo( 139 FieldInfo info = new FieldInfo(
139 name: element.name, 140 name: field.name,
140 type: '${element.type}', 141 type: '${environment.getFieldType(field)}',
141 inferredType: '$inferredType', 142 inferredType: '$inferredType',
142 code: code, 143 code: code,
143 outputUnit: _unitInfoForElement(element), 144 outputUnit: _unitInfoForEntity(field),
144 isConst: element.isConst); 145 isConst: field.isConst);
145 _elementToInfo[element] = info; 146 _entityToInfo[field] = info;
146 if (element.isConst) { 147 if (codegenWorldBuilder.hasConstantFieldInitializer(field)) {
147 var value = compiler.backend.constantCompilerTask 148 info.initializer = _constantToInfo[
148 .getConstantValue(element.constant); 149 codegenWorldBuilder.getConstantFieldInitializer(field)];
149 if (value != null) {
150 info.initializer = _constantToInfo[value];
151 }
152 } 150 }
153 151
154 if (JavaScriptBackend.TRACE_METHOD == 'post') { 152 if (JavaScriptBackend.TRACE_METHOD == 'post') {
155 // We use element.hashCode because it is globally unique and it is 153 // We use field.hashCode because it is globally unique and it is
156 // available while we are doing codegen. 154 // available while we are doing codegen.
157 info.coverageId = '${element.hashCode}'; 155 info.coverageId = '${field.hashCode}';
158 } 156 }
159 157
160 int closureSize = _addClosureInfo(info, element); 158 int closureSize = _addClosureInfo(info, field);
161 info.size = size + closureSize; 159 info.size = size + closureSize;
162 160
163 result.fields.add(info); 161 result.fields.add(info);
164 return info; 162 return info;
165 } 163 }
166 164
167 ClassInfo visitClassElement(ClassElement element, _) { 165 ClassInfo visitClass(ClassEntity clazz) {
166 // Omit class if it is not needed.
167 if (!environment.hasClassBeenResolved(clazz)) return null;
168
168 ClassInfo classInfo = new ClassInfo( 169 ClassInfo classInfo = new ClassInfo(
169 name: element.name, 170 name: clazz.name,
170 isAbstract: element.isAbstract, 171 isAbstract: clazz.isAbstract,
171 outputUnit: _unitInfoForElement(element)); 172 outputUnit: _unitInfoForEntity(clazz));
172 _elementToInfo[element] = classInfo; 173 _entityToInfo[clazz] = classInfo;
173 174
174 int size = compiler.dumpInfoTask.sizeOf(element); 175 int size = compiler.dumpInfoTask.sizeOf(clazz);
175 element.forEachLocalMember((Element member) { 176 environment.forEachClassMember(clazz, (declarer, member) {
176 Info info = this.process(member); 177 // We only care about local members.
177 if (info == null) return; 178 if (declarer != clazz) return;
178 if (info is FieldInfo) { 179
179 classInfo.fields.add(info); 180 // TODO(het): add closureInfo size to the size
Siggi Cherem (dart-lang) 2017/08/30 17:30:42 delete? seems like you are already doing so?
Harry Terkelsen 2017/08/30 21:28:50 Done.
180 info.parent = classInfo; 181 if (member.isFunction || member.isGetter || member.isSetter) {
181 for (ClosureInfo closureInfo in info.closures) { 182 FunctionInfo functionInfo = visitFunction(member);
182 size += closureInfo.size; 183 if (functionInfo != null) {
184 classInfo.functions.add(functionInfo);
185 functionInfo.parent = classInfo;
186 for (var closureInfo in functionInfo.closures) {
187 size += closureInfo.size;
188 }
189 }
190 } else if (member.isField) {
191 FieldInfo fieldInfo = visitField(member);
192 if (fieldInfo != null) {
193 classInfo.fields.add(fieldInfo);
194 fieldInfo.parent = classInfo;
195 for (var closureInfo in fieldInfo.closures) {
196 size += closureInfo.size;
197 }
183 } 198 }
184 } else { 199 } else {
185 assert(info is FunctionInfo); 200 throw new StateError('Class member not a function or field');
186 classInfo.functions.add(info); 201 }
187 info.parent = classInfo; 202 });
188 for (ClosureInfo closureInfo in (info as FunctionInfo).closures) { 203 environment.forEachConstructor(clazz, (constructor) {
204 FunctionInfo functionInfo = visitFunction(constructor);
205 if (functionInfo != null) {
206 classInfo.functions.add(functionInfo);
207 functionInfo.parent = classInfo;
208 for (var closureInfo in functionInfo.closures) {
189 size += closureInfo.size; 209 size += closureInfo.size;
190 } 210 }
191 } 211 }
192 }); 212 }, ensureResolved: false);
193 213
194 classInfo.size = size; 214 classInfo.size = size;
195 215
196 // Omit element if it is not needed. 216 if (!compiler.backend.emitter.neededClasses.contains(clazz) &&
197 JavaScriptBackend backend = compiler.backend;
198 if (!backend.emitter.neededClasses.contains(element) &&
199 classInfo.fields.isEmpty && 217 classInfo.fields.isEmpty &&
200 classInfo.functions.isEmpty) { 218 classInfo.functions.isEmpty) {
201 return null; 219 return null;
202 } 220 }
221
203 result.classes.add(classInfo); 222 result.classes.add(classInfo);
204 return classInfo; 223 return classInfo;
205 } 224 }
206 225
207 ClosureInfo visitClosureClassElement(ClosureClassElement element, _) { 226 ClosureInfo visitClosureClass(ClosureClassElement element) {
208 ClosureInfo closureInfo = new ClosureInfo( 227 ClosureInfo closureInfo = new ClosureInfo(
209 name: element.name, 228 name: element.name,
210 outputUnit: _unitInfoForElement(element), 229 outputUnit: _unitInfoForEntity(element),
211 size: compiler.dumpInfoTask.sizeOf(element)); 230 size: compiler.dumpInfoTask.sizeOf(element));
212 _elementToInfo[element] = closureInfo; 231 _entityToInfo[element] = closureInfo;
213 232
214 ClosureRepresentationInfo closureRepresentation = 233 ClosureRepresentationInfo closureRepresentation =
215 compiler.backendStrategy.closureDataLookup.getClosureInfo(element.node); 234 compiler.backendStrategy.closureDataLookup.getClosureInfo(element.node);
216 assert(closureRepresentation.closureClassEntity == element); 235 assert(closureRepresentation.closureClassEntity == element);
217 236
218 FunctionInfo functionInfo = this.process(closureRepresentation.callMethod); 237 FunctionInfo functionInfo = visitFunction(closureRepresentation.callMethod);
219 if (functionInfo == null) return null; 238 if (functionInfo == null) return null;
220 closureInfo.function = functionInfo; 239 closureInfo.function = functionInfo;
221 functionInfo.parent = closureInfo; 240 functionInfo.parent = closureInfo;
222 241
223 result.closures.add(closureInfo); 242 result.closures.add(closureInfo);
224 return closureInfo; 243 return closureInfo;
225 } 244 }
226 245
227 FunctionInfo visitFunctionElement(FunctionElement element, _) { 246 FunctionInfo visitFunction(FunctionEntity function) {
228 int size = compiler.dumpInfoTask.sizeOf(element); 247 int size = compiler.dumpInfoTask.sizeOf(function);
229 // TODO(sigmund): consider adding a small info to represent unreachable 248 // TODO(sigmund): consider adding a small info to represent unreachable
230 // code here. 249 // code here.
231 if (size == 0 && !shouldKeep(element)) return null; 250 if (size == 0 && !shouldKeep(function)) return null;
232 251
233 String name = element.name; 252 // TODO(het): use 'toString' instead of 'text'? It will add '=' for setters
234 int kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND; 253 String name = function.memberName.text;
235 var enclosingElement = element.enclosingElement; 254 int kind;
236 if (enclosingElement.isField || 255 if (function.isStatic || function.isTopLevel) {
237 enclosingElement.isFunction ||
238 element.isClosure ||
239 enclosingElement.isConstructor) {
240 kind = FunctionInfo.CLOSURE_FUNCTION_KIND;
241 name = "<unnamed>";
242 } else if (element.isStatic) {
243 kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND; 256 kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
244 } else if (enclosingElement.isClass) { 257 } else if (function.enclosingClass != null) {
245 kind = FunctionInfo.METHOD_FUNCTION_KIND; 258 kind = FunctionInfo.METHOD_FUNCTION_KIND;
246 } 259 }
247 260
248 if (element.isConstructor) { 261 if (function.isConstructor) {
249 name = name == "" 262 name = name == ""
250 ? "${element.enclosingElement.name}" 263 ? "${function.enclosingClass.name}"
251 : "${element.enclosingElement.name}.${element.name}"; 264 : "${function.enclosingClass.name}.${function.name}";
252 kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND; 265 kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND;
253 } 266 }
254 267
268 assert(kind != null);
269
255 FunctionModifiers modifiers = new FunctionModifiers( 270 FunctionModifiers modifiers = new FunctionModifiers(
256 isStatic: element.isStatic, 271 isStatic: function.isStatic,
257 isConst: element.isConst, 272 isConst: function.isConst,
258 isFactory: element.isFactoryConstructor, 273 isFactory: function.isConstructor
259 isExternal: element.isPatched); 274 ? (function as ConstructorEntity).isFactoryConstructor
260 String code = compiler.dumpInfoTask.codeOf(element); 275 : false,
276 isExternal: function.isExternal,
277 );
278 String code = compiler.dumpInfoTask.codeOf(function);
261 279
262 String returnType = null;
263 List<ParameterInfo> parameters = <ParameterInfo>[]; 280 List<ParameterInfo> parameters = <ParameterInfo>[];
264 if (element.hasFunctionSignature) { 281 List<String> inferredParameterTypes = <String>[];
265 FunctionElement implementation = element.implementation; 282 codegenWorldBuilder.forEachParameterAsLocal(function, (parameter) {
266 FunctionSignature signature = implementation.functionSignature; 283 inferredParameterTypes.add('${_resultOfParameter(parameter).type}');
267 signature.forEachParameter((parameter) { 284 });
268 parameters.add(new ParameterInfo(parameter.name, 285 int parameterIndex = 0;
269 '${_resultOfParameter(parameter).type}', '${parameter.node.type}')); 286 codegenWorldBuilder.forEachParameter(function, (type, name, _) {
270 }); 287 parameters.add(new ParameterInfo(
271 returnType = '${element.type.returnType}'; 288 name, inferredParameterTypes[parameterIndex++], '$type'));
272 } 289 });
273 290
274 MethodElement method; 291 var functionType = environment.getFunctionType(function);
275 if (element is LocalFunctionElement) { 292 String returnType = '${functionType.returnType}';
276 method = element.callMethod;
277 } else {
278 method = element;
279 }
280 293
281 String inferredReturnType = '${_resultOfMember(method).returnType}'; 294 String inferredReturnType = '${_resultOfMember(function).returnType}';
282 String sideEffects = '${closedWorld.getSideEffectsOfElement(method)}'; 295 String sideEffects = '${closedWorld.getSideEffectsOfElement(function)}';
283 296
284 int inlinedCount = compiler.dumpInfoTask.inlineCount[element]; 297 int inlinedCount = compiler.dumpInfoTask.inlineCount[function];
285 if (inlinedCount == null) inlinedCount = 0; 298 if (inlinedCount == null) inlinedCount = 0;
286 299
287 FunctionInfo info = new FunctionInfo( 300 FunctionInfo info = new FunctionInfo(
288 name: name, 301 name: name,
289 functionKind: kind, 302 functionKind: kind,
290 modifiers: modifiers, 303 modifiers: modifiers,
291 returnType: returnType, 304 returnType: returnType,
292 inferredReturnType: inferredReturnType, 305 inferredReturnType: inferredReturnType,
293 parameters: parameters, 306 parameters: parameters,
294 sideEffects: sideEffects, 307 sideEffects: sideEffects,
295 inlinedCount: inlinedCount, 308 inlinedCount: inlinedCount,
296 code: code, 309 code: code,
297 type: element.type.toString(), 310 type: functionType.toString(),
298 outputUnit: _unitInfoForElement(element)); 311 outputUnit: _unitInfoForEntity(function));
299 _elementToInfo[element] = info; 312 _entityToInfo[function] = info;
300 313
301 if (element is MemberElement) { 314 int closureSize = _addClosureInfo(info, function);
302 int closureSize = _addClosureInfo(info, element as MemberElement); 315 size += closureSize;
303 size += closureSize;
304 } else {
305 info.closures = <ClosureInfo>[];
306 }
307 316
308 if (JavaScriptBackend.TRACE_METHOD == 'post') { 317 if (JavaScriptBackend.TRACE_METHOD == 'post') {
309 // We use element.hashCode because it is globally unique and it is 318 // We use function.hashCode because it is globally unique and it is
310 // available while we are doing codegen. 319 // available while we are doing codegen.
311 info.coverageId = '${element.hashCode}'; 320 info.coverageId = '${function.hashCode}';
312 } 321 }
313 322
314 info.size = size; 323 info.size = size;
315 324
316 result.functions.add(info); 325 result.functions.add(info);
317 return info; 326 return info;
318 } 327 }
319 328
320 /// Adds closure information to [info], using all nested closures in [member]. 329 /// Adds closure information to [info], using all nested closures in [member].
321 /// 330 ///
322 /// Returns the total size of the nested closures, to add to the info size. 331 /// Returns the total size of the nested closures, to add to the info size.
323 int _addClosureInfo(Info info, MemberElement member) { 332 int _addClosureInfo(Info info, MemberEntity member) {
324 assert(info is FunctionInfo || info is FieldInfo); 333 assert(info is FunctionInfo || info is FieldInfo);
325 int size = 0; 334 int size = 0;
326 List<ClosureInfo> nestedClosures = <ClosureInfo>[]; 335 List<ClosureInfo> nestedClosures = <ClosureInfo>[];
327 for (Element function in member.nestedClosures) { 336 environment.forEachNestedClosure(member, (closure) {
328 assert(function is SynthesizedCallMethodElementX); 337 ClosureInfo closureInfo = visitClosureClass(closure.enclosingClass);
329 SynthesizedCallMethodElementX callMethod = function; 338 if (closureInfo != null) {
330 ClosureInfo closure = this.process(callMethod.closureClass); 339 closureInfo.parent = info;
331 if (closure != null) { 340 nestedClosures.add(closureInfo);
332 closure.parent = info; 341 size += closureInfo.size;
333 nestedClosures.add(closure);
334 size += closure.size;
335 } 342 }
336 } 343 });
337 if (info is FunctionInfo) info.closures = nestedClosures; 344 if (info is FunctionInfo) info.closures = nestedClosures;
338 if (info is FieldInfo) info.closures = nestedClosures; 345 if (info is FieldInfo) info.closures = nestedClosures;
339 346
340 return size; 347 return size;
341 } 348 }
342 349
343 OutputUnitInfo _infoFromOutputUnit(OutputUnit outputUnit) { 350 OutputUnitInfo _infoFromOutputUnit(OutputUnit outputUnit) {
344 return _outputToInfo.putIfAbsent(outputUnit, () { 351 return _outputToInfo.putIfAbsent(outputUnit, () {
345 // Dump-info currently only works with the full emitter. If another 352 // Dump-info currently only works with the full emitter. If another
346 // emitter is used it will fail here. 353 // emitter is used it will fail here.
347 JavaScriptBackend backend = compiler.backend; 354 JavaScriptBackend backend = compiler.backend;
348 assert(outputUnit.name != null || outputUnit.isMainOutput); 355 assert(outputUnit.name != null || outputUnit.isMainOutput);
349 OutputUnitInfo info = new OutputUnitInfo( 356 OutputUnitInfo info = new OutputUnitInfo(
350 outputUnit.name, backend.emitter.emitter.generatedSize(outputUnit)); 357 outputUnit.name, backend.emitter.emitter.generatedSize(outputUnit));
351 info.imports.addAll(compiler.deferredLoadTask.getImportNames(outputUnit)); 358 info.imports.addAll(compiler.deferredLoadTask.getImportNames(outputUnit));
352 result.outputUnits.add(info); 359 result.outputUnits.add(info);
353 return info; 360 return info;
354 }); 361 });
355 } 362 }
356 363
357 OutputUnitInfo _unitInfoForElement(Element element) { 364 OutputUnitInfo _unitInfoForEntity(Entity entity) {
358 return _infoFromOutputUnit( 365 return _infoFromOutputUnit(
359 compiler.deferredLoadTask.outputUnitForElement(element)); 366 compiler.deferredLoadTask.outputUnitForEntity(entity));
360 } 367 }
361 368
362 OutputUnitInfo _unitInfoForConstant(ConstantValue constant) { 369 OutputUnitInfo _unitInfoForConstant(ConstantValue constant) {
363 OutputUnit outputUnit = 370 OutputUnit outputUnit =
364 compiler.deferredLoadTask.outputUnitForConstant(constant); 371 compiler.deferredLoadTask.outputUnitForConstant(constant);
365 if (outputUnit == null) { 372 if (outputUnit == null) {
366 assert(constant is InterceptorConstantValue); 373 assert(constant is InterceptorConstantValue);
367 return null; 374 return null;
368 } 375 }
369 return _infoFromOutputUnit(outputUnit); 376 return _infoFromOutputUnit(outputUnit);
370 } 377 }
371 } 378 }
372 379
373 class Selection { 380 class Selection {
374 final Entity selectedElement; 381 final Entity selectedEntity;
375 final ReceiverConstraint mask; 382 final ReceiverConstraint mask;
376 Selection(this.selectedElement, this.mask); 383 Selection(this.selectedEntity, this.mask);
377 } 384 }
378 385
379 /// Interface used to record information from different parts of the compiler so 386 /// Interface used to record information from different parts of the compiler so
380 /// we can emit them in the dump-info task. 387 /// we can emit them in the dump-info task.
381 // TODO(sigmund,het): move more features here. Ideally the dump-info task 388 // TODO(sigmund,het): move more features here. Ideally the dump-info task
382 // shouldn't reach into internals of other parts of the compiler. For example, 389 // shouldn't reach into internals of other parts of the compiler. For example,
383 // we currently reach into the full emitter and as a result we don't support 390 // we currently reach into the full emitter and as a result we don't support
384 // dump-info when using the startup-emitter (issue #24190). 391 // dump-info when using the startup-emitter (issue #24190).
385 abstract class InfoReporter { 392 abstract class InfoReporter {
386 void reportInlined(Element element, Element inlinedFrom); 393 void reportInlined(Element element, Element inlinedFrom);
387 } 394 }
388 395
389 class DumpInfoTask extends CompilerTask implements InfoReporter { 396 class DumpInfoTask extends CompilerTask implements InfoReporter {
390 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Dump info'); 397 static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Dump info');
391 final Compiler compiler; 398 final Compiler compiler;
392 399
393 DumpInfoTask(Compiler compiler) 400 DumpInfoTask(Compiler compiler)
394 : compiler = compiler, 401 : compiler = compiler,
395 super(compiler.measurer); 402 super(compiler.measurer);
396 403
397 String get name => "Dump Info"; 404 String get name => "Dump Info";
398 405
399 ElementInfoCollector infoCollector; 406 ElementInfoCollector infoCollector;
400 407
401 /// The size of the generated output. 408 /// The size of the generated output.
402 int _programSize; 409 int _programSize;
403 410
404 // A set of javascript AST nodes that we care about the size of. 411 // A set of javascript AST nodes that we care about the size of.
405 // This set is automatically populated when registerElementAst() 412 // This set is automatically populated when registerEntityAst()
406 // is called. 413 // is called.
407 final Set<jsAst.Node> _tracking = new Set<jsAst.Node>(); 414 final Set<jsAst.Node> _tracking = new Set<jsAst.Node>();
408 // A mapping from Dart Elements to Javascript AST Nodes. 415
409 final Map<Entity, List<jsAst.Node>> _elementToNodes = 416 // A mapping from Dart Entities to Javascript AST Nodes.
417 final Map<Entity, List<jsAst.Node>> _entityToNodes =
410 <Entity, List<jsAst.Node>>{}; 418 <Entity, List<jsAst.Node>>{};
411 final Map<ConstantValue, jsAst.Node> _constantToNode = 419 final Map<ConstantValue, jsAst.Node> _constantToNode =
412 <ConstantValue, jsAst.Node>{}; 420 <ConstantValue, jsAst.Node>{};
421
413 // A mapping from Javascript AST Nodes to the size of their 422 // A mapping from Javascript AST Nodes to the size of their
414 // pretty-printed contents. 423 // pretty-printed contents.
415 final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{}; 424 final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{};
416 425
417 final Map<Element, int> inlineCount = <Element, int>{}; 426 final Map<Entity, int> inlineCount = <Entity, int>{};
418 // A mapping from an element to a list of elements that are 427
428 // A mapping from an entity to a list of entities that are
419 // inlined inside of it. 429 // inlined inside of it.
420 final Map<Element, List<Element>> inlineMap = <Element, List<Element>>{}; 430 final Map<Entity, List<Entity>> inlineMap = <Entity, List<Entity>>{};
421 431
422 final Map<MemberEntity, WorldImpact> impacts = <MemberEntity, WorldImpact>{}; 432 final Map<MemberEntity, WorldImpact> impacts = <MemberEntity, WorldImpact>{};
423 433
424 /// Register the size of the generated output. 434 /// Register the size of the generated output.
425 void reportSize(int programSize) { 435 void reportSize(int programSize) {
426 _programSize = programSize; 436 _programSize = programSize;
427 } 437 }
428 438
429 void reportInlined(Element element, Element inlinedFrom) { 439 void reportInlined(Element element, Element inlinedFrom) {
430 element = element.declaration; 440 element = element.declaration;
431 inlinedFrom = inlinedFrom.declaration; 441 inlinedFrom = inlinedFrom.declaration;
432 442
433 inlineCount.putIfAbsent(element, () => 0); 443 inlineCount.putIfAbsent(element, () => 0);
434 inlineCount[element] += 1; 444 inlineCount[element] += 1;
435 inlineMap.putIfAbsent(inlinedFrom, () => new List<Element>()); 445 inlineMap.putIfAbsent(inlinedFrom, () => new List<Element>());
436 inlineMap[inlinedFrom].add(element); 446 inlineMap[inlinedFrom].add(element);
437 } 447 }
438 448
439 void registerImpact(MemberEntity element, WorldImpact impact) { 449 void registerImpact(MemberEntity member, WorldImpact impact) {
440 if (compiler.options.dumpInfo) { 450 if (compiler.options.dumpInfo) {
441 impacts[element] = impact; 451 impacts[member] = impact;
442 } 452 }
443 } 453 }
444 454
445 void unregisterImpact(var impactSource) { 455 void unregisterImpact(var impactSource) {
446 impacts.remove(impactSource); 456 impacts.remove(impactSource);
447 } 457 }
448 458
449 /** 459 /// Returns an iterable of [Selection]s that are used by [entity]. Each
450 * Returns an iterable of [Selection]s that are used by 460 /// [Selection] contains an entity that is used and the selector that
451 * [element]. Each [Selection] contains an element that is 461 /// selected the entity.
452 * used and the selector that selected the element. 462 Iterable<Selection> getRetaining(Entity entity, ClosedWorld closedWorld) {
453 */ 463 WorldImpact impact = impacts[entity];
454 Iterable<Selection> getRetaining(Element element, ClosedWorld closedWorld) {
455 WorldImpact impact = impacts[element];
456 if (impact == null) return const <Selection>[]; 464 if (impact == null) return const <Selection>[];
457 465
458 var selections = <Selection>[]; 466 var selections = <Selection>[];
459 compiler.impactStrategy.visitImpact( 467 compiler.impactStrategy.visitImpact(
460 element, 468 entity,
461 impact, 469 impact,
462 new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) { 470 new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) {
463 selections.addAll(closedWorld 471 selections.addAll(closedWorld
464 .locateMembers(dynamicUse.selector, dynamicUse.mask) 472 .locateMembers(dynamicUse.selector, dynamicUse.mask)
465 .map((MemberEntity e) => new Selection(e, dynamicUse.mask))); 473 .map((MemberEntity e) => new Selection(e, dynamicUse.mask)));
466 }, visitStaticUse: (staticUse) { 474 }, visitStaticUse: (staticUse) {
467 selections.add(new Selection(staticUse.element, null)); 475 selections.add(new Selection(staticUse.element, null));
468 }), 476 }),
469 IMPACT_USE); 477 IMPACT_USE);
470 return selections; 478 return selections;
471 } 479 }
472 480
473 // Returns true if we care about tracking the size of 481 // Returns true if we care about tracking the size of
474 // this node. 482 // this node.
475 bool isTracking(jsAst.Node code) { 483 bool isTracking(jsAst.Node code) {
476 if (compiler.options.dumpInfo) { 484 if (compiler.options.dumpInfo) {
477 return _tracking.contains(code); 485 return _tracking.contains(code);
478 } else { 486 } else {
479 return false; 487 return false;
480 } 488 }
481 } 489 }
482 490
483 // Registers that a javascript AST node `code` was produced by the 491 /// Registers that a javascript AST node [code] was produced by the dart
484 // dart Element `element`. 492 /// Entity [entity].
485 void registerElementAst(Entity element, jsAst.Node code) { 493 void registerEntityAst(Entity entity, jsAst.Node code) {
486 if (compiler.options.dumpInfo) { 494 if (compiler.options.dumpInfo) {
487 _elementToNodes 495 _entityToNodes
488 .putIfAbsent(element, () => new List<jsAst.Node>()) 496 .putIfAbsent(entity, () => new List<jsAst.Node>())
489 .add(code); 497 .add(code);
490 _tracking.add(code); 498 _tracking.add(code);
491 } 499 }
492 } 500 }
493 501
494 void registerConstantAst(ConstantValue constant, jsAst.Node code) { 502 void registerConstantAst(ConstantValue constant, jsAst.Node code) {
495 if (compiler.options.dumpInfo) { 503 if (compiler.options.dumpInfo) {
496 assert(_constantToNode[constant] == null || 504 assert(_constantToNode[constant] == null ||
497 _constantToNode[constant] == code); 505 _constantToNode[constant] == code);
498 _constantToNode[constant] = code; 506 _constantToNode[constant] = code;
499 _tracking.add(code); 507 _tracking.add(code);
500 } 508 }
501 } 509 }
502 510
503 // Records the size of a dart AST node after it has been 511 /// Records the size of a dart AST node after it has been pretty-printed into
504 // pretty-printed into the output buffer. 512 /// the output buffer.
505 void recordAstSize(jsAst.Node node, int size) { 513 void recordAstSize(jsAst.Node node, int size) {
506 if (isTracking(node)) { 514 if (isTracking(node)) {
507 //TODO: should I be incrementing here instead? 515 //TODO: should I be incrementing here instead?
508 _nodeToSize[node] = size; 516 _nodeToSize[node] = size;
509 } 517 }
510 } 518 }
511 519
512 // Returns the size of the source code that 520 /// Returns the size of the source code that was generated for an entity.
513 // was generated for an element. If no source 521 /// If no source code was produced, return 0.
514 // code was produced, return 0. 522 int sizeOf(Entity entity) {
515 int sizeOf(Element element) { 523 if (_entityToNodes.containsKey(entity)) {
516 if (_elementToNodes.containsKey(element)) { 524 return _entityToNodes[entity].map(sizeOfNode).fold(0, (a, b) => a + b);
517 return _elementToNodes[element].map(sizeOfNode).fold(0, (a, b) => a + b);
518 } else { 525 } else {
519 return 0; 526 return 0;
520 } 527 }
521 } 528 }
522 529
523 int sizeOfNode(jsAst.Node node) { 530 int sizeOfNode(jsAst.Node node) {
524 // TODO(sigmund): switch back to null aware operators (issue #24136) 531 // TODO(sigmund): switch back to null aware operators (issue #24136)
525 var size = _nodeToSize[node]; 532 var size = _nodeToSize[node];
526 return size == null ? 0 : size; 533 return size == null ? 0 : size;
527 } 534 }
528 535
529 String codeOf(Element element) { 536 String codeOf(Entity entity) {
530 List<jsAst.Node> code = _elementToNodes[element]; 537 List<jsAst.Node> code = _entityToNodes[entity];
531 if (code == null) return null; 538 if (code == null) return null;
532 // Concatenate rendered ASTs. 539 // Concatenate rendered ASTs.
533 StringBuffer sb = new StringBuffer(); 540 StringBuffer sb = new StringBuffer();
534 for (jsAst.Node ast in code) { 541 for (jsAst.Node ast in code) {
535 sb.writeln(jsAst.prettyPrint(ast, compiler.options)); 542 sb.writeln(jsAst.prettyPrint(ast, compiler.options));
536 } 543 }
537 return sb.toString(); 544 return sb.toString();
538 } 545 }
539 546
540 void dumpInfo(ClosedWorld closedWorld) { 547 void dumpInfo(ClosedWorld closedWorld) {
(...skipping 11 matching lines...) Expand all
552 } 559 }
553 560
554 void dumpInfoJson(StringSink buffer, ClosedWorld closedWorld) { 561 void dumpInfoJson(StringSink buffer, ClosedWorld closedWorld) {
555 JsonEncoder encoder = const JsonEncoder.withIndent(' '); 562 JsonEncoder encoder = const JsonEncoder.withIndent(' ');
556 Stopwatch stopwatch = new Stopwatch(); 563 Stopwatch stopwatch = new Stopwatch();
557 stopwatch.start(); 564 stopwatch.start();
558 565
559 AllInfo result = infoCollector.result; 566 AllInfo result = infoCollector.result;
560 567
561 // Recursively build links to function uses 568 // Recursively build links to function uses
562 Iterable<Entity> functionElements = 569 Iterable<Entity> functionEntities =
563 infoCollector._elementToInfo.keys.where((k) => k is FunctionElement); 570 infoCollector._entityToInfo.keys.where((k) => k is FunctionEntity);
564 for (FunctionElement element in functionElements) { 571 for (FunctionEntity entity in functionEntities) {
565 FunctionInfo info = infoCollector._elementToInfo[element]; 572 FunctionInfo info = infoCollector._entityToInfo[entity];
566 Iterable<Selection> uses = getRetaining(element, closedWorld); 573 Iterable<Selection> uses = getRetaining(entity, closedWorld);
567 // Don't bother recording an empty list of dependencies. 574 // Don't bother recording an empty list of dependencies.
568 for (Selection selection in uses) { 575 for (Selection selection in uses) {
569 // Don't register dart2js builtin functions that are not recorded. 576 // Don't register dart2js builtin functions that are not recorded.
570 Info useInfo = infoCollector._elementToInfo[selection.selectedElement]; 577 Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
571 if (useInfo == null) continue; 578 if (useInfo == null) continue;
572 info.uses.add(new DependencyInfo(useInfo, '${selection.mask}')); 579 info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
573 } 580 }
574 } 581 }
575 582
576 // Recursively build links to field uses 583 // Recursively build links to field uses
577 Iterable<Entity> fieldElements = 584 Iterable<Entity> fieldEntity =
578 infoCollector._elementToInfo.keys.where((k) => k is FieldElement); 585 infoCollector._entityToInfo.keys.where((k) => k is FieldEntity);
579 for (FieldElement element in fieldElements) { 586 for (FieldEntity entity in fieldEntity) {
580 FieldInfo info = infoCollector._elementToInfo[element]; 587 FieldInfo info = infoCollector._entityToInfo[entity];
581 Iterable<Selection> uses = getRetaining(element, closedWorld); 588 Iterable<Selection> uses = getRetaining(entity, closedWorld);
582 // Don't bother recording an empty list of dependencies. 589 // Don't bother recording an empty list of dependencies.
583 for (Selection selection in uses) { 590 for (Selection selection in uses) {
584 Info useInfo = infoCollector._elementToInfo[selection.selectedElement]; 591 Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
585 if (useInfo == null) continue; 592 if (useInfo == null) continue;
586 info.uses.add(new DependencyInfo(useInfo, '${selection.mask}')); 593 info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
587 } 594 }
588 } 595 }
589 596
590 // Notify the impact strategy impacts are no longer needed for dump info. 597 // Notify the impact strategy impacts are no longer needed for dump info.
591 compiler.impactStrategy.onImpactUsed(IMPACT_USE); 598 compiler.impactStrategy.onImpactUsed(IMPACT_USE);
592 599
593 // Track dependencies that come from inlining. 600 // Track dependencies that come from inlining.
594 for (Element element in inlineMap.keys) { 601 for (Entity entity in inlineMap.keys) {
595 CodeInfo outerInfo = infoCollector._elementToInfo[element]; 602 CodeInfo outerInfo = infoCollector._entityToInfo[entity];
596 if (outerInfo == null) continue; 603 if (outerInfo == null) continue;
597 for (Element inlined in inlineMap[element]) { 604 for (Entity inlined in inlineMap[entity]) {
598 Info inlinedInfo = infoCollector._elementToInfo[inlined]; 605 Info inlinedInfo = infoCollector._entityToInfo[inlined];
599 if (inlinedInfo == null) continue; 606 if (inlinedInfo == null) continue;
600 outerInfo.uses.add(new DependencyInfo(inlinedInfo, 'inlined')); 607 outerInfo.uses.add(new DependencyInfo(inlinedInfo, 'inlined'));
601 } 608 }
602 } 609 }
603 610
604 result.deferredFiles = compiler.deferredLoadTask.computeDeferredMap(); 611 result.deferredFiles = compiler.deferredLoadTask.computeDeferredMap();
605 stopwatch.stop(); 612 stopwatch.stop();
606 result.program = new ProgramInfo( 613 result.program = new ProgramInfo(
607 entrypoint: infoCollector 614 entrypoint: infoCollector
608 ._elementToInfo[closedWorld.elementEnvironment.mainFunction], 615 ._entityToInfo[closedWorld.elementEnvironment.mainFunction],
609 size: _programSize, 616 size: _programSize,
610 dart2jsVersion: 617 dart2jsVersion:
611 compiler.options.hasBuildId ? compiler.options.buildId : null, 618 compiler.options.hasBuildId ? compiler.options.buildId : null,
612 compilationMoment: new DateTime.now(), 619 compilationMoment: new DateTime.now(),
613 compilationDuration: compiler.measurer.wallClock.elapsed, 620 compilationDuration: compiler.measurer.wallClock.elapsed,
614 toJsonDuration: 621 toJsonDuration:
615 new Duration(milliseconds: stopwatch.elapsedMilliseconds), 622 new Duration(milliseconds: stopwatch.elapsedMilliseconds),
616 dumpInfoDuration: new Duration(milliseconds: this.timing), 623 dumpInfoDuration: new Duration(milliseconds: this.timing),
617 noSuchMethodEnabled: closedWorld.backendUsage.isNoSuchMethodUsed, 624 noSuchMethodEnabled: closedWorld.backendUsage.isNoSuchMethodUsed,
618 minified: compiler.options.enableMinification); 625 minified: compiler.options.enableMinification);
619 626
620 ChunkedConversionSink<Object> sink = encoder.startChunkedConversion( 627 ChunkedConversionSink<Object> sink = encoder.startChunkedConversion(
621 new StringConversionSink.fromStringSink(buffer)); 628 new StringConversionSink.fromStringSink(buffer));
622 sink.add(new AllInfoJsonCodec().encode(result)); 629 sink.add(new AllInfoJsonCodec().encode(result));
623 compiler.reporter.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, { 630 compiler.reporter.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
624 'text': "View the dumped .info.json file at " 631 'text': "View the dumped .info.json file at "
625 "https://dart-lang.github.io/dump-info-visualizer" 632 "https://dart-lang.github.io/dump-info-visualizer"
626 }); 633 });
627 } 634 }
628 } 635 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698