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

Side by Side Diff: pkg/analyzer/lib/src/dart/sdk/patch.dart

Issue 2411883003: Add SdkPatcher and implement top-level function / getter / setter patching. (Closed)
Patch Set: tweaks Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/dart/sdk/sdk.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, 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 analyzer.src.dart.sdk.patch;
6
7 import 'package:analyzer/dart/ast/ast.dart';
8 import 'package:analyzer/dart/ast/token.dart';
9 import 'package:analyzer/error/listener.dart';
10 import 'package:analyzer/file_system/file_system.dart';
11 import 'package:analyzer/src/dart/scanner/reader.dart';
12 import 'package:analyzer/src/dart/scanner/scanner.dart';
13 import 'package:analyzer/src/dart/sdk/sdk.dart';
14 import 'package:analyzer/src/generated/parser.dart';
15 import 'package:analyzer/src/generated/sdk.dart';
16 import 'package:analyzer/src/generated/source.dart';
17 import 'package:meta/meta.dart';
18 import 'package:path/src/context.dart';
19
20 /**
21 * [SdkPatcher] applies patches to SDK [CompilationUnit].
22 */
23 class SdkPatcher {
24 /**
25 * Patch the given [unit] of a SDK [source] with the patches defined in
26 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch
27 * file cannot be read, or the contents violates rules for patch files.
28 *
29 * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the
30 * defining unit of a library, so new top-level declarations should be
31 * added to this unit. For parts new declarations may be added only to the
32 * patched classes.
33 *
34 * TODO(scheglov) auto-detect [addNewTopLevelDeclarations]
35 */
36 void patch(FolderBasedDartSdk sdk, int platform,
37 AnalysisErrorListener errorListener, Source source, CompilationUnit unit,
38 {bool addNewTopLevelDeclarations: true}) {
39 // Prepare the patch files to apply.
40 List<String> patchPaths;
41 {
42 // TODO(scheglov) add support for patching parts
43 String uriStr = source.uri.toString();
44 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uriStr);
45 if (sdkLibrary == null) {
46 throw new ArgumentError(
47 'The library $uriStr is not defined in the SDK.');
48 }
49 patchPaths = sdkLibrary.getPatches(platform);
50 }
51
52 bool strongMode = sdk.analysisOptions.strongMode;
53 Context pathContext = sdk.resourceProvider.pathContext;
54 for (String path in patchPaths) {
55 String pathInLib = pathContext.joinAll(path.split('/'));
56 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib);
57 if (!patchFile.exists) {
58 throw new ArgumentError(
59 'The patch file ${patchFile.path} does not exist.');
60 }
61 Source patchSource = patchFile.createSource();
62 CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener);
63 _patchTopLevelDeclarations(
64 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations);
65 }
66 }
67
68 void _failExternalKeyword(Source source, String name, int offset) {
69 throw new ArgumentError(
70 'The keyword "external" was expected for "$name" in $source @ $offset.') ;
71 }
72
73 void _patchTopLevelDeclarations(
74 Source baseSource,
75 CompilationUnit baseUnit,
76 Source patchSource,
77 CompilationUnit patchUnit,
78 bool addNewTopLevelDeclarations) {
79 List<CompilationUnitMember> declarationsToAppend = [];
80 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) {
81 if (patchDeclaration is FunctionDeclaration) {
82 String name = patchDeclaration.name.name;
83 if (_hasPatchAnnotation(patchDeclaration.metadata)) {
84 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) {
85 if (patchDeclaration is FunctionDeclaration &&
86 baseDeclaration is FunctionDeclaration &&
87 baseDeclaration.name.name == name) {
88 if (_hasPatchAnnotation(patchDeclaration.metadata)) {
89 // Remove the "external" keyword.
90 if (baseDeclaration.externalKeyword != null) {
91 baseDeclaration.externalKeyword = null;
92 } else {
93 _failExternalKeyword(
94 baseSource, name, baseDeclaration.offset);
95 }
96 // Replace the body.
97 baseDeclaration.functionExpression.body =
98 patchDeclaration.functionExpression.body;
99 }
100 }
101 }
102 } else if (addNewTopLevelDeclarations) {
103 // No @patch, must be private.
104 if (!Identifier.isPrivateName(name)) {
105 throw new ArgumentError(
106 'The patch file $patchSource attempts to append '
107 'a non-private declaration "$name".');
108 }
109 declarationsToAppend.add(patchDeclaration);
110 }
111 }
112 }
113 // Append new top-level declarations.
114 baseUnit.declarations.addAll(declarationsToAppend);
115 }
116
117 /**
118 * Parse the given [source] into AST.
119 */
120 @visibleForTesting
121 static CompilationUnit parse(
122 Source source, bool strong, AnalysisErrorListener errorListener) {
123 String code = source.contents.data;
124
125 CharSequenceReader reader = new CharSequenceReader(code);
126 Scanner scanner = new Scanner(source, reader, errorListener);
127 scanner.scanGenericMethodComments = strong;
128 Token token = scanner.tokenize();
129 LineInfo lineInfo = new LineInfo(scanner.lineStarts);
130
131 Parser parser = new Parser(source, errorListener);
132 parser.parseGenericMethodComments = strong;
133 CompilationUnit unit = parser.parseCompilationUnit(token);
134 unit.lineInfo = lineInfo;
135 return unit;
136 }
137
138 /**
139 * Return `true` if [metadata] has the `@patch` annotation.
140 */
141 static bool _hasPatchAnnotation(List<Annotation> metadata) {
142 return metadata.any((annotation) {
143 Identifier name = annotation.name;
144 return annotation.constructorName == null &&
145 name is SimpleIdentifier &&
146 name.name == 'patch';
147 });
148 }
149 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/dart/sdk/sdk.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698