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

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: 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(DartSdk sdk, int platform, AnalysisErrorListener errorListener,
37 Source source, CompilationUnit unit,
38 {bool addNewTopLevelDeclarations: true}) {
39 if (sdk is FolderBasedDartSdk) {
Paul Berry 2016/10/11 20:24:45 Will sdk ever not be FolderBasedDartSdk? What sho
scheglov 2016/10/11 20:34:08 Done.
40 // Prepare the patch files to apply.
41 List<String> patchPaths;
42 {
43 String uriStr = source.uri.toString();
44 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uriStr);
Paul Berry 2016/10/11 20:24:45 When patching a file that is a "part" of an SDK li
scheglov 2016/10/11 20:34:08 I will add TODO for now.
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 =
63 parse(patchSource, strongMode, errorListener);
64 _patchTopLevelDeclarations(
65 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations);
66 }
67 }
68 }
69
70 void _failExternalKeyword(Source source, String name, int offset) {
71 throw new ArgumentError(
72 'The keyword "external" was expected for "$name" in $source @ $offset.') ;
73 }
74
75 void _patchTopLevelDeclarations(
76 Source baseSource,
77 CompilationUnit baseUnit,
78 Source patchSource,
79 CompilationUnit patchUnit,
80 bool addNewTopLevelDeclarations) {
81 List<CompilationUnitMember> declarationsToAppend = [];
82 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) {
83 if (patchDeclaration is FunctionDeclaration) {
84 String name = patchDeclaration.name.name;
85 if (_hasPatchAnnotation(patchDeclaration.metadata)) {
86 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) {
87 if (patchDeclaration is FunctionDeclaration &&
88 baseDeclaration is FunctionDeclaration &&
89 baseDeclaration.name.name == name) {
90 if (_hasPatchAnnotation(patchDeclaration.metadata)) {
91 // Remove the "external" keyword.
92 if (baseDeclaration.externalKeyword != null) {
93 baseDeclaration.externalKeyword = null;
94 } else {
95 _failExternalKeyword(
96 baseSource, name, baseDeclaration.offset);
97 }
98 // Replace the body.
99 baseDeclaration.functionExpression.body =
100 patchDeclaration.functionExpression.body;
101 }
102 }
103 }
104 } else if (addNewTopLevelDeclarations) {
105 // No @patch, must be private.
106 if (!Identifier.isPrivateName(name)) {
107 throw new ArgumentError(
108 'The patch file $patchSource attempts to append '
109 'a non-private declaration "$name".');
110 }
111 declarationsToAppend.add(patchDeclaration);
112 }
113 }
114 }
115 // Append new top-level declarations.
116 baseUnit.declarations.addAll(declarationsToAppend);
117 }
118
119 /**
120 * Parse the given [source] into AST.
121 */
122 @visibleForTesting
123 static CompilationUnit parse(
124 Source source, bool strong, AnalysisErrorListener errorListener) {
125 String code = source.contents.data;
126
127 CharSequenceReader reader = new CharSequenceReader(code);
128 Scanner scanner = new Scanner(source, reader, errorListener);
129 scanner.scanGenericMethodComments = strong;
130 Token token = scanner.tokenize();
131 LineInfo lineInfo = new LineInfo(scanner.lineStarts);
132
133 Parser parser = new Parser(source, errorListener);
134 parser.parseGenericMethodComments = strong;
135 CompilationUnit unit = parser.parseCompilationUnit(token);
136 unit.lineInfo = lineInfo;
137 return unit;
138 }
139
140 /**
141 * Return `true` if [metadata] has the `@patch` annotation.
142 */
143 static bool _hasPatchAnnotation(List<Annotation> metadata) {
144 return metadata.any((annotation) {
145 Identifier name = annotation.name;
146 return annotation.constructorName == null &&
147 name is SimpleIdentifier &&
148 name.name == 'patch';
149 });
150 }
151 }
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