| 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 /** | 5 /** |
| 6 * This library contains the infrastructure to parse and integrate patch files. | 6 * This library contains the infrastructure to parse and integrate patch files. |
| 7 * | 7 * |
| 8 * Three types of elements can be patched: [LibraryElement], [ClassElement], | 8 * Three types of elements can be patched: [LibraryElement], [ClassElement], |
| 9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded | 9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded |
| 10 * together with the corresponding origin library. Which libraries that are | 10 * together with the corresponding origin library. Which libraries that are |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 * - Builders shift between declaration and implementation depending on usages. | 109 * - Builders shift between declaration and implementation depending on usages. |
| 110 * - Compile-time constants use constructor implementation exclusively. | 110 * - Compile-time constants use constructor implementation exclusively. |
| 111 * - Work on function parameters is performed on the declaration of the function | 111 * - Work on function parameters is performed on the declaration of the function |
| 112 * element. | 112 * element. |
| 113 */ | 113 */ |
| 114 | 114 |
| 115 library dart2js.patchparser; | 115 library dart2js.patchparser; |
| 116 | 116 |
| 117 import 'dart:async'; | 117 import 'dart:async'; |
| 118 | 118 |
| 119 import 'constants/values.dart' show | 119 import 'constants/values.dart' show ConstantValue; |
| 120 ConstantValue; | |
| 121 import 'common.dart'; | 120 import 'common.dart'; |
| 122 import 'compiler.dart' show | 121 import 'compiler.dart' show Compiler; |
| 123 Compiler; | 122 import 'common/tasks.dart' show CompilerTask; |
| 124 import 'common/tasks.dart' show | 123 import 'dart_types.dart' show DartType; |
| 125 CompilerTask; | |
| 126 import 'dart_types.dart' show | |
| 127 DartType; | |
| 128 import 'elements/elements.dart'; | 124 import 'elements/elements.dart'; |
| 129 import 'elements/modelx.dart' show | 125 import 'elements/modelx.dart' |
| 130 BaseFunctionElementX, | 126 show |
| 131 ClassElementX, | 127 BaseFunctionElementX, |
| 132 GetterElementX, | 128 ClassElementX, |
| 133 LibraryElementX, | 129 GetterElementX, |
| 134 MetadataAnnotationX, | 130 LibraryElementX, |
| 135 SetterElementX; | 131 MetadataAnnotationX, |
| 136 import 'js_backend/js_backend.dart' show | 132 SetterElementX; |
| 137 JavaScriptBackend; | 133 import 'js_backend/js_backend.dart' show JavaScriptBackend; |
| 138 import 'library_loader.dart' show | 134 import 'library_loader.dart' show LibraryLoader; |
| 139 LibraryLoader; | 135 import 'options.dart' show ParserOptions; |
| 140 import 'options.dart' show | 136 import 'parser/listener.dart' show Listener, ParserError; |
| 141 ParserOptions; | 137 import 'parser/element_listener.dart' show ElementListener; |
| 142 import 'parser/listener.dart' show | 138 import 'parser/member_listener.dart' show MemberListener; |
| 143 Listener, | 139 import 'parser/partial_elements.dart' show PartialClassElement; |
| 144 ParserError; | 140 import 'parser/partial_parser.dart' show PartialParser; |
| 145 import 'parser/element_listener.dart' show | 141 import 'parser/parser.dart' show Parser; |
| 146 ElementListener; | 142 import 'scanner/scanner.dart' show Scanner; |
| 147 import 'parser/member_listener.dart' show | |
| 148 MemberListener; | |
| 149 import 'parser/partial_elements.dart' show | |
| 150 PartialClassElement; | |
| 151 import 'parser/partial_parser.dart' show | |
| 152 PartialParser; | |
| 153 import 'parser/parser.dart' show | |
| 154 Parser; | |
| 155 import 'scanner/scanner.dart' show | |
| 156 Scanner; | |
| 157 import 'script.dart'; | 143 import 'script.dart'; |
| 158 import 'tokens/token.dart' show | 144 import 'tokens/token.dart' show StringToken, Token; |
| 159 StringToken, | |
| 160 Token; | |
| 161 | 145 |
| 162 class PatchParserTask extends CompilerTask { | 146 class PatchParserTask extends CompilerTask { |
| 163 final String name = "Patching Parser"; | 147 final String name = "Patching Parser"; |
| 164 final ParserOptions parserOptions; | 148 final ParserOptions parserOptions; |
| 165 | 149 |
| 166 PatchParserTask(Compiler compiler, this.parserOptions) : super(compiler); | 150 PatchParserTask(Compiler compiler, this.parserOptions) : super(compiler); |
| 167 | 151 |
| 168 /** | 152 /** |
| 169 * Scans a library patch file, applies the method patches and | 153 * Scans a library patch file, applies the method patches and |
| 170 * injections to the library, and returns a list of class | 154 * injections to the library, and returns a list of class |
| 171 * patches. | 155 * patches. |
| 172 */ | 156 */ |
| 173 Future patchLibrary(LibraryLoader loader, | 157 Future patchLibrary( |
| 174 Uri patchUri, LibraryElement originLibrary) { | 158 LibraryLoader loader, Uri patchUri, LibraryElement originLibrary) { |
| 175 return compiler.readScript(patchUri, originLibrary) | 159 return compiler.readScript(patchUri, originLibrary).then((Script script) { |
| 176 .then((Script script) { | |
| 177 var patchLibrary = new LibraryElementX(script, null, originLibrary); | 160 var patchLibrary = new LibraryElementX(script, null, originLibrary); |
| 178 return reporter.withCurrentElement(patchLibrary, () { | 161 return reporter.withCurrentElement(patchLibrary, () { |
| 179 loader.registerNewLibrary(patchLibrary); | 162 loader.registerNewLibrary(patchLibrary); |
| 180 reporter.withCurrentElement(patchLibrary.entryCompilationUnit, () { | 163 reporter.withCurrentElement(patchLibrary.entryCompilationUnit, () { |
| 181 // This patches the elements of the patch library into [library]. | 164 // This patches the elements of the patch library into [library]. |
| 182 // Injected elements are added directly under the compilation unit. | 165 // Injected elements are added directly under the compilation unit. |
| 183 // Patch elements are stored on the patched functions or classes. | 166 // Patch elements are stored on the patched functions or classes. |
| 184 scanLibraryElements(patchLibrary.entryCompilationUnit); | 167 scanLibraryElements(patchLibrary.entryCompilationUnit); |
| 185 }); | 168 }); |
| 186 return loader.processLibraryTags(patchLibrary); | 169 return loader.processLibraryTags(patchLibrary); |
| 187 }); | 170 }); |
| 188 }); | 171 }); |
| 189 } | 172 } |
| 190 | 173 |
| 191 void scanLibraryElements(CompilationUnitElement compilationUnit) { | 174 void scanLibraryElements(CompilationUnitElement compilationUnit) { |
| 192 measure(() { | 175 measure(() { |
| 193 // TODO(johnniwinther): Test that parts and exports are handled correctly. | 176 // TODO(johnniwinther): Test that parts and exports are handled correctly. |
| 194 Script script = compilationUnit.script; | 177 Script script = compilationUnit.script; |
| 195 Token tokens = new Scanner(script.file).tokenize(); | 178 Token tokens = new Scanner(script.file).tokenize(); |
| 196 Function idGenerator = compiler.getNextFreeClassId; | 179 Function idGenerator = compiler.getNextFreeClassId; |
| 197 Listener patchListener = new PatchElementListener(compiler, | 180 Listener patchListener = |
| 198 compilationUnit, | 181 new PatchElementListener(compiler, compilationUnit, idGenerator); |
| 199 idGenerator); | |
| 200 try { | 182 try { |
| 201 new PartialParser(patchListener, parserOptions).parseUnit(tokens); | 183 new PartialParser(patchListener, parserOptions).parseUnit(tokens); |
| 202 } on ParserError catch (e) { | 184 } on ParserError catch (e) { |
| 203 // No need to recover from a parser error in platform libraries, user | 185 // No need to recover from a parser error in platform libraries, user |
| 204 // will never see this if the libraries are tested correctly. | 186 // will never see this if the libraries are tested correctly. |
| 205 reporter.internalError( | 187 reporter.internalError( |
| 206 compilationUnit, "Parser error in patch file: $e"); | 188 compilationUnit, "Parser error in patch file: $e"); |
| 207 } | 189 } |
| 208 }); | 190 }); |
| 209 } | 191 } |
| 210 | 192 |
| 211 void parsePatchClassNode(PartialClassElement cls) { | 193 void parsePatchClassNode(PartialClassElement cls) { |
| 212 // Parse [PartialClassElement] using a "patch"-aware parser instead | 194 // Parse [PartialClassElement] using a "patch"-aware parser instead |
| 213 // of calling its [parseNode] method. | 195 // of calling its [parseNode] method. |
| 214 if (cls.cachedNode != null) return; | 196 if (cls.cachedNode != null) return; |
| 215 | 197 |
| 216 measure(() => reporter.withCurrentElement(cls, () { | 198 measure(() => reporter.withCurrentElement(cls, () { |
| 217 MemberListener listener = new PatchMemberListener(compiler, cls); | 199 MemberListener listener = new PatchMemberListener(compiler, cls); |
| 218 Parser parser = new PatchClassElementParser(listener, parserOptions); | 200 Parser parser = new PatchClassElementParser(listener, parserOptions); |
| 219 try { | 201 try { |
| 220 Token token = parser.parseTopLevelDeclaration(cls.beginToken); | 202 Token token = parser.parseTopLevelDeclaration(cls.beginToken); |
| 221 assert(identical(token, cls.endToken.next)); | 203 assert(identical(token, cls.endToken.next)); |
| 222 } on ParserError catch (e) { | 204 } on ParserError catch (e) { |
| 223 // No need to recover from a parser error in platform libraries, user | 205 // No need to recover from a parser error in platform libraries, use
r |
| 224 // will never see this if the libraries are tested correctly. | 206 // will never see this if the libraries are tested correctly. |
| 225 reporter.internalError( | 207 reporter.internalError(cls, "Parser error in patch file: $e"); |
| 226 cls, "Parser error in patch file: $e"); | 208 } |
| 227 } | 209 cls.cachedNode = listener.popNode(); |
| 228 cls.cachedNode = listener.popNode(); | 210 assert(listener.nodes.isEmpty); |
| 229 assert(listener.nodes.isEmpty); | 211 })); |
| 230 })); | |
| 231 } | 212 } |
| 232 } | 213 } |
| 233 | 214 |
| 234 class PatchMemberListener extends MemberListener { | 215 class PatchMemberListener extends MemberListener { |
| 235 final Compiler compiler; | 216 final Compiler compiler; |
| 236 | 217 |
| 237 PatchMemberListener(Compiler compiler, ClassElement enclosingClass) | 218 PatchMemberListener(Compiler compiler, ClassElement enclosingClass) |
| 238 : this.compiler = compiler, | 219 : this.compiler = compiler, |
| 239 super(compiler.parsing.getScannerOptionsFor(enclosingClass), | 220 super(compiler.parsing.getScannerOptionsFor(enclosingClass), |
| 240 compiler.reporter, | 221 compiler.reporter, enclosingClass); |
| 241 enclosingClass); | |
| 242 | 222 |
| 243 @override | 223 @override |
| 244 void addMember(Element patch) { | 224 void addMember(Element patch) { |
| 245 addMetadata(patch); | 225 addMetadata(patch); |
| 246 | 226 |
| 247 PatchVersion patchVersion = getPatchVersion(compiler, patch); | 227 PatchVersion patchVersion = getPatchVersion(compiler, patch); |
| 248 if (patchVersion != null) { | 228 if (patchVersion != null) { |
| 249 if (patchVersion.isActive(compiler.patchVersion)) { | 229 if (patchVersion.isActive(compiler.patchVersion)) { |
| 250 Element origin = enclosingClass.origin.localLookup(patch.name); | 230 Element origin = enclosingClass.origin.localLookup(patch.name); |
| 251 patchElement(compiler, reporter, origin, patch); | 231 patchElement(compiler, reporter, origin, patch); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 272 | 252 |
| 273 Token parseClassBody(Token token) => fullParseClassBody(token); | 253 Token parseClassBody(Token token) => fullParseClassBody(token); |
| 274 } | 254 } |
| 275 | 255 |
| 276 /** | 256 /** |
| 277 * Extension of [ElementListener] for parsing patch files. | 257 * Extension of [ElementListener] for parsing patch files. |
| 278 */ | 258 */ |
| 279 class PatchElementListener extends ElementListener implements Listener { | 259 class PatchElementListener extends ElementListener implements Listener { |
| 280 final Compiler compiler; | 260 final Compiler compiler; |
| 281 | 261 |
| 282 PatchElementListener(Compiler compiler, | 262 PatchElementListener( |
| 283 CompilationUnitElement patchElement, | 263 Compiler compiler, CompilationUnitElement patchElement, int idGenerator()) |
| 284 int idGenerator()) | 264 : this.compiler = compiler, |
| 285 : this.compiler = compiler, | 265 super(compiler.parsing.getScannerOptionsFor(patchElement), |
| 286 super(compiler.parsing.getScannerOptionsFor(patchElement), | |
| 287 compiler.reporter, patchElement, idGenerator); | 266 compiler.reporter, patchElement, idGenerator); |
| 288 | 267 |
| 289 @override | 268 @override |
| 290 void pushElement(Element patch) { | 269 void pushElement(Element patch) { |
| 291 popMetadata(patch); | 270 popMetadata(patch); |
| 292 | 271 |
| 293 PatchVersion patchVersion = getPatchVersion(compiler, patch); | 272 PatchVersion patchVersion = getPatchVersion(compiler, patch); |
| 294 if (patchVersion != null) { | 273 if (patchVersion != null) { |
| 295 if (patchVersion.isActive(compiler.patchVersion)) { | 274 if (patchVersion.isActive(compiler.patchVersion)) { |
| 296 LibraryElement originLibrary = compilationUnitElement.library; | 275 LibraryElement originLibrary = compilationUnitElement.library; |
| 297 assert(originLibrary.isPatched); | 276 assert(originLibrary.isPatched); |
| 298 Element origin = originLibrary.localLookup(patch.name); | 277 Element origin = originLibrary.localLookup(patch.name); |
| 299 patchElement(compiler, reporter, origin, patch); | 278 patchElement(compiler, reporter, origin, patch); |
| 300 compilationUnitElement.addMember(patch, reporter); | 279 compilationUnitElement.addMember(patch, reporter); |
| 301 } else { | 280 } else { |
| 302 // Skip this element. | 281 // Skip this element. |
| 303 } | 282 } |
| 304 } else { | 283 } else { |
| 305 if (Name.isPublicName(patch.name)) { | 284 if (Name.isPublicName(patch.name)) { |
| 306 reporter.reportErrorMessage(patch, MessageKind.INJECTED_PUBLIC_MEMBER); | 285 reporter.reportErrorMessage(patch, MessageKind.INJECTED_PUBLIC_MEMBER); |
| 307 } | 286 } |
| 308 compilationUnitElement.addMember(patch, reporter); | 287 compilationUnitElement.addMember(patch, reporter); |
| 309 } | 288 } |
| 310 } | 289 } |
| 311 } | 290 } |
| 312 | 291 |
| 313 void patchElement(Compiler compiler, | 292 void patchElement(Compiler compiler, DiagnosticReporter reporter, |
| 314 DiagnosticReporter reporter, | 293 Element origin, Element patch) { |
| 315 Element origin, | |
| 316 Element patch) { | |
| 317 if (origin == null) { | 294 if (origin == null) { |
| 318 reporter.reportErrorMessage( | 295 reporter.reportErrorMessage( |
| 319 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); | 296 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); |
| 320 return; | 297 return; |
| 321 } | 298 } |
| 322 | 299 |
| 323 if (!(origin.isClass || | 300 if (!(origin.isClass || |
| 324 origin.isConstructor || | 301 origin.isConstructor || |
| 325 origin.isFunction || | 302 origin.isFunction || |
| 326 origin.isAbstractField)) { | 303 origin.isAbstractField)) { |
| 327 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 304 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
| 328 reporter.reportErrorMessage(origin, MessageKind.PATCH_NONPATCHABLE); | 305 reporter.reportErrorMessage(origin, MessageKind.PATCH_NONPATCHABLE); |
| 329 return; | 306 return; |
| 330 } | 307 } |
| 331 if (patch.isClass) { | 308 if (patch.isClass) { |
| 332 tryPatchClass(compiler, reporter, origin, patch); | 309 tryPatchClass(compiler, reporter, origin, patch); |
| 333 } else if (patch.isGetter) { | 310 } else if (patch.isGetter) { |
| 334 tryPatchGetter(reporter, origin, patch); | 311 tryPatchGetter(reporter, origin, patch); |
| 335 } else if (patch.isSetter) { | 312 } else if (patch.isSetter) { |
| 336 tryPatchSetter(reporter, origin, patch); | 313 tryPatchSetter(reporter, origin, patch); |
| 337 } else if (patch.isConstructor) { | 314 } else if (patch.isConstructor) { |
| 338 tryPatchConstructor(reporter, origin, patch); | 315 tryPatchConstructor(reporter, origin, patch); |
| 339 } else if(patch.isFunction) { | 316 } else if (patch.isFunction) { |
| 340 tryPatchFunction(reporter, origin, patch); | 317 tryPatchFunction(reporter, origin, patch); |
| 341 } else { | 318 } else { |
| 342 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 319 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
| 343 reporter.reportErrorMessage(patch, MessageKind.PATCH_NONPATCHABLE); | 320 reporter.reportErrorMessage(patch, MessageKind.PATCH_NONPATCHABLE); |
| 344 } | 321 } |
| 345 } | 322 } |
| 346 | 323 |
| 347 void tryPatchClass(Compiler compiler, | 324 void tryPatchClass(Compiler compiler, DiagnosticReporter reporter, |
| 348 DiagnosticReporter reporter, | 325 Element origin, ClassElement patch) { |
| 349 Element origin, | |
| 350 ClassElement patch) { | |
| 351 if (!origin.isClass) { | 326 if (!origin.isClass) { |
| 352 reporter.reportError( | 327 reporter.reportError( |
| 353 reporter.createMessage( | 328 reporter.createMessage( |
| 354 origin, | 329 origin, MessageKind.PATCH_NON_CLASS, {'className': patch.name}), |
| 355 MessageKind.PATCH_NON_CLASS, | |
| 356 {'className': patch.name}), | |
| 357 <DiagnosticMessage>[ | 330 <DiagnosticMessage>[ |
| 358 reporter.createMessage( | 331 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_CLASS, |
| 359 patch, | 332 {'className': patch.name}), |
| 360 MessageKind.PATCH_POINT_TO_CLASS, | |
| 361 {'className': patch.name}), | |
| 362 ]); | 333 ]); |
| 363 return; | 334 return; |
| 364 } | 335 } |
| 365 patchClass(compiler, reporter, origin, patch); | 336 patchClass(compiler, reporter, origin, patch); |
| 366 } | 337 } |
| 367 | 338 |
| 368 void patchClass(Compiler compiler, | 339 void patchClass(Compiler compiler, DiagnosticReporter reporter, |
| 369 DiagnosticReporter reporter, | 340 ClassElementX origin, ClassElementX patch) { |
| 370 ClassElementX origin, | |
| 371 ClassElementX patch) { | |
| 372 if (origin.isPatched) { | 341 if (origin.isPatched) { |
| 373 reporter.internalError(origin, | 342 reporter.internalError(origin, "Patching the same class more than once."); |
| 374 "Patching the same class more than once."); | |
| 375 } | 343 } |
| 376 origin.applyPatch(patch); | 344 origin.applyPatch(patch); |
| 377 checkNativeAnnotation(compiler, patch); | 345 checkNativeAnnotation(compiler, patch); |
| 378 } | 346 } |
| 379 | 347 |
| 380 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its | 348 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its |
| 381 /// native name from the annotation. | 349 /// native name from the annotation. |
| 382 checkNativeAnnotation(Compiler compiler, ClassElement cls) { | 350 checkNativeAnnotation(Compiler compiler, ClassElement cls) { |
| 383 EagerAnnotationHandler.checkAnnotation(compiler, cls, | 351 EagerAnnotationHandler.checkAnnotation( |
| 384 const NativeAnnotationHandler()); | 352 compiler, cls, const NativeAnnotationHandler()); |
| 385 } | 353 } |
| 386 | 354 |
| 387 checkJsInteropAnnotation(Compiler compiler, element) { | 355 checkJsInteropAnnotation(Compiler compiler, element) { |
| 388 EagerAnnotationHandler.checkAnnotation(compiler, element, | 356 EagerAnnotationHandler.checkAnnotation( |
| 389 const JsInteropAnnotationHandler()); | 357 compiler, element, const JsInteropAnnotationHandler()); |
| 390 } | 358 } |
| 391 | 359 |
| 392 | |
| 393 /// Abstract interface for pre-resolution detection of metadata. | 360 /// Abstract interface for pre-resolution detection of metadata. |
| 394 /// | 361 /// |
| 395 /// The detection is handled in two steps: | 362 /// The detection is handled in two steps: |
| 396 /// - match the annotation syntactically and assume that the annotation is valid | 363 /// - match the annotation syntactically and assume that the annotation is valid |
| 397 /// if it looks correct, | 364 /// if it looks correct, |
| 398 /// - setup a deferred action to check that the annotation has a valid constant | 365 /// - setup a deferred action to check that the annotation has a valid constant |
| 399 /// value and report an internal error if not. | 366 /// value and report an internal error if not. |
| 400 abstract class EagerAnnotationHandler<T> { | 367 abstract class EagerAnnotationHandler<T> { |
| 401 /// Checks that [annotation] looks like a matching annotation and optionally | 368 /// Checks that [annotation] looks like a matching annotation and optionally |
| 402 /// applies actions on [element]. Returns a non-null annotation marker if the | 369 /// applies actions on [element]. Returns a non-null annotation marker if the |
| 403 /// annotation matched and should be validated. | 370 /// annotation matched and should be validated. |
| 404 T apply(Compiler compiler, | 371 T apply(Compiler compiler, Element element, MetadataAnnotation annotation); |
| 405 Element element, | |
| 406 MetadataAnnotation annotation); | |
| 407 | 372 |
| 408 /// Checks that the annotation value is valid. | 373 /// Checks that the annotation value is valid. |
| 409 void validate(Compiler compiler, | 374 void validate(Compiler compiler, Element element, |
| 410 Element element, | 375 MetadataAnnotation annotation, ConstantValue constant); |
| 411 MetadataAnnotation annotation, | |
| 412 ConstantValue constant); | |
| 413 | |
| 414 | 376 |
| 415 /// Checks [element] for metadata matching the [handler]. Return a non-null | 377 /// Checks [element] for metadata matching the [handler]. Return a non-null |
| 416 /// annotation marker matching metadata was found. | 378 /// annotation marker matching metadata was found. |
| 417 static checkAnnotation(Compiler compiler, | 379 static checkAnnotation( |
| 418 Element element, | 380 Compiler compiler, Element element, EagerAnnotationHandler handler) { |
| 419 EagerAnnotationHandler handler) { | |
| 420 for (MetadataAnnotation annotation in element.implementation.metadata) { | 381 for (MetadataAnnotation annotation in element.implementation.metadata) { |
| 421 var result = handler.apply(compiler, element, annotation); | 382 var result = handler.apply(compiler, element, annotation); |
| 422 if (result != null) { | 383 if (result != null) { |
| 423 // TODO(johnniwinther): Perform this check in | 384 // TODO(johnniwinther): Perform this check in |
| 424 // [Compiler.onLibrariesLoaded]. | 385 // [Compiler.onLibrariesLoaded]. |
| 425 compiler.enqueuer.resolution.addDeferredAction(element, () { | 386 compiler.enqueuer.resolution.addDeferredAction(element, () { |
| 426 annotation.ensureResolved(compiler.resolution); | 387 annotation.ensureResolved(compiler.resolution); |
| 427 handler.validate( | 388 handler.validate(compiler, element, annotation, |
| 428 compiler, element, annotation, | |
| 429 compiler.constants.getConstantValue(annotation.constant)); | 389 compiler.constants.getConstantValue(annotation.constant)); |
| 430 }); | 390 }); |
| 431 return result; | 391 return result; |
| 432 } | 392 } |
| 433 } | 393 } |
| 434 return null; | 394 return null; |
| 435 } | 395 } |
| 436 } | 396 } |
| 437 | 397 |
| 438 /// Annotation handler for pre-resolution detection of `@Native(...)` | 398 /// Annotation handler for pre-resolution detection of `@Native(...)` |
| 439 /// annotations. | 399 /// annotations. |
| 440 class NativeAnnotationHandler implements EagerAnnotationHandler<String> { | 400 class NativeAnnotationHandler implements EagerAnnotationHandler<String> { |
| 441 const NativeAnnotationHandler(); | 401 const NativeAnnotationHandler(); |
| 442 | 402 |
| 443 String getNativeAnnotation(MetadataAnnotation annotation) { | 403 String getNativeAnnotation(MetadataAnnotation annotation) { |
| 444 if (annotation.beginToken != null && | 404 if (annotation.beginToken != null && |
| 445 annotation.beginToken.next.value == 'Native') { | 405 annotation.beginToken.next.value == 'Native') { |
| 446 // Skipping '@', 'Native', and '('. | 406 // Skipping '@', 'Native', and '('. |
| 447 Token argument = annotation.beginToken.next.next.next; | 407 Token argument = annotation.beginToken.next.next.next; |
| 448 if (argument is StringToken) { | 408 if (argument is StringToken) { |
| 449 return argument.value; | 409 return argument.value; |
| 450 } | 410 } |
| 451 } | 411 } |
| 452 return null; | 412 return null; |
| 453 } | 413 } |
| 454 | 414 |
| 455 String apply(Compiler compiler, | 415 String apply( |
| 456 Element element, | 416 Compiler compiler, Element element, MetadataAnnotation annotation) { |
| 457 MetadataAnnotation annotation) { | |
| 458 if (element.isClass) { | 417 if (element.isClass) { |
| 459 String native = getNativeAnnotation(annotation); | 418 String native = getNativeAnnotation(annotation); |
| 460 if (native != null) { | 419 if (native != null) { |
| 461 JavaScriptBackend backend = compiler.backend; | 420 JavaScriptBackend backend = compiler.backend; |
| 462 backend.nativeData.setNativeClassTagInfo(element, native); | 421 backend.nativeData.setNativeClassTagInfo(element, native); |
| 463 return native; | 422 return native; |
| 464 } | 423 } |
| 465 } | 424 } |
| 466 return null; | 425 return null; |
| 467 } | 426 } |
| 468 | 427 |
| 469 void validate(Compiler compiler, | 428 void validate(Compiler compiler, Element element, |
| 470 Element element, | 429 MetadataAnnotation annotation, ConstantValue constant) { |
| 471 MetadataAnnotation annotation, | |
| 472 ConstantValue constant) { | |
| 473 DartType annotationType = constant.getType(compiler.coreTypes); | 430 DartType annotationType = constant.getType(compiler.coreTypes); |
| 474 if (annotationType.element != compiler.nativeAnnotationClass) { | 431 if (annotationType.element != compiler.nativeAnnotationClass) { |
| 475 DiagnosticReporter reporter = compiler.reporter; | 432 DiagnosticReporter reporter = compiler.reporter; |
| 476 reporter.internalError(annotation, 'Invalid @Native(...) annotation.'); | 433 reporter.internalError(annotation, 'Invalid @Native(...) annotation.'); |
| 477 } | 434 } |
| 478 } | 435 } |
| 479 } | 436 } |
| 480 | 437 |
| 481 /// Annotation handler for pre-resolution detection of `@JS(...)` | 438 /// Annotation handler for pre-resolution detection of `@JS(...)` |
| 482 /// annotations. | 439 /// annotations. |
| 483 class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> { | 440 class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> { |
| 484 const JsInteropAnnotationHandler(); | 441 const JsInteropAnnotationHandler(); |
| 485 | 442 |
| 486 bool hasJsNameAnnotation(MetadataAnnotation annotation) => | 443 bool hasJsNameAnnotation(MetadataAnnotation annotation) => |
| 487 annotation.beginToken != null && annotation.beginToken.next.value == 'JS'; | 444 annotation.beginToken != null && annotation.beginToken.next.value == 'JS'; |
| 488 | 445 |
| 489 bool apply(Compiler compiler, | 446 bool apply( |
| 490 Element element, | 447 Compiler compiler, Element element, MetadataAnnotation annotation) { |
| 491 MetadataAnnotation annotation) { | |
| 492 bool hasJsInterop = hasJsNameAnnotation(annotation); | 448 bool hasJsInterop = hasJsNameAnnotation(annotation); |
| 493 if (hasJsInterop) { | 449 if (hasJsInterop) { |
| 494 JavaScriptBackend backend = compiler.backend; | 450 JavaScriptBackend backend = compiler.backend; |
| 495 backend.nativeData.markAsJsInterop(element); | 451 backend.nativeData.markAsJsInterop(element); |
| 496 } | 452 } |
| 497 // Due to semantics of apply in the baseclass we have to return null to | 453 // Due to semantics of apply in the baseclass we have to return null to |
| 498 // indicate that no match was found. | 454 // indicate that no match was found. |
| 499 return hasJsInterop ? true : null; | 455 return hasJsInterop ? true : null; |
| 500 } | 456 } |
| 501 | 457 |
| 502 @override | 458 @override |
| 503 void validate(Compiler compiler, | 459 void validate(Compiler compiler, Element element, |
| 504 Element element, | 460 MetadataAnnotation annotation, ConstantValue constant) { |
| 505 MetadataAnnotation annotation, | |
| 506 ConstantValue constant) { | |
| 507 JavaScriptBackend backend = compiler.backend; | 461 JavaScriptBackend backend = compiler.backend; |
| 508 if (constant.getType(compiler.coreTypes).element != | 462 if (constant.getType(compiler.coreTypes).element != |
| 509 backend.helpers.jsAnnotationClass) { | 463 backend.helpers.jsAnnotationClass) { |
| 510 compiler.reporter.internalError(annotation, 'Invalid @JS(...) annotation.'
); | 464 compiler.reporter |
| 465 .internalError(annotation, 'Invalid @JS(...) annotation.'); |
| 511 } | 466 } |
| 512 } | 467 } |
| 513 } | 468 } |
| 514 | 469 |
| 515 /// Annotation handler for pre-resolution detection of `@patch` annotations. | 470 /// Annotation handler for pre-resolution detection of `@patch` annotations. |
| 516 class PatchAnnotationHandler implements EagerAnnotationHandler<PatchVersion> { | 471 class PatchAnnotationHandler implements EagerAnnotationHandler<PatchVersion> { |
| 517 const PatchAnnotationHandler(); | 472 const PatchAnnotationHandler(); |
| 518 | 473 |
| 519 PatchVersion getPatchVersion(MetadataAnnotation annotation) { | 474 PatchVersion getPatchVersion(MetadataAnnotation annotation) { |
| 520 if (annotation.beginToken != null) { | 475 if (annotation.beginToken != null) { |
| 521 if (annotation.beginToken.next.value == 'patch') { | 476 if (annotation.beginToken.next.value == 'patch') { |
| 522 return const PatchVersion(null); | 477 return const PatchVersion(null); |
| 523 } else if (annotation.beginToken.next.value == 'patch_full') { | 478 } else if (annotation.beginToken.next.value == 'patch_full') { |
| 524 return const PatchVersion('full'); | 479 return const PatchVersion('full'); |
| 525 } else if (annotation.beginToken.next.value == 'patch_lazy') { | 480 } else if (annotation.beginToken.next.value == 'patch_lazy') { |
| 526 return const PatchVersion('lazy'); | 481 return const PatchVersion('lazy'); |
| 527 } else if (annotation.beginToken.next.value == 'patch_startup') { | 482 } else if (annotation.beginToken.next.value == 'patch_startup') { |
| 528 return const PatchVersion('startup'); | 483 return const PatchVersion('startup'); |
| 529 } | 484 } |
| 530 } | 485 } |
| 531 return null; | 486 return null; |
| 532 } | 487 } |
| 533 | 488 |
| 534 @override | 489 @override |
| 535 PatchVersion apply(Compiler compiler, | 490 PatchVersion apply( |
| 536 Element element, | 491 Compiler compiler, Element element, MetadataAnnotation annotation) { |
| 537 MetadataAnnotation annotation) { | |
| 538 return getPatchVersion(annotation); | 492 return getPatchVersion(annotation); |
| 539 } | 493 } |
| 540 | 494 |
| 541 @override | 495 @override |
| 542 void validate(Compiler compiler, | 496 void validate(Compiler compiler, Element element, |
| 543 Element element, | 497 MetadataAnnotation annotation, ConstantValue constant) { |
| 544 MetadataAnnotation annotation, | |
| 545 ConstantValue constant) { | |
| 546 DartType annotationType = constant.getType(compiler.coreTypes); | 498 DartType annotationType = constant.getType(compiler.coreTypes); |
| 547 if (annotationType.element != compiler.patchAnnotationClass) { | 499 if (annotationType.element != compiler.patchAnnotationClass) { |
| 548 DiagnosticReporter reporter = compiler.reporter; | 500 DiagnosticReporter reporter = compiler.reporter; |
| 549 reporter.internalError(annotation, 'Invalid patch annotation.'); | 501 reporter.internalError(annotation, 'Invalid patch annotation.'); |
| 550 } | 502 } |
| 551 } | 503 } |
| 552 } | 504 } |
| 553 | 505 |
| 554 | 506 void tryPatchGetter( |
| 555 void tryPatchGetter(DiagnosticReporter reporter, | 507 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
| 556 Element origin, | |
| 557 FunctionElement patch) { | |
| 558 if (!origin.isAbstractField) { | 508 if (!origin.isAbstractField) { |
| 559 reporter.reportError( | 509 reporter.reportError( |
| 560 reporter.createMessage( | 510 reporter.createMessage( |
| 561 origin, | 511 origin, MessageKind.PATCH_NON_GETTER, {'name': origin.name}), |
| 562 MessageKind.PATCH_NON_GETTER, | |
| 563 {'name': origin.name}), | |
| 564 <DiagnosticMessage>[ | 512 <DiagnosticMessage>[ |
| 565 reporter.createMessage( | 513 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_GETTER, |
| 566 patch, | 514 {'getterName': patch.name}), |
| 567 MessageKind.PATCH_POINT_TO_GETTER, | |
| 568 {'getterName': patch.name}), | |
| 569 ]); | 515 ]); |
| 570 return; | 516 return; |
| 571 } | 517 } |
| 572 AbstractFieldElement originField = origin; | 518 AbstractFieldElement originField = origin; |
| 573 if (originField.getter == null) { | 519 if (originField.getter == null) { |
| 574 reporter.reportError( | 520 reporter.reportError( |
| 575 reporter.createMessage( | 521 reporter.createMessage( |
| 576 origin, | 522 origin, MessageKind.PATCH_NO_GETTER, {'getterName': patch.name}), |
| 577 MessageKind.PATCH_NO_GETTER, | |
| 578 {'getterName': patch.name}), | |
| 579 <DiagnosticMessage>[ | 523 <DiagnosticMessage>[ |
| 580 reporter.createMessage( | 524 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_GETTER, |
| 581 patch, | 525 {'getterName': patch.name}), |
| 582 MessageKind.PATCH_POINT_TO_GETTER, | |
| 583 {'getterName': patch.name}), | |
| 584 ]); | 526 ]); |
| 585 return; | 527 return; |
| 586 } | 528 } |
| 587 GetterElementX getter = originField.getter; | 529 GetterElementX getter = originField.getter; |
| 588 patchFunction(reporter, getter, patch); | 530 patchFunction(reporter, getter, patch); |
| 589 } | 531 } |
| 590 | 532 |
| 591 void tryPatchSetter(DiagnosticReporter reporter, | 533 void tryPatchSetter( |
| 592 Element origin, | 534 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
| 593 FunctionElement patch) { | |
| 594 if (!origin.isAbstractField) { | 535 if (!origin.isAbstractField) { |
| 595 reporter.reportError( | 536 reporter.reportError( |
| 596 reporter.createMessage( | 537 reporter.createMessage( |
| 597 origin, | 538 origin, MessageKind.PATCH_NON_SETTER, {'name': origin.name}), |
| 598 MessageKind.PATCH_NON_SETTER, | |
| 599 {'name': origin.name}), | |
| 600 <DiagnosticMessage>[ | 539 <DiagnosticMessage>[ |
| 601 reporter.createMessage( | 540 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_SETTER, |
| 602 patch, | 541 {'setterName': patch.name}), |
| 603 MessageKind.PATCH_POINT_TO_SETTER, | |
| 604 {'setterName': patch.name}), | |
| 605 ]); | 542 ]); |
| 606 return; | 543 return; |
| 607 } | 544 } |
| 608 AbstractFieldElement originField = origin; | 545 AbstractFieldElement originField = origin; |
| 609 if (originField.setter == null) { | 546 if (originField.setter == null) { |
| 610 reporter.reportError( | 547 reporter.reportError( |
| 611 reporter.createMessage( | 548 reporter.createMessage( |
| 612 origin, | 549 origin, MessageKind.PATCH_NO_SETTER, {'setterName': patch.name}), |
| 613 MessageKind.PATCH_NO_SETTER, | |
| 614 {'setterName': patch.name}), | |
| 615 <DiagnosticMessage>[ | 550 <DiagnosticMessage>[ |
| 616 reporter.createMessage( | 551 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_SETTER, |
| 617 patch, | 552 {'setterName': patch.name}), |
| 618 MessageKind.PATCH_POINT_TO_SETTER, | |
| 619 {'setterName': patch.name}), | |
| 620 ]); | 553 ]); |
| 621 return; | 554 return; |
| 622 } | 555 } |
| 623 SetterElementX setter = originField.setter; | 556 SetterElementX setter = originField.setter; |
| 624 patchFunction(reporter, setter, patch); | 557 patchFunction(reporter, setter, patch); |
| 625 } | 558 } |
| 626 | 559 |
| 627 void tryPatchConstructor(DiagnosticReporter reporter, | 560 void tryPatchConstructor( |
| 628 Element origin, | 561 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
| 629 FunctionElement patch) { | |
| 630 if (!origin.isConstructor) { | 562 if (!origin.isConstructor) { |
| 631 reporter.reportError( | 563 reporter.reportError( |
| 632 reporter.createMessage( | 564 reporter.createMessage(origin, MessageKind.PATCH_NON_CONSTRUCTOR, |
| 633 origin, | |
| 634 MessageKind.PATCH_NON_CONSTRUCTOR, | |
| 635 {'constructorName': patch.name}), | 565 {'constructorName': patch.name}), |
| 636 <DiagnosticMessage>[ | 566 <DiagnosticMessage>[ |
| 637 reporter.createMessage( | 567 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_CONSTRUCTOR, |
| 638 patch, | 568 {'constructorName': patch.name}), |
| 639 MessageKind.PATCH_POINT_TO_CONSTRUCTOR, | |
| 640 {'constructorName': patch.name}), | |
| 641 ]); | 569 ]); |
| 642 return; | 570 return; |
| 643 } | 571 } |
| 644 patchFunction(reporter, origin, patch); | 572 patchFunction(reporter, origin, patch); |
| 645 } | 573 } |
| 646 | 574 |
| 647 void tryPatchFunction(DiagnosticReporter reporter, | 575 void tryPatchFunction( |
| 648 Element origin, | 576 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
| 649 FunctionElement patch) { | |
| 650 if (!origin.isFunction) { | 577 if (!origin.isFunction) { |
| 651 reporter.reportError( | 578 reporter.reportError( |
| 652 reporter.createMessage( | 579 reporter.createMessage(origin, MessageKind.PATCH_NON_FUNCTION, |
| 653 origin, | |
| 654 MessageKind.PATCH_NON_FUNCTION, | |
| 655 {'functionName': patch.name}), | 580 {'functionName': patch.name}), |
| 656 <DiagnosticMessage>[ | 581 <DiagnosticMessage>[ |
| 657 reporter.createMessage( | 582 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_FUNCTION, |
| 658 patch, | 583 {'functionName': patch.name}), |
| 659 MessageKind.PATCH_POINT_TO_FUNCTION, | |
| 660 {'functionName': patch.name}), | |
| 661 ]); | 584 ]); |
| 662 return; | 585 return; |
| 663 } | 586 } |
| 664 patchFunction(reporter, origin, patch); | 587 patchFunction(reporter, origin, patch); |
| 665 } | 588 } |
| 666 | 589 |
| 667 void patchFunction(DiagnosticReporter reporter, | 590 void patchFunction(DiagnosticReporter reporter, BaseFunctionElementX origin, |
| 668 BaseFunctionElementX origin, | 591 BaseFunctionElementX patch) { |
| 669 BaseFunctionElementX patch) { | |
| 670 if (!origin.modifiers.isExternal) { | 592 if (!origin.modifiers.isExternal) { |
| 671 reporter.reportError( | 593 reporter.reportError( |
| 672 reporter.createMessage(origin, MessageKind.PATCH_NON_EXTERNAL), | 594 reporter.createMessage(origin, MessageKind.PATCH_NON_EXTERNAL), |
| 673 <DiagnosticMessage>[ | 595 <DiagnosticMessage>[ |
| 674 reporter.createMessage( | 596 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_FUNCTION, |
| 675 patch, | 597 {'functionName': patch.name}), |
| 676 MessageKind.PATCH_POINT_TO_FUNCTION, | |
| 677 {'functionName': patch.name}), | |
| 678 ]); | 598 ]); |
| 679 return; | 599 return; |
| 680 } | 600 } |
| 681 if (origin.isPatched) { | 601 if (origin.isPatched) { |
| 682 reporter.internalError(origin, | 602 reporter.internalError( |
| 683 "Trying to patch a function more than once."); | 603 origin, "Trying to patch a function more than once."); |
| 684 } | 604 } |
| 685 origin.applyPatch(patch); | 605 origin.applyPatch(patch); |
| 686 } | 606 } |
| 687 | 607 |
| 688 PatchVersion getPatchVersion(Compiler compiler, Element element) { | 608 PatchVersion getPatchVersion(Compiler compiler, Element element) { |
| 689 return EagerAnnotationHandler.checkAnnotation(compiler, element, | 609 return EagerAnnotationHandler.checkAnnotation( |
| 690 const PatchAnnotationHandler()); | 610 compiler, element, const PatchAnnotationHandler()); |
| 691 } | 611 } |
| 692 | 612 |
| 693 class PatchVersion { | 613 class PatchVersion { |
| 694 final String tag; | 614 final String tag; |
| 695 | 615 |
| 696 const PatchVersion(this.tag); | 616 const PatchVersion(this.tag); |
| 697 | 617 |
| 698 bool isActive(String patchTag) => tag == null || tag == patchTag; | 618 bool isActive(String patchTag) => tag == null || tag == patchTag; |
| 699 | 619 |
| 700 String toString() => 'PatchVersion($tag)'; | 620 String toString() => 'PatchVersion($tag)'; |
| 701 } | 621 } |
| OLD | NEW |