Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 const VERBOSE_OPTIMIZER_HINTS = false; | 7 const VERBOSE_OPTIMIZER_HINTS = false; |
| 8 | 8 |
| 9 const bool USE_CPS_IR = const bool.fromEnvironment("USE_CPS_IR"); | 9 const bool USE_CPS_IR = const bool.fromEnvironment("USE_CPS_IR"); |
| 10 | 10 |
| 11 class JavaScriptItemCompilationContext extends ItemCompilationContext { | 11 class JavaScriptItemCompilationContext extends ItemCompilationContext { |
| 12 final Set<HInstruction> boundsChecked = new Set<HInstruction>(); | 12 final Set<HInstruction> boundsChecked = new Set<HInstruction>(); |
| 13 final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); | 13 final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); |
| 14 } | 14 } |
| 15 | 15 |
| 16 abstract class FunctionCompiler { | 16 abstract class FunctionCompiler { |
| 17 /// Generates JavaScript code for `work.element`. | 17 /// Generates JavaScript code for `work.element`. |
| 18 jsAst.Fun compile(CodegenWorkItem work); | 18 jsAst.Fun compile(CodegenWorkItem work); |
| 19 | 19 |
| 20 Iterable get tasks; | 20 Iterable get tasks; |
| 21 } | 21 } |
| 22 | 22 |
| 23 /* | 23 /* |
| 24 * Invariants: | 24 * Invariants: |
| 25 * canInline(function) implies canInline(function, insideLoop:true) | 25 * canInline(function) implies canInline(function, insideLoop:true) |
| 26 * !canInline(function, insideLoop: true) implies !canInline(function) | 26 * !canInline(function, insideLoop: true) implies !canInline(function) |
| 27 */ | 27 */ |
| 28 class FunctionInlineCache { | 28 class FunctionInlineCache { |
| 29 final Map<FunctionElement, bool> canBeInlined = | 29 static const int _mustNotInline = 0; |
|
Johnni Winther
2015/03/03 08:57:47
The logic for these tags is both complex and scatt
floitsch
2015/03/03 18:54:13
Made the logic much more explicit (and verbose).
| |
| 30 new Map<FunctionElement, bool>(); | 30 // May-inline-in-loop means that the function may not be inlined outside loops |
| 31 // but may be inlined in a loop. | |
| 32 static const int _mayInlineInLoop = 1; | |
| 33 // The function can be inlined in a loop, but not outside. | |
| 34 static const int _canInlineInLoop = 2; | |
| 35 // May-inline means that we know that it can be inlined inside a loop, but | |
| 36 // don't know about the general case yet. | |
| 37 static const int _mayInline = 3; | |
| 38 static const int _canInline = 4; | |
| 39 static const int _mustInline = 5; | |
| 31 | 40 |
| 32 final Map<FunctionElement, bool> canBeInlinedInsideLoop = | 41 final Map<FunctionElement, int> _cachedDecisions = |
| 33 new Map<FunctionElement, bool>(); | 42 new Map<FunctionElement, int>(); |
| 34 | 43 |
| 35 // Returns [:true:]/[:false:] if we have a cached decision. | 44 // Returns `true`/`false` if we have a cached decision. |
| 36 // Returns [:null:] otherwise. | 45 // Returns `null` otherwise. |
| 37 bool canInline(FunctionElement element, {bool insideLoop}) { | 46 bool canInline(FunctionElement element, {bool insideLoop}) { |
| 38 return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; | 47 int decision = _cachedDecisions[element]; |
| 48 if (decision == null) return null; | |
| 49 if (insideLoop) { | |
| 50 // We might be able to inline inside a loop, but don't know it yet. | |
| 51 if (decision == _mayInlineInLoop) return null; | |
| 52 return (decision >= _canInlineInLoop); | |
| 53 } | |
| 54 if (decision == _mayInline) return null; | |
| 55 return decision >= _canInline; | |
| 39 } | 56 } |
| 40 | 57 |
| 41 void markAsInlinable(FunctionElement element, {bool insideLoop}) { | 58 void markAsInlinable(FunctionElement element, {bool insideLoop}) { |
| 59 int oldDecision = _cachedDecisions[element]; | |
| 60 assert(oldDecision != _mustNotInline); | |
| 61 | |
| 42 if (insideLoop) { | 62 if (insideLoop) { |
| 43 canBeInlinedInsideLoop[element] = true; | 63 if (oldDecision == null) { |
| 64 // We know that it can be inlined in a loop, but don't know about the | |
| 65 // non-loop case yet. | |
| 66 _cachedDecisions[element] = _mayInline; | |
| 67 } else if (oldDecision == _mayInlineInLoop) { | |
| 68 _cachedDecisions[element] = _canInlineInLoop; | |
| 69 } | |
| 44 } else { | 70 } else { |
| 45 // If we can inline a function outside a loop then we should do it inside | 71 if (oldDecision == null || oldDecision <= _mayInline) { |
| 46 // a loop as well. | 72 _cachedDecisions[element] = _canInline; |
| 47 canBeInlined[element] = true; | 73 } |
| 48 canBeInlinedInsideLoop[element] = true; | |
| 49 } | 74 } |
| 50 } | 75 } |
| 51 | 76 |
| 52 void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { | 77 void markAsNonInlinable(FunctionElement element, {bool insideLoop: true}) { |
| 53 if (insideLoop == null || insideLoop) { | 78 assert(_cachedDecisions[element] != _mustInline); |
| 54 // If we can't inline a function inside a loop, then we should not inline | 79 |
| 55 // it outside a loop either. | 80 if (insideLoop) { |
| 56 canBeInlined[element] = false; | 81 _cachedDecisions[element] = _mustNotInline; |
| 57 canBeInlinedInsideLoop[element] = false; | |
| 58 } else { | 82 } else { |
| 59 canBeInlined[element] = false; | 83 // We can't inline outside a loop, but we might still be allowed to do it |
| 84 // outside. | |
| 85 int oldDecision = _cachedDecisions[element]; | |
| 86 if (oldDecision == null) { | |
| 87 _cachedDecisions[element] = _mayInlineInLoop; | |
| 88 } else if (oldDecision == _mayInline) { | |
| 89 // We already knew that the function could be inlined inside a loop, but | |
| 90 // didn't have information about the non-loop case. Now we know that it | |
| 91 // can't be inlined outside a loop. | |
| 92 _cachedDecisions[element] = _canInlineInLoop; | |
| 93 } | |
| 60 } | 94 } |
| 61 } | 95 } |
| 96 | |
| 97 void markAsMustInline(FunctionElement element) { | |
| 98 _cachedDecisions[element] = _mustInline; | |
| 99 } | |
| 62 } | 100 } |
| 63 | 101 |
| 64 class JavaScriptBackend extends Backend { | 102 class JavaScriptBackend extends Backend { |
| 65 static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); | 103 static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); |
| 66 static final Uri DART_INTERCEPTORS = | 104 static final Uri DART_INTERCEPTORS = |
| 67 new Uri(scheme: 'dart', path: '_interceptors'); | 105 new Uri(scheme: 'dart', path: '_interceptors'); |
| 68 static final Uri DART_INTERNAL = | 106 static final Uri DART_INTERNAL = |
| 69 new Uri(scheme: 'dart', path: '_internal'); | 107 new Uri(scheme: 'dart', path: '_internal'); |
| 70 static final Uri DART_FOREIGN_HELPER = | 108 static final Uri DART_FOREIGN_HELPER = |
| 71 new Uri(scheme: 'dart', path: '_foreign_helper'); | 109 new Uri(scheme: 'dart', path: '_foreign_helper'); |
| 72 static final Uri DART_JS_MIRRORS = | 110 static final Uri DART_JS_MIRRORS = |
| 73 new Uri(scheme: 'dart', path: '_js_mirrors'); | 111 new Uri(scheme: 'dart', path: '_js_mirrors'); |
| 74 static final Uri DART_JS_NAMES = | 112 static final Uri DART_JS_NAMES = |
| 75 new Uri(scheme: 'dart', path: '_js_names'); | 113 new Uri(scheme: 'dart', path: '_js_names'); |
| 76 static final Uri DART_EMBEDDED_NAMES = | 114 static final Uri DART_EMBEDDED_NAMES = |
| 77 new Uri(scheme: 'dart', path: '_js_embedded_names'); | 115 new Uri(scheme: 'dart', path: '_js_embedded_names'); |
| 78 static final Uri DART_ISOLATE_HELPER = | 116 static final Uri DART_ISOLATE_HELPER = |
| 79 new Uri(scheme: 'dart', path: '_isolate_helper'); | 117 new Uri(scheme: 'dart', path: '_isolate_helper'); |
| 80 static final Uri DART_HTML = | 118 static final Uri DART_HTML = |
| 81 new Uri(scheme: 'dart', path: 'html'); | 119 new Uri(scheme: 'dart', path: 'html'); |
| 82 | 120 |
| 83 static const String INVOKE_ON = '_getCachedInvocation'; | 121 static const String INVOKE_ON = '_getCachedInvocation'; |
| 84 static const String START_ROOT_ISOLATE = 'startRootIsolate'; | 122 static const String START_ROOT_ISOLATE = 'startRootIsolate'; |
| 85 | 123 |
| 86 | 124 |
| 87 /// The list of functions for classes in the [internalLibrary] that we want | |
| 88 /// to inline always. Any function in this list must be inlinable with | |
| 89 /// respect to the conditions used in [InlineWeeder.canInline], except for | |
| 90 /// size/complexity heuristics. | |
| 91 static const Map<String, List<String>> ALWAYS_INLINE = | |
| 92 const <String, List<String>> { | |
| 93 }; | |
| 94 | |
| 95 String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old'; | 125 String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old'; |
| 96 | 126 |
| 97 final Annotations annotations = new Annotations(); | 127 final Annotations annotations = new Annotations(); |
| 98 | 128 |
| 99 /// List of [FunctionElement]s that we want to inline always. This list is | |
| 100 /// filled when resolution is complete by looking up in [internalLibrary]. | |
| 101 List<FunctionElement> functionsToAlwaysInline; | |
| 102 | |
| 103 /// Reference to the internal library to lookup functions to always inline. | 129 /// Reference to the internal library to lookup functions to always inline. |
| 104 LibraryElement internalLibrary; | 130 LibraryElement internalLibrary; |
| 105 | 131 |
| 106 | 132 |
| 107 /// Set of classes that need to be considered for reflection although not | 133 /// Set of classes that need to be considered for reflection although not |
| 108 /// otherwise visible during resolution. | 134 /// otherwise visible during resolution. |
| 109 Iterable<ClassElement> get classesRequiredForReflection { | 135 Iterable<ClassElement> get classesRequiredForReflection { |
| 110 // TODO(herhut): Clean this up when classes needed for rti are tracked. | 136 // TODO(herhut): Clean this up when classes needed for rti are tracked. |
| 111 return [closureClass, jsIndexableClass]; | 137 return [closureClass, jsIndexableClass]; |
| 112 } | 138 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 ClassElement typeLiteralClass; | 193 ClassElement typeLiteralClass; |
| 168 ClassElement mapLiteralClass; | 194 ClassElement mapLiteralClass; |
| 169 ClassElement constMapLiteralClass; | 195 ClassElement constMapLiteralClass; |
| 170 ClassElement typeVariableClass; | 196 ClassElement typeVariableClass; |
| 171 ConstructorElement mapLiteralConstructor; | 197 ConstructorElement mapLiteralConstructor; |
| 172 ConstructorElement mapLiteralConstructorEmpty; | 198 ConstructorElement mapLiteralConstructorEmpty; |
| 173 | 199 |
| 174 ClassElement noSideEffectsClass; | 200 ClassElement noSideEffectsClass; |
| 175 ClassElement noThrowsClass; | 201 ClassElement noThrowsClass; |
| 176 ClassElement noInlineClass; | 202 ClassElement noInlineClass; |
| 203 ClassElement forceInlineClass; | |
| 177 ClassElement irRepresentationClass; | 204 ClassElement irRepresentationClass; |
| 178 | 205 |
| 179 Element getInterceptorMethod; | 206 Element getInterceptorMethod; |
| 180 | 207 |
| 181 ClassElement jsInvocationMirrorClass; | 208 ClassElement jsInvocationMirrorClass; |
| 182 | 209 |
| 183 /// If [true], the compiler will emit code that writes the name of the current | 210 /// If [true], the compiler will emit code that writes the name of the current |
| 184 /// method together with its class and library to the console the first time | 211 /// method together with its class and library to the console the first time |
| 185 /// the method is called. | 212 /// the method is called. |
| 186 static const bool TRACE_CALLS = false; | 213 static const bool TRACE_CALLS = false; |
| (...skipping 767 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 954 assert(traceHelper != null); | 981 assert(traceHelper != null); |
| 955 enqueueInResolution(traceHelper, registry); | 982 enqueueInResolution(traceHelper, registry); |
| 956 } | 983 } |
| 957 registerCheckedModeHelpers(registry); | 984 registerCheckedModeHelpers(registry); |
| 958 } | 985 } |
| 959 | 986 |
| 960 onResolutionComplete() { | 987 onResolutionComplete() { |
| 961 super.onResolutionComplete(); | 988 super.onResolutionComplete(); |
| 962 computeMembersNeededForReflection(); | 989 computeMembersNeededForReflection(); |
| 963 rti.computeClassesNeedingRti(); | 990 rti.computeClassesNeedingRti(); |
| 964 computeFunctionsToAlwaysInline(); | |
| 965 } | |
| 966 | |
| 967 void computeFunctionsToAlwaysInline() { | |
| 968 functionsToAlwaysInline = <FunctionElement>[]; | |
| 969 if (internalLibrary == null) return; | |
| 970 | |
| 971 // Try to find all functions intended to always inline. If their enclosing | |
| 972 // class is not resolved we skip the methods, but it is an error to mention | |
| 973 // a function or class that cannot be found. | |
| 974 for (String className in ALWAYS_INLINE.keys) { | |
| 975 ClassElement cls = find(internalLibrary, className); | |
| 976 if (cls.resolutionState != STATE_DONE) continue; | |
| 977 for (String functionName in ALWAYS_INLINE[className]) { | |
| 978 Element function = cls.lookupMember(functionName); | |
| 979 assert(invariant(cls, function is FunctionElement, | |
| 980 message: 'unable to find function $functionName in $className')); | |
| 981 functionsToAlwaysInline.add(function); | |
| 982 } | |
| 983 } | |
| 984 } | 991 } |
| 985 | 992 |
| 986 void registerGetRuntimeTypeArgument(Registry registry) { | 993 void registerGetRuntimeTypeArgument(Registry registry) { |
| 987 enqueueInResolution(getGetRuntimeTypeArgument(), registry); | 994 enqueueInResolution(getGetRuntimeTypeArgument(), registry); |
| 988 enqueueInResolution(getGetTypeArgumentByIndex(), registry); | 995 enqueueInResolution(getGetTypeArgumentByIndex(), registry); |
| 989 enqueueInResolution(getCopyTypeArguments(), registry); | 996 enqueueInResolution(getCopyTypeArguments(), registry); |
| 990 } | 997 } |
| 991 | 998 |
| 992 void registerCallMethodWithFreeTypeVariables( | 999 void registerCallMethodWithFreeTypeVariables( |
| 993 Element callMethod, | 1000 Element callMethod, |
| (...skipping 868 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1862 | 1869 |
| 1863 typeLiteralClass = findClass('TypeImpl'); | 1870 typeLiteralClass = findClass('TypeImpl'); |
| 1864 constMapLiteralClass = findClass('ConstantMap'); | 1871 constMapLiteralClass = findClass('ConstantMap'); |
| 1865 typeVariableClass = findClass('TypeVariable'); | 1872 typeVariableClass = findClass('TypeVariable'); |
| 1866 | 1873 |
| 1867 jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); | 1874 jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); |
| 1868 | 1875 |
| 1869 noSideEffectsClass = findClass('NoSideEffects'); | 1876 noSideEffectsClass = findClass('NoSideEffects'); |
| 1870 noThrowsClass = findClass('NoThrows'); | 1877 noThrowsClass = findClass('NoThrows'); |
| 1871 noInlineClass = findClass('NoInline'); | 1878 noInlineClass = findClass('NoInline'); |
| 1879 forceInlineClass = findClass('ForceInline'); | |
| 1872 irRepresentationClass = findClass('IrRepresentation'); | 1880 irRepresentationClass = findClass('IrRepresentation'); |
| 1873 | 1881 |
| 1874 getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); | 1882 getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); |
| 1875 | 1883 |
| 1876 requiresPreambleMarker = findMethod('requiresPreamble'); | 1884 requiresPreambleMarker = findMethod('requiresPreamble'); |
| 1877 } else if (uri == DART_JS_MIRRORS) { | 1885 } else if (uri == DART_JS_MIRRORS) { |
| 1878 disableTreeShakingMarker = find(library, 'disableTreeShaking'); | 1886 disableTreeShakingMarker = find(library, 'disableTreeShaking'); |
| 1879 preserveMetadataMarker = find(library, 'preserveMetadata'); | 1887 preserveMetadataMarker = find(library, 'preserveMetadata'); |
| 1880 preserveUrisMarker = find(library, 'preserveUris'); | 1888 preserveUrisMarker = find(library, 'preserveUris'); |
| 1881 preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); | 1889 preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); |
| (...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2318 } | 2326 } |
| 2319 | 2327 |
| 2320 void onElementResolved(Element element, TreeElements elements) { | 2328 void onElementResolved(Element element, TreeElements elements) { |
| 2321 if (element.isFunction && annotations.noInline(element)) { | 2329 if (element.isFunction && annotations.noInline(element)) { |
| 2322 inlineCache.markAsNonInlinable(element); | 2330 inlineCache.markAsNonInlinable(element); |
| 2323 } | 2331 } |
| 2324 | 2332 |
| 2325 LibraryElement library = element.library; | 2333 LibraryElement library = element.library; |
| 2326 if (!library.isPlatformLibrary && !library.canUseNative) return; | 2334 if (!library.isPlatformLibrary && !library.canUseNative) return; |
| 2327 bool hasNoInline = false; | 2335 bool hasNoInline = false; |
| 2336 bool hasForceInline = false; | |
| 2328 bool hasNoThrows = false; | 2337 bool hasNoThrows = false; |
| 2329 bool hasNoSideEffects = false; | 2338 bool hasNoSideEffects = false; |
| 2330 for (MetadataAnnotation metadata in element.metadata) { | 2339 for (MetadataAnnotation metadata in element.metadata) { |
| 2331 metadata.ensureResolved(compiler); | 2340 metadata.ensureResolved(compiler); |
| 2332 if (!metadata.constant.value.isConstructedObject) continue; | 2341 if (!metadata.constant.value.isConstructedObject) continue; |
| 2333 ObjectConstantValue value = metadata.constant.value; | 2342 ObjectConstantValue value = metadata.constant.value; |
| 2334 ClassElement cls = value.type.element; | 2343 ClassElement cls = value.type.element; |
| 2335 if (cls == noInlineClass) { | 2344 if (cls == forceInlineClass) { |
| 2345 hasForceInline = true; | |
| 2346 if (VERBOSE_OPTIMIZER_HINTS) { | |
| 2347 compiler.reportHint(element, | |
| 2348 MessageKind.GENERIC, | |
| 2349 {'text': "Must inline"}); | |
| 2350 } | |
| 2351 inlineCache.markAsMustInline(element); | |
| 2352 } else if (cls == noInlineClass) { | |
| 2336 hasNoInline = true; | 2353 hasNoInline = true; |
| 2337 if (VERBOSE_OPTIMIZER_HINTS) { | 2354 if (VERBOSE_OPTIMIZER_HINTS) { |
| 2338 compiler.reportHint(element, | 2355 compiler.reportHint(element, |
| 2339 MessageKind.GENERIC, | 2356 MessageKind.GENERIC, |
| 2340 {'text': "Cannot inline"}); | 2357 {'text': "Cannot inline"}); |
| 2341 } | 2358 } |
| 2342 inlineCache.markAsNonInlinable(element); | 2359 inlineCache.markAsNonInlinable(element); |
| 2343 } else if (cls == noThrowsClass) { | 2360 } else if (cls == noThrowsClass) { |
| 2344 hasNoThrows = true; | 2361 hasNoThrows = true; |
| 2345 if (!Elements.isStaticOrTopLevelFunction(element)) { | 2362 if (!Elements.isStaticOrTopLevelFunction(element)) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 2356 } else if (cls == noSideEffectsClass) { | 2373 } else if (cls == noSideEffectsClass) { |
| 2357 hasNoSideEffects = true; | 2374 hasNoSideEffects = true; |
| 2358 if (VERBOSE_OPTIMIZER_HINTS) { | 2375 if (VERBOSE_OPTIMIZER_HINTS) { |
| 2359 compiler.reportHint(element, | 2376 compiler.reportHint(element, |
| 2360 MessageKind.GENERIC, | 2377 MessageKind.GENERIC, |
| 2361 {'text': "Has no side effects"}); | 2378 {'text': "Has no side effects"}); |
| 2362 } | 2379 } |
| 2363 compiler.world.registerSideEffectsFree(element); | 2380 compiler.world.registerSideEffectsFree(element); |
| 2364 } | 2381 } |
| 2365 } | 2382 } |
| 2383 if (hasForceInline && hasNoInline) { | |
| 2384 compiler.internalError(element, | |
| 2385 "@ForceInline() must not be used with @NoInline."); | |
| 2386 } | |
| 2366 if (hasNoThrows && !hasNoInline) { | 2387 if (hasNoThrows && !hasNoInline) { |
| 2367 compiler.internalError(element, | 2388 compiler.internalError(element, |
| 2368 "@NoThrows() should always be combined with @NoInline."); | 2389 "@NoThrows() should always be combined with @NoInline."); |
| 2369 } | 2390 } |
| 2370 if (hasNoSideEffects && !hasNoInline) { | 2391 if (hasNoSideEffects && !hasNoInline) { |
| 2371 compiler.internalError(element, | 2392 compiler.internalError(element, |
| 2372 "@NoSideEffects() should always be combined with @NoInline."); | 2393 "@NoSideEffects() should always be combined with @NoInline."); |
| 2373 } | 2394 } |
| 2374 if (element == invokeOnMethod) { | 2395 if (element == invokeOnMethod) { |
| 2375 compiler.enabledInvokeOn = true; | 2396 compiler.enabledInvokeOn = true; |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2706 } | 2727 } |
| 2707 } | 2728 } |
| 2708 | 2729 |
| 2709 /// Records that [constant] is used by the element behind [registry]. | 2730 /// Records that [constant] is used by the element behind [registry]. |
| 2710 class Dependency { | 2731 class Dependency { |
| 2711 final ConstantValue constant; | 2732 final ConstantValue constant; |
| 2712 final Element annotatedElement; | 2733 final Element annotatedElement; |
| 2713 | 2734 |
| 2714 const Dependency(this.constant, this.annotatedElement); | 2735 const Dependency(this.constant, this.annotatedElement); |
| 2715 } | 2736 } |
| OLD | NEW |