Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, 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 summary_resynthesizer; | |
| 6 | |
| 7 import 'package:analyzer/analyzer.dart'; | |
| 8 import 'package:analyzer/src/generated/element.dart'; | |
| 9 import 'package:analyzer/src/generated/element_handle.dart'; | |
| 10 import 'package:analyzer/src/generated/engine.dart'; | |
| 11 import 'package:analyzer/src/generated/resolver.dart'; | |
| 12 import 'package:analyzer/src/generated/source_io.dart'; | |
| 13 import 'package:analyzer/src/summary/format.dart'; | |
| 14 | |
| 15 /** | |
| 16 * Callback used by [SummaryResynthesizer] to obtain the summary for a given | |
| 17 * URI. | |
| 18 */ | |
| 19 typedef PrelinkedLibrary GetSummaryCallback(String uri); | |
| 20 | |
| 21 /** | |
| 22 * Specialization of [FunctionTypeImpl] used for function types resynthesized | |
| 23 * from summaries. | |
| 24 */ | |
| 25 class ResynthesizedFunctionTypeImpl extends FunctionTypeImpl | |
| 26 with ResynthesizedType { | |
| 27 final SummaryResynthesizer summaryResynthesizer; | |
| 28 | |
| 29 ResynthesizedFunctionTypeImpl( | |
| 30 FunctionTypeAliasElement element, String name, this.summaryResynthesizer) | |
| 31 : super.elementWithName(element, name); | |
| 32 | |
| 33 int get _numTypeParameters { | |
| 34 FunctionTypeAliasElement element = this.element; | |
| 35 return element.typeParameters.length; | |
| 36 } | |
| 37 } | |
| 38 | |
| 39 /** | |
| 40 * Specialization of [InterfaceTypeImpl] used for interface types resynthesized | |
| 41 * from summaries. | |
| 42 */ | |
| 43 class ResynthesizedInterfaceTypeImpl extends InterfaceTypeImpl | |
| 44 with ResynthesizedType { | |
| 45 final SummaryResynthesizer summaryResynthesizer; | |
| 46 | |
| 47 ResynthesizedInterfaceTypeImpl( | |
| 48 ClassElement element, String name, this.summaryResynthesizer) | |
| 49 : super.elementWithName(element, name); | |
| 50 | |
| 51 int get _numTypeParameters => element.typeParameters.length; | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * Common code for types resynthesized from summaries. This code takes care of | |
| 56 * filling in the appropriate number of copies of `dynamic` when it is queried | |
| 57 * for type parameters on a bare type reference (i.e. it converts `List` to | |
| 58 * `List<dynamic>`). | |
| 59 */ | |
| 60 abstract class ResynthesizedType implements DartType { | |
| 61 /** | |
| 62 * The type arguments, if known. Otherwise `null`. | |
| 63 */ | |
| 64 List<DartType> _typeArguments; | |
| 65 | |
| 66 SummaryResynthesizer get summaryResynthesizer; | |
| 67 | |
| 68 List<DartType> get typeArguments { | |
| 69 if (_typeArguments == null) { | |
| 70 // Default to replicating "dynamic" as many times as the class element | |
| 71 // requires. | |
| 72 _typeArguments = new List<DartType>.filled( | |
| 73 _numTypeParameters, summaryResynthesizer.typeProvider.dynamicType); | |
| 74 } | |
| 75 return _typeArguments; | |
| 76 } | |
| 77 | |
| 78 int get _numTypeParameters; | |
| 79 } | |
| 80 | |
| 81 /** | |
| 82 * Implementation of [ElementResynthesizer] used when resynthesizing an element | |
| 83 * model from summaries. | |
| 84 */ | |
| 85 class SummaryResynthesizer extends ElementResynthesizer { | |
| 86 /** | |
| 87 * Callback used to obtain the summary for a given URI. | |
| 88 */ | |
| 89 final GetSummaryCallback getSummary; | |
| 90 | |
| 91 /** | |
| 92 * Source factory used to convert URIs to [Source] objects. | |
| 93 */ | |
| 94 final SourceFactory sourceFactory; | |
| 95 | |
| 96 /** | |
| 97 * Cache of [Source] objects that have already been converted from URIs. | |
| 98 */ | |
| 99 final Map<String, Source> _sources = <String, Source>{}; | |
| 100 | |
| 101 /** | |
| 102 * The [TypeProvider] used to obtain core types (such as Object, int, List, | |
| 103 * and dynamic) during resynthesis. | |
| 104 * | |
| 105 * TODO(paulberry): will this create a chicken-and-egg problem when trying to | |
| 106 * resynthesize the core library from summaries? | |
| 107 */ | |
| 108 final TypeProvider typeProvider; | |
| 109 | |
| 110 /** | |
| 111 * Map of top level elements resynthesized from summaries. The three map | |
| 112 * keys are the first three elements of the element's location (the library | |
| 113 * URI, the compilation unit URI, and the name of the top level declaration). | |
| 114 */ | |
| 115 final Map<String, Map<String, Map<String, Element>>> _resynthesizedElements = | |
| 116 <String, Map<String, Map<String, Element>>>{}; | |
| 117 | |
| 118 /** | |
| 119 * Map of libraries which have been resynthesized from summaries. The map | |
| 120 * key is the library URI. | |
| 121 */ | |
| 122 final Map<String, LibraryElement> _resynthesizedLibraries = | |
| 123 <String, LibraryElement>{}; | |
| 124 | |
| 125 SummaryResynthesizer( | |
| 126 AnalysisContext context, this.getSummary, this.sourceFactory) | |
| 127 : super(context), | |
| 128 typeProvider = context.typeProvider; | |
| 129 | |
| 130 /** | |
| 131 * Number of libraries that have been resynthesized so far. | |
| 132 */ | |
| 133 int get resynthesisCount => _resynthesizedLibraries.length; | |
| 134 | |
| 135 @override | |
| 136 Element getElement(ElementLocation location) { | |
| 137 if (location.components.length == 1) { | |
| 138 return getLibraryElement(location.components[0]); | |
| 139 } else if (location.components.length == 3) { | |
| 140 String uri = location.components[0]; | |
| 141 Map<String, Map<String, Element>> libraryMap = | |
| 142 _resynthesizedElements[uri]; | |
| 143 if (libraryMap == null) { | |
| 144 getLibraryElement(uri); | |
| 145 libraryMap = _resynthesizedElements[uri]; | |
| 146 assert(libraryMap != null); | |
| 147 } | |
| 148 Element element = libraryMap[location.components[1]] | |
|
scheglov
2015/12/16 01:46:49
Can this expression be null?
Paul Berry
2015/12/16 16:34:20
Good point. It could happen, if files are updated
| |
| 149 [location.components[2]]; | |
| 150 if (element == null) { | |
| 151 throw new Exception('Failed to resynthesize $location'); | |
| 152 } | |
| 153 assert(element != null); | |
| 154 return element; | |
| 155 } else { | |
| 156 throw new UnimplementedError(location.toString()); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 * Get the [LibraryElement] for the given [uri], resynthesizing it if it | |
| 162 * hasn't been resynthesized already. | |
| 163 */ | |
| 164 LibraryElement getLibraryElement(String uri) { | |
| 165 return _resynthesizedLibraries.putIfAbsent(uri, () { | |
| 166 PrelinkedLibrary serializedLibrary = getSummary(uri); | |
| 167 _LibraryResynthesizer libraryResynthesizer = | |
| 168 new _LibraryResynthesizer(this, serializedLibrary, _getSource(uri)); | |
| 169 LibraryElement library = libraryResynthesizer.buildLibrary(); | |
| 170 _resynthesizedElements[uri] = libraryResynthesizer.resummarizedElements; | |
| 171 return library; | |
| 172 }); | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * Get the [Source] object for the given [uri]. | |
| 177 */ | |
| 178 Source _getSource(String uri) { | |
| 179 return _sources.putIfAbsent(uri, () => sourceFactory.forUri(uri)); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 /** | |
| 184 * An instance of [_LibraryResynthesizer] is responsible for resynthesizing the | |
| 185 * elements in a single library from that library's summary. | |
| 186 */ | |
| 187 class _LibraryResynthesizer { | |
| 188 /** | |
| 189 * The [SummaryResynthesizer] which is being used to obtain summaries. | |
| 190 */ | |
| 191 final SummaryResynthesizer summaryResynthesizer; | |
| 192 | |
| 193 /** | |
| 194 * The library to be resynthesized. | |
| 195 */ | |
| 196 final PrelinkedLibrary prelinkedLibrary; | |
| 197 | |
| 198 /** | |
| 199 * [Source] object for the library to be resynthesized. | |
| 200 */ | |
| 201 final Source librarySource; | |
| 202 | |
| 203 /** | |
| 204 * [ElementHolder] into which resynthesized elements should be placed. This | |
| 205 * object is recreated afresh for each unit in the library, and is used to | |
| 206 * populate the [CompilationUnitElement]. | |
| 207 */ | |
| 208 ElementHolder unitHolder; | |
| 209 | |
| 210 /** | |
| 211 * The [PrelinkedUnit] from which elements are currently being resynthesized. | |
| 212 */ | |
| 213 PrelinkedUnit prelinkedUnit; | |
| 214 | |
| 215 /** | |
| 216 * Map of top level elements that have been resynthesized so far. The first | |
| 217 * key is the URI of the compilation unit; the second is the name of the top | |
| 218 * level element. | |
| 219 */ | |
| 220 final Map<String, Map<String, Element>> resummarizedElements = | |
| 221 <String, Map<String, Element>>{}; | |
| 222 | |
| 223 /** | |
| 224 * Type parameters for the class or typedef currently being resynthesized. | |
| 225 * | |
| 226 * TODO(paulberry): extend this to do the right thing for generic methods. | |
| 227 */ | |
| 228 List<TypeParameterElement> currentTypeParameters; | |
| 229 | |
| 230 _LibraryResynthesizer(this.summaryResynthesizer, | |
| 231 PrelinkedLibrary serializedLibrary, this.librarySource) | |
| 232 : prelinkedLibrary = serializedLibrary; | |
| 233 | |
| 234 /** | |
| 235 * Resynthesize a [ClassElement] and place it in [unitHolder]. | |
| 236 */ | |
| 237 void buildClass(UnlinkedClass serializedClass) { | |
| 238 try { | |
| 239 currentTypeParameters = | |
| 240 serializedClass.typeParameters.map(buildTypeParameter).toList(); | |
| 241 for (int i = 0; i < serializedClass.typeParameters.length; i++) { | |
| 242 finishTypeParameter( | |
| 243 serializedClass.typeParameters[i], currentTypeParameters[i]); | |
| 244 } | |
| 245 ClassElementImpl classElement; | |
| 246 if (serializedClass.isMixinApplication) { | |
| 247 classElement = new ClassElementImpl(serializedClass.name, -1); | |
| 248 classElement.setModifier(Modifier.MIXIN_APPLICATION, true); | |
| 249 } else { | |
| 250 classElement = new ClassElementImpl(serializedClass.name, -1); | |
|
Brian Wilkerson
2015/12/16 00:29:07
Unless I'm missing something, this line is inside
Paul Berry
2015/12/16 16:34:20
Oops, you're right. Fixed.
| |
| 251 } | |
| 252 InterfaceTypeImpl correspondingType = new InterfaceTypeImpl(classElement); | |
| 253 if (serializedClass.supertype != null) { | |
| 254 classElement.supertype = buildType(serializedClass.supertype); | |
| 255 } else { | |
| 256 // TODO(paulberry): don't make Object point to itself. | |
| 257 classElement.supertype = summaryResynthesizer.typeProvider.objectType; | |
| 258 } | |
| 259 classElement.interfaces = | |
| 260 serializedClass.interfaces.map(buildType).toList(); | |
| 261 classElement.mixins = serializedClass.mixins.map(buildType).toList(); | |
| 262 classElement.typeParameters = currentTypeParameters; | |
| 263 ElementHolder memberHolder = new ElementHolder(); | |
| 264 bool constructorFound = false; | |
| 265 for (UnlinkedExecutable serializedExecutable | |
| 266 in serializedClass.executables) { | |
| 267 switch (serializedExecutable.kind) { | |
| 268 case UnlinkedExecutableKind.constructor: | |
| 269 constructorFound = true; | |
| 270 buildConstructor(serializedExecutable, memberHolder); | |
| 271 break; | |
| 272 case UnlinkedExecutableKind.functionOrMethod: | |
| 273 case UnlinkedExecutableKind.getter: | |
| 274 case UnlinkedExecutableKind.setter: | |
| 275 buildExecutable(serializedExecutable, memberHolder); | |
| 276 break; | |
| 277 } | |
| 278 } | |
| 279 for (UnlinkedVariable serializedVariable in serializedClass.fields) { | |
| 280 buildVariable(serializedVariable, memberHolder); | |
| 281 } | |
| 282 if (!serializedClass.isMixinApplication) { | |
| 283 if (!constructorFound) { | |
| 284 // Synthesize implicit constructors. | |
| 285 ConstructorElementImpl constructor = | |
| 286 new ConstructorElementImpl('', -1); | |
| 287 constructor.setModifier(Modifier.SYNTHETIC, true); | |
| 288 constructor.returnType = correspondingType; | |
| 289 constructor.type = new FunctionTypeImpl(constructor); | |
| 290 memberHolder.addConstructor(constructor); | |
| 291 } | |
| 292 classElement.constructors = memberHolder.constructors; | |
| 293 } | |
| 294 classElement.accessors = memberHolder.accessors; | |
| 295 classElement.fields = memberHolder.fields; | |
| 296 classElement.methods = memberHolder.methods; | |
| 297 correspondingType.typeArguments = | |
| 298 currentTypeParameters.map((param) => param.type).toList(); | |
| 299 classElement.type = correspondingType; | |
| 300 unitHolder.addType(classElement); | |
| 301 } finally { | |
| 302 currentTypeParameters = null; | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 /** | |
| 307 * Resynthesize a [NamespaceCombinator]. | |
| 308 */ | |
| 309 NamespaceCombinator buildCombinator(UnlinkedCombinator serializedCombinator) { | |
| 310 if (serializedCombinator.shows.isNotEmpty) { | |
| 311 ShowElementCombinatorImpl combinator = new ShowElementCombinatorImpl(); | |
| 312 combinator.shownNames = serializedCombinator.shows | |
| 313 .map((UnlinkedCombinatorName n) => n.name) | |
| 314 .toList(); | |
| 315 return combinator; | |
| 316 } else { | |
| 317 HideElementCombinatorImpl combinator = new HideElementCombinatorImpl(); | |
| 318 combinator.hiddenNames = serializedCombinator.hides | |
| 319 .map((UnlinkedCombinatorName n) => n.name) | |
| 320 .toList(); | |
| 321 return combinator; | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 /** | |
| 326 * Resynthesize a [ConstructorElement] and place it in the given [holder]. | |
| 327 */ | |
| 328 void buildConstructor( | |
| 329 UnlinkedExecutable serializedExecutable, ElementHolder holder) { | |
| 330 assert(serializedExecutable.kind == UnlinkedExecutableKind.constructor); | |
| 331 ConstructorElementImpl constructorElement = | |
| 332 new ConstructorElementImpl(serializedExecutable.name, -1); | |
| 333 buildExecutableCommonParts(constructorElement, serializedExecutable); | |
| 334 if (serializedExecutable.isFactory) { | |
| 335 constructorElement.setModifier(Modifier.FACTORY, true); | |
|
scheglov
2015/12/16 01:46:49
ConstructorElementImpl has a setter "factory".
...
Paul Berry
2015/12/16 16:34:20
Good point. I'll do a follow-up commit that repla
| |
| 336 } | |
| 337 if (serializedExecutable.isConst) { | |
| 338 constructorElement.setModifier(Modifier.CONST, true); | |
| 339 } | |
| 340 holder.addConstructor(constructorElement); | |
| 341 } | |
| 342 | |
| 343 /** | |
| 344 * Resynthesize the [ClassElement] corresponding to an enum, along with the | |
| 345 * associated fields and implicit accessors. | |
| 346 */ | |
| 347 void buildEnum(UnlinkedEnum serializedEnum) { | |
| 348 ClassElementImpl classElement = | |
| 349 new ClassElementImpl(serializedEnum.name, -1); | |
|
Brian Wilkerson
2015/12/16 00:29:07
I assume that we'll get offsets at some point.
Paul Berry
2015/12/16 16:34:20
Yes, that's the plan. I've added a TODO comment t
| |
| 350 classElement.setModifier(Modifier.ENUM, true); | |
| 351 InterfaceType enumType = new InterfaceTypeImpl(classElement); | |
| 352 classElement.type = enumType; | |
| 353 classElement.supertype = summaryResynthesizer.typeProvider.objectType; | |
| 354 ElementHolder memberHolder = new ElementHolder(); | |
| 355 FieldElementImpl indexField = new FieldElementImpl('index', -1); | |
| 356 indexField.setModifier(Modifier.FINAL, true); | |
| 357 indexField.setModifier(Modifier.SYNTHETIC, true); | |
| 358 indexField.type = summaryResynthesizer.typeProvider.intType; | |
| 359 memberHolder.addField(indexField); | |
| 360 buildImplicitAccessors(indexField, memberHolder); | |
| 361 FieldElementImpl valuesField = new ConstFieldElementImpl('values', -1); | |
| 362 valuesField.setModifier(Modifier.SYNTHETIC, true); | |
| 363 valuesField.setModifier(Modifier.CONST, true); | |
| 364 valuesField.setModifier(Modifier.STATIC, true); | |
| 365 valuesField.type = summaryResynthesizer.typeProvider.listType | |
| 366 .substitute4(<DartType>[enumType]); | |
| 367 memberHolder.addField(valuesField); | |
| 368 buildImplicitAccessors(valuesField, memberHolder); | |
| 369 for (UnlinkedEnumValue serializedEnumValue in serializedEnum.values) { | |
| 370 ConstFieldElementImpl valueField = | |
| 371 new ConstFieldElementImpl(serializedEnumValue.name, -1); | |
| 372 valueField.setModifier(Modifier.CONST, true); | |
| 373 valueField.setModifier(Modifier.STATIC, true); | |
| 374 valueField.type = enumType; | |
| 375 memberHolder.addField(valueField); | |
| 376 buildImplicitAccessors(valueField, memberHolder); | |
| 377 } | |
| 378 classElement.fields = memberHolder.fields; | |
| 379 classElement.accessors = memberHolder.accessors; | |
| 380 classElement.constructors = <ConstructorElement>[]; | |
| 381 unitHolder.addEnum(classElement); | |
| 382 } | |
| 383 | |
| 384 /** | |
| 385 * Resynthesize an [ExecutableElement] and place it in the given [holder]. | |
| 386 */ | |
| 387 void buildExecutable(UnlinkedExecutable serializedExecutable, | |
| 388 [ElementHolder holder]) { | |
| 389 bool isTopLevel = holder == null; | |
| 390 if (holder == null) { | |
| 391 holder = unitHolder; | |
| 392 } | |
| 393 String name = serializedExecutable.name; | |
| 394 if (name.endsWith('=')) { | |
| 395 name = name.substring(0, name.length - 1); | |
| 396 } | |
| 397 UnlinkedExecutableKind kind = serializedExecutable.kind; | |
| 398 switch (kind) { | |
| 399 case UnlinkedExecutableKind.functionOrMethod: | |
| 400 if (isTopLevel) { | |
| 401 FunctionElementImpl executableElement = | |
| 402 new FunctionElementImpl(name, -1); | |
| 403 buildExecutableCommonParts(executableElement, serializedExecutable); | |
| 404 holder.addFunction(executableElement); | |
| 405 } else { | |
| 406 MethodElementImpl executableElement = new MethodElementImpl(name, -1); | |
| 407 buildExecutableCommonParts(executableElement, serializedExecutable); | |
| 408 executableElement.setModifier( | |
| 409 Modifier.STATIC, serializedExecutable.isStatic); | |
| 410 holder.addMethod(executableElement); | |
| 411 } | |
| 412 break; | |
| 413 case UnlinkedExecutableKind.getter: | |
| 414 case UnlinkedExecutableKind.setter: | |
| 415 PropertyAccessorElementImpl executableElement = | |
| 416 new PropertyAccessorElementImpl(name, -1); | |
| 417 if (isTopLevel) { | |
| 418 executableElement.setModifier(Modifier.STATIC, true); | |
| 419 } else { | |
| 420 executableElement.setModifier( | |
| 421 Modifier.STATIC, serializedExecutable.isStatic); | |
| 422 } | |
| 423 buildExecutableCommonParts(executableElement, serializedExecutable); | |
| 424 DartType type; | |
| 425 if (kind == UnlinkedExecutableKind.getter) { | |
| 426 executableElement.setModifier(Modifier.GETTER, true); | |
| 427 type = executableElement.returnType; | |
| 428 } else { | |
| 429 executableElement.setModifier(Modifier.SETTER, true); | |
| 430 type = executableElement.parameters[0].type; | |
| 431 } | |
| 432 holder.addAccessor(executableElement); | |
| 433 // TODO(paulberry): consider removing implicit variables from the | |
| 434 // element model; the spec doesn't call for them, and they cause | |
| 435 // trouble when getters/setters exist in different parts. | |
| 436 PropertyInducingElementImpl implicitVariable; | |
| 437 if (isTopLevel) { | |
| 438 implicitVariable = buildImplicitTopLevelVariable(name, kind, holder); | |
| 439 } else { | |
| 440 implicitVariable = buildImplicitField(name, type, kind, holder); | |
| 441 implicitVariable.setModifier( | |
| 442 Modifier.STATIC, serializedExecutable.isStatic); | |
| 443 } | |
| 444 executableElement.variable = implicitVariable; | |
| 445 if (kind == UnlinkedExecutableKind.getter) { | |
| 446 implicitVariable.getter = executableElement; | |
| 447 } else { | |
| 448 implicitVariable.setter = executableElement; | |
| 449 } | |
| 450 // TODO(paulberry): do the right thing when getter and setter are in | |
| 451 // different units. | |
| 452 break; | |
| 453 default: | |
| 454 // The only other executable type is a constructor, and that is handled | |
| 455 // separately (in [buildConstructor]. So this code should be | |
| 456 // unreachable. | |
| 457 assert(false); | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 /** | |
| 462 * Handle the parts of an executable element that are common to constructors, | |
| 463 * functions, methods, getters, and setters. | |
| 464 */ | |
| 465 void buildExecutableCommonParts(ExecutableElementImpl executableElement, | |
| 466 UnlinkedExecutable serializedExecutable) { | |
| 467 executableElement.parameters = | |
| 468 serializedExecutable.parameters.map(buildParameter).toList(); | |
| 469 if (serializedExecutable.returnType != null) { | |
| 470 executableElement.returnType = buildType(serializedExecutable.returnType); | |
| 471 } else { | |
| 472 executableElement.returnType = VoidTypeImpl.instance; | |
| 473 } | |
| 474 executableElement.type = new FunctionTypeImpl(executableElement); | |
| 475 if (serializedExecutable.hasImplicitReturnType) { | |
| 476 executableElement.setModifier(Modifier.IMPLICIT_TYPE, true); | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 /** | |
| 481 * Resynthesize an [ExportElement], | |
| 482 */ | |
| 483 ExportElement buildExport(UnlinkedExport serializedExport) { | |
| 484 ExportElementImpl exportElement = new ExportElementImpl(0); | |
| 485 String exportedLibraryUri = summaryResynthesizer.sourceFactory | |
| 486 .resolveUri(librarySource, serializedExport.uri) | |
| 487 .uri | |
| 488 .toString(); | |
| 489 exportElement.exportedLibrary = new LibraryElementHandle( | |
| 490 summaryResynthesizer, | |
| 491 new ElementLocationImpl.con3(<String>[exportedLibraryUri])); | |
| 492 exportElement.uri = serializedExport.uri; | |
| 493 exportElement.combinators = | |
| 494 serializedExport.combinators.map(buildCombinator).toList(); | |
| 495 return exportElement; | |
| 496 } | |
| 497 | |
| 498 /** | |
| 499 * Resynthesize a [FieldElement]. | |
| 500 */ | |
| 501 FieldElement buildField(UnlinkedVariable serializedField) { | |
| 502 FieldElementImpl fieldElement = | |
| 503 new FieldElementImpl(serializedField.name, -1); | |
| 504 fieldElement.type = buildType(serializedField.type); | |
| 505 if (serializedField.isConst) { | |
| 506 fieldElement.setModifier(Modifier.CONST, true); | |
| 507 } | |
| 508 return fieldElement; | |
| 509 } | |
| 510 | |
| 511 /** | |
| 512 * Build the implicit getter and setter associated with [element], and place | |
| 513 * them in [holder]. | |
| 514 */ | |
| 515 void buildImplicitAccessors( | |
| 516 PropertyInducingElementImpl element, ElementHolder holder) { | |
| 517 String name = element.name; | |
| 518 DartType type = element.type; | |
| 519 PropertyAccessorElementImpl getter = | |
| 520 new PropertyAccessorElementImpl(name, -1); | |
| 521 getter.setModifier(Modifier.GETTER, true); | |
| 522 getter.setModifier(Modifier.STATIC, element.isStatic); | |
| 523 getter.setModifier(Modifier.SYNTHETIC, true); | |
| 524 getter.returnType = type; | |
| 525 getter.type = new FunctionTypeImpl(getter); | |
| 526 getter.variable = element; | |
| 527 holder.addAccessor(getter); | |
| 528 element.getter = getter; | |
| 529 if (!(element.isConst || element.isFinal)) { | |
| 530 PropertyAccessorElementImpl setter = | |
| 531 new PropertyAccessorElementImpl(name, -1); | |
| 532 setter.setModifier(Modifier.SETTER, true); | |
| 533 setter.setModifier(Modifier.STATIC, element.isStatic); | |
| 534 setter.setModifier(Modifier.SYNTHETIC, true); | |
| 535 setter.parameters = <ParameterElement>[ | |
| 536 new ParameterElementImpl('_$name', -1) | |
| 537 ..setModifier(Modifier.SYNTHETIC, true) | |
| 538 ..type = type | |
| 539 ..parameterKind = ParameterKind.REQUIRED | |
| 540 ]; | |
| 541 setter.returnType = VoidTypeImpl.instance; | |
| 542 setter.type = new FunctionTypeImpl(setter); | |
| 543 setter.variable = element; | |
| 544 holder.addAccessor(setter); | |
| 545 element.setter = setter; | |
| 546 } | |
| 547 } | |
| 548 | |
| 549 /** | |
| 550 * Build the implicit field associated with a getter or setter, and place it | |
| 551 * in [holder]. | |
| 552 */ | |
| 553 PropertyInducingElementImpl buildImplicitField(String name, DartType type, | |
| 554 UnlinkedExecutableKind kind, ElementHolder holder) { | |
| 555 if (holder.getField(name) == null) { | |
| 556 FieldElementImpl field = new FieldElementImpl(name, -1); | |
| 557 field.setModifier(Modifier.SYNTHETIC, true); | |
| 558 if (kind == UnlinkedExecutableKind.getter) { | |
| 559 field.setModifier(Modifier.FINAL, true); | |
| 560 } | |
| 561 field.type = type; | |
| 562 holder.addField(field); | |
| 563 return field; | |
| 564 } else { | |
| 565 // TODO(paulberry): if adding a setter where there was previously | |
| 566 // only a getter, remove "final" modifier. | |
| 567 // TODO(paulberry): what if the getter and setter have a type mismatch? | |
| 568 throw new UnimplementedError(); | |
| 569 } | |
| 570 } | |
| 571 | |
| 572 /** | |
| 573 * Build the implicit top level variable associated with a getter or setter, | |
| 574 * and place it in [holder]. | |
| 575 */ | |
| 576 PropertyInducingElementImpl buildImplicitTopLevelVariable( | |
| 577 String name, UnlinkedExecutableKind kind, ElementHolder holder) { | |
| 578 if (holder.getTopLevelVariable(name) == null) { | |
| 579 TopLevelVariableElementImpl variable = | |
| 580 new TopLevelVariableElementImpl(name, -1); | |
| 581 variable.setModifier(Modifier.SYNTHETIC, true); | |
| 582 if (kind == UnlinkedExecutableKind.getter) { | |
| 583 variable.setModifier(Modifier.FINAL, true); | |
| 584 } | |
| 585 holder.addTopLevelVariable(variable); | |
| 586 return variable; | |
| 587 } else { | |
| 588 // TODO(paulberry): if adding a setter where there was previously | |
| 589 // only a getter, remove "final" modifier. | |
| 590 // TODO(paulberry): what if the getter and setter have a type mismatch? | |
| 591 throw new UnimplementedError(); | |
| 592 } | |
| 593 } | |
| 594 | |
| 595 /** | |
| 596 * Resynthesize an [ImportElement]. | |
| 597 */ | |
| 598 ImportElement buildImport(UnlinkedImport serializedImport, int dependency) { | |
| 599 bool isSynthetic = serializedImport.isImplicit; | |
| 600 // TODO(paulberry): it seems problematic for the offset to be 0 for | |
| 601 // non-synthetic imports, since it is used to disambiguate location. | |
| 602 ImportElementImpl importElement = | |
| 603 new ImportElementImpl(isSynthetic ? -1 : serializedImport.offset); | |
| 604 String absoluteUri = summaryResynthesizer.sourceFactory | |
| 605 .resolveUri( | |
| 606 librarySource, prelinkedLibrary.dependencies[dependency].uri) | |
| 607 .uri | |
| 608 .toString(); | |
| 609 importElement.importedLibrary = new LibraryElementHandle( | |
| 610 summaryResynthesizer, | |
| 611 new ElementLocationImpl.con3(<String>[absoluteUri])); | |
| 612 if (isSynthetic) { | |
| 613 importElement.setModifier(Modifier.SYNTHETIC, true); | |
| 614 } else { | |
| 615 importElement.uri = serializedImport.uri; | |
| 616 } | |
| 617 if (serializedImport.prefixReference != 0) { | |
| 618 UnlinkedReference serializedPrefix = prelinkedLibrary.units[0] | |
| 619 .unlinked | |
| 620 .references[serializedImport.prefixReference]; | |
| 621 importElement.prefix = new PrefixElementImpl(serializedPrefix.name, -1); | |
| 622 } | |
| 623 importElement.combinators = | |
| 624 serializedImport.combinators.map(buildCombinator).toList(); | |
| 625 return importElement; | |
| 626 } | |
| 627 | |
| 628 /** | |
| 629 * Main entry point. Resynthesize the [LibraryElement] and return it. | |
| 630 */ | |
| 631 LibraryElement buildLibrary() { | |
| 632 // TODO(paulberry): is it ok to pass -1 for offset and nameLength? | |
| 633 LibraryElementImpl libraryElement = new LibraryElementImpl( | |
| 634 summaryResynthesizer.context, | |
| 635 prelinkedLibrary.units[0].unlinked.libraryName, | |
| 636 -1, | |
| 637 -1); | |
| 638 CompilationUnitElementImpl definingCompilationUnit = | |
| 639 new CompilationUnitElementImpl(librarySource.shortName); | |
| 640 libraryElement.definingCompilationUnit = definingCompilationUnit; | |
| 641 definingCompilationUnit.source = librarySource; | |
| 642 definingCompilationUnit.librarySource = librarySource; | |
| 643 List<CompilationUnitElement> parts = <CompilationUnitElement>[]; | |
| 644 UnlinkedUnit unlinkedDefiningUnit = prelinkedLibrary.units[0].unlinked; | |
| 645 assert( | |
| 646 unlinkedDefiningUnit.parts.length + 1 == prelinkedLibrary.units.length); | |
| 647 for (int i = 1; i < prelinkedLibrary.units.length; i++) { | |
| 648 CompilationUnitElementImpl part = buildPart( | |
| 649 unlinkedDefiningUnit.parts[i - 1].uri, | |
| 650 prelinkedLibrary.units[i].unlinked); | |
| 651 parts.add(part); | |
| 652 } | |
| 653 libraryElement.parts = parts; | |
| 654 List<ImportElement> imports = <ImportElement>[]; | |
| 655 for (int i = 0; i < unlinkedDefiningUnit.imports.length; i++) { | |
| 656 imports.add(buildImport(unlinkedDefiningUnit.imports[i], | |
| 657 prelinkedLibrary.importDependencies[i])); | |
| 658 } | |
| 659 libraryElement.imports = imports; | |
| 660 libraryElement.exports = | |
| 661 unlinkedDefiningUnit.exports.map(buildExport).toList(); | |
| 662 populateUnit(definingCompilationUnit, 0); | |
| 663 for (int i = 0; i < parts.length; i++) { | |
| 664 populateUnit(parts[i], i + 1); | |
| 665 } | |
| 666 return libraryElement; | |
| 667 } | |
| 668 | |
| 669 /** | |
| 670 * Resynthesize a [ParameterElement]. | |
| 671 */ | |
| 672 ParameterElement buildParameter(UnlinkedParam serializedParameter) { | |
| 673 ParameterElementImpl parameterElement = | |
| 674 new ParameterElementImpl(serializedParameter.name, -1); | |
| 675 if (serializedParameter.isFunctionTyped) { | |
| 676 FunctionElementImpl parameterTypeElement = | |
| 677 new FunctionElementImpl('', -1); | |
| 678 parameterTypeElement.setModifier(Modifier.SYNTHETIC, true); | |
| 679 parameterElement.parameters = | |
| 680 serializedParameter.parameters.map(buildParameter).toList(); | |
| 681 parameterTypeElement.enclosingElement = parameterElement; | |
| 682 parameterTypeElement.shareParameters(parameterElement.parameters); | |
| 683 if (serializedParameter.type != null) { | |
| 684 parameterTypeElement.returnType = buildType(serializedParameter.type); | |
| 685 } else { | |
| 686 parameterTypeElement.returnType = VoidTypeImpl.instance; | |
| 687 } | |
| 688 parameterElement.type = new FunctionTypeImpl(parameterTypeElement); | |
| 689 } else { | |
| 690 parameterElement.type = buildType(serializedParameter.type); | |
| 691 if (serializedParameter.hasImplicitType) { | |
| 692 parameterElement.setModifier(Modifier.IMPLICIT_TYPE, true); | |
| 693 } | |
| 694 } | |
| 695 switch (serializedParameter.kind) { | |
| 696 case UnlinkedParamKind.named: | |
| 697 parameterElement.parameterKind = ParameterKind.NAMED; | |
| 698 break; | |
| 699 case UnlinkedParamKind.positional: | |
| 700 parameterElement.parameterKind = ParameterKind.POSITIONAL; | |
| 701 break; | |
| 702 case UnlinkedParamKind.required: | |
| 703 parameterElement.parameterKind = ParameterKind.REQUIRED; | |
| 704 break; | |
| 705 } | |
| 706 return parameterElement; | |
| 707 } | |
| 708 | |
| 709 /** | |
| 710 * Create, but do not populate, the [CompilationUnitElement] for a part other | |
| 711 * than the defining compilation unit. | |
| 712 */ | |
| 713 CompilationUnitElementImpl buildPart( | |
| 714 String uri, UnlinkedUnit serializedPart) { | |
| 715 Source unitSource = | |
| 716 summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri); | |
| 717 CompilationUnitElementImpl partUnit = | |
| 718 new CompilationUnitElementImpl(unitSource.shortName); | |
| 719 partUnit.source = unitSource; | |
| 720 partUnit.librarySource = librarySource; | |
| 721 partUnit.uri = uri; | |
| 722 return partUnit; | |
| 723 } | |
| 724 | |
| 725 /** | |
| 726 * Build a [DartType] object based on an [UnlinkedTypeRef]. This [DartType] | |
| 727 * may refer to elements in other libraries than the library being | |
| 728 * deserialized, so handles are used to avoid having to deserialize other | |
| 729 * libraries in the process. | |
| 730 */ | |
| 731 DartType buildType(UnlinkedTypeRef type) { | |
| 732 if (type.paramReference != 0) { | |
| 733 // TODO(paulberry): make this work for generic methods. | |
| 734 return currentTypeParameters[ | |
| 735 currentTypeParameters.length - type.paramReference].type; | |
| 736 } else { | |
| 737 // TODO(paulberry): handle references to things other than classes (note: | |
| 738 // this should only occur in the case of erroneous code). | |
| 739 // TODO(paulberry): test reference to something inside a part. | |
| 740 // TODO(paulberry): test reference to something inside a part of the | |
| 741 // current lib. | |
| 742 UnlinkedReference reference = | |
| 743 prelinkedUnit.unlinked.references[type.reference]; | |
| 744 PrelinkedReference referenceResolution = | |
| 745 prelinkedUnit.references[type.reference]; | |
| 746 String referencedLibraryUri; | |
| 747 String partUri; | |
| 748 if (referenceResolution.dependency != 0) { | |
| 749 PrelinkedDependency dependency = | |
| 750 prelinkedLibrary.dependencies[referenceResolution.dependency]; | |
| 751 Source referencedLibrarySource = summaryResynthesizer.sourceFactory | |
| 752 .resolveUri(librarySource, dependency.uri); | |
| 753 referencedLibraryUri = referencedLibrarySource.uri.toString(); | |
| 754 PrelinkedLibrary referencedLibrary = | |
| 755 summaryResynthesizer.getSummary(referencedLibraryUri); | |
| 756 // TODO(paulberry): consider changing Location format so that this is | |
| 757 // not necessary (2nd string in location should just be the unit | |
| 758 // number). | |
| 759 if (referenceResolution.unit != 0) { | |
| 760 String uri = referencedLibrary.units[0].unlinked.parts[0].uri; | |
| 761 Source partSource = summaryResynthesizer.sourceFactory | |
| 762 .resolveUri(referencedLibrarySource, uri); | |
| 763 partUri = partSource.uri.toString(); | |
| 764 } else { | |
| 765 partUri = referencedLibraryUri; | |
| 766 } | |
| 767 } else if (referenceResolution.kind == | |
| 768 PrelinkedReferenceKind.unresolved) { | |
| 769 return summaryResynthesizer.typeProvider.undefinedType; | |
| 770 } else if (reference.name.isEmpty) { | |
| 771 return summaryResynthesizer.typeProvider.dynamicType; | |
| 772 } else { | |
| 773 referencedLibraryUri = librarySource.uri.toString(); | |
| 774 if (referenceResolution.unit != 0) { | |
| 775 String uri = prelinkedLibrary.units[0].unlinked.parts[ | |
| 776 referenceResolution.unit - 1].uri; | |
| 777 Source partSource = | |
| 778 summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri); | |
| 779 partUri = partSource.uri.toString(); | |
| 780 } else { | |
| 781 partUri = referencedLibraryUri; | |
| 782 } | |
| 783 } | |
| 784 ResynthesizedType resynthesizedType; | |
| 785 ElementLocationImpl location = new ElementLocationImpl.con3( | |
| 786 <String>[referencedLibraryUri, partUri, reference.name]); | |
| 787 switch (referenceResolution.kind) { | |
| 788 case PrelinkedReferenceKind.classOrEnum: | |
| 789 resynthesizedType = new ResynthesizedInterfaceTypeImpl( | |
| 790 new ClassElementHandle(summaryResynthesizer, location), | |
| 791 reference.name, | |
| 792 summaryResynthesizer); | |
| 793 break; | |
| 794 case PrelinkedReferenceKind.typedef: | |
| 795 resynthesizedType = new ResynthesizedFunctionTypeImpl( | |
| 796 new FunctionTypeAliasElementHandle( | |
| 797 summaryResynthesizer, location), | |
| 798 reference.name, | |
| 799 summaryResynthesizer); | |
| 800 break; | |
| 801 default: | |
| 802 // TODO(paulberry): figure out how to handle this case (which should | |
| 803 // only occur in the event of erroneous code). | |
| 804 throw new UnimplementedError(); | |
| 805 } | |
| 806 if (type.typeArguments.isNotEmpty) { | |
| 807 resynthesizedType._typeArguments = | |
| 808 type.typeArguments.map(buildType).toList(); | |
| 809 } | |
| 810 return resynthesizedType; | |
| 811 } | |
| 812 } | |
| 813 | |
| 814 /** | |
| 815 * Resynthesize a [FunctionTypeAliasElement] and place it in the | |
| 816 * [unitHolder]. | |
| 817 */ | |
| 818 void buildTypedef(UnlinkedTypedef serializedTypedef) { | |
| 819 try { | |
| 820 currentTypeParameters = | |
| 821 serializedTypedef.typeParameters.map(buildTypeParameter).toList(); | |
| 822 for (int i = 0; i < serializedTypedef.typeParameters.length; i++) { | |
| 823 finishTypeParameter( | |
| 824 serializedTypedef.typeParameters[i], currentTypeParameters[i]); | |
| 825 } | |
| 826 FunctionTypeAliasElementImpl functionTypeAliasElement = | |
| 827 new FunctionTypeAliasElementImpl(serializedTypedef.name, -1); | |
| 828 functionTypeAliasElement.parameters = | |
| 829 serializedTypedef.parameters.map(buildParameter).toList(); | |
| 830 if (serializedTypedef.returnType != null) { | |
| 831 functionTypeAliasElement.returnType = | |
| 832 buildType(serializedTypedef.returnType); | |
| 833 } else { | |
| 834 functionTypeAliasElement.returnType = VoidTypeImpl.instance; | |
| 835 } | |
| 836 functionTypeAliasElement.type = | |
| 837 new FunctionTypeImpl.forTypedef(functionTypeAliasElement); | |
| 838 functionTypeAliasElement.typeParameters = currentTypeParameters; | |
| 839 unitHolder.addTypeAlias(functionTypeAliasElement); | |
| 840 } finally { | |
| 841 currentTypeParameters = null; | |
| 842 } | |
| 843 } | |
| 844 | |
| 845 /** | |
| 846 * Resynthesize a [TypeParameterElement], handling all parts of its except | |
| 847 * its bound. | |
| 848 * | |
| 849 * The bound is deferred until later since it may refer to other type | |
| 850 * parameters that have not been resynthesized yet. To handle the bound, | |
| 851 * call [finishTypeParameter]. | |
| 852 */ | |
| 853 TypeParameterElement buildTypeParameter( | |
| 854 UnlinkedTypeParam serializedTypeParameter) { | |
| 855 TypeParameterElementImpl typeParameterElement = | |
| 856 new TypeParameterElementImpl(serializedTypeParameter.name, -1); | |
| 857 typeParameterElement.type = new TypeParameterTypeImpl(typeParameterElement); | |
| 858 return typeParameterElement; | |
| 859 } | |
| 860 | |
| 861 /** | |
| 862 * Resynthesize a [TopLevelVariableElement] or [FieldElement]. | |
| 863 */ | |
| 864 void buildVariable(UnlinkedVariable serializedVariable, | |
| 865 [ElementHolder holder]) { | |
| 866 if (holder == null) { | |
| 867 TopLevelVariableElementImpl element = | |
| 868 new TopLevelVariableElementImpl(serializedVariable.name, -1); | |
| 869 buildVariableCommonParts(element, serializedVariable); | |
| 870 unitHolder.addTopLevelVariable(element); | |
| 871 buildImplicitAccessors(element, unitHolder); | |
| 872 } else { | |
| 873 FieldElementImpl element = | |
| 874 new FieldElementImpl(serializedVariable.name, -1); | |
| 875 buildVariableCommonParts(element, serializedVariable); | |
| 876 holder.addField(element); | |
| 877 buildImplicitAccessors(element, holder); | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 /** | |
| 882 * Handle the parts that are common to top level variables and fields. | |
| 883 */ | |
| 884 void buildVariableCommonParts(PropertyInducingElementImpl element, | |
| 885 UnlinkedVariable serializedVariable) { | |
| 886 element.type = buildType(serializedVariable.type); | |
| 887 element.setModifier(Modifier.CONST, serializedVariable.isConst); | |
| 888 element.setModifier(Modifier.STATIC, serializedVariable.isStatic); | |
| 889 } | |
| 890 | |
| 891 /** | |
| 892 * Finish creating a [TypeParameterElement] by deserializing its bound. | |
| 893 */ | |
| 894 void finishTypeParameter(UnlinkedTypeParam serializedTypeParameter, | |
| 895 TypeParameterElementImpl typeParameterElement) { | |
| 896 if (serializedTypeParameter.bound != null) { | |
| 897 typeParameterElement.bound = buildType(serializedTypeParameter.bound); | |
| 898 } | |
| 899 } | |
| 900 | |
| 901 /** | |
| 902 * Populate a [CompilationUnitElement] by deserializing all the elements | |
| 903 * contained in it. | |
| 904 */ | |
| 905 void populateUnit(CompilationUnitElementImpl unit, int unitNum) { | |
| 906 prelinkedUnit = prelinkedLibrary.units[unitNum]; | |
| 907 unitHolder = new ElementHolder(); | |
| 908 UnlinkedUnit unlinkedUnit = prelinkedUnit.unlinked; | |
| 909 unlinkedUnit.classes.forEach(buildClass); | |
| 910 unlinkedUnit.enums.forEach(buildEnum); | |
| 911 unlinkedUnit.executables.forEach(buildExecutable); | |
| 912 unlinkedUnit.typedefs.forEach(buildTypedef); | |
| 913 unlinkedUnit.variables.forEach(buildVariable); | |
| 914 String absoluteUri = unit.source.uri.toString(); | |
| 915 unit.accessors = unitHolder.accessors; | |
| 916 unit.enums = unitHolder.enums; | |
| 917 unit.functions = unitHolder.functions; | |
| 918 List<FunctionTypeAliasElement> typeAliases = unitHolder.typeAliases; | |
| 919 for (FunctionTypeAliasElementImpl typeAlias in typeAliases) { | |
| 920 if (typeAlias.isSynthetic) { | |
| 921 typeAlias.enclosingElement = unit; | |
| 922 } | |
| 923 } | |
| 924 unit.typeAliases = typeAliases.where((e) => !e.isSynthetic).toList(); | |
| 925 unit.types = unitHolder.types; | |
| 926 unit.topLevelVariables = unitHolder.topLevelVariables; | |
| 927 Map<String, Element> elementMap = <String, Element>{}; | |
| 928 for (ClassElement cls in unit.types) { | |
| 929 elementMap[cls.name] = cls; | |
| 930 } | |
| 931 for (ClassElement cls in unit.enums) { | |
| 932 elementMap[cls.name] = cls; | |
| 933 } | |
| 934 for (FunctionTypeAliasElement typeAlias in unit.functionTypeAliases) { | |
| 935 elementMap[typeAlias.name] = typeAlias; | |
| 936 } | |
| 937 resummarizedElements[absoluteUri] = elementMap; | |
| 938 unitHolder = null; | |
| 939 prelinkedUnit = null; | |
| 940 } | |
| 941 } | |
| OLD | NEW |