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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/dart/sdk/sdk.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/lib/src/dart/sdk/patch.dart
diff --git a/pkg/analyzer/lib/src/dart/sdk/patch.dart b/pkg/analyzer/lib/src/dart/sdk/patch.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3a5e5acab8a4e8b00e2c462dce9b2ddb66cd935a
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/sdk/patch.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library analyzer.src.dart.sdk.patch;
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/scanner/reader.dart';
+import 'package:analyzer/src/dart/scanner/scanner.dart';
+import 'package:analyzer/src/dart/sdk/sdk.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
+import 'package:path/src/context.dart';
+
+/**
+ * [SdkPatcher] applies patches to SDK [CompilationUnit].
+ */
+class SdkPatcher {
+ /**
+ * Patch the given [unit] of a SDK [source] with the patches defined in
+ * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch
+ * file cannot be read, or the contents violates rules for patch files.
+ *
+ * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the
+ * defining unit of a library, so new top-level declarations should be
+ * added to this unit. For parts new declarations may be added only to the
+ * patched classes.
+ *
+ * TODO(scheglov) auto-detect [addNewTopLevelDeclarations]
+ */
+ void patch(FolderBasedDartSdk sdk, int platform,
+ AnalysisErrorListener errorListener, Source source, CompilationUnit unit,
+ {bool addNewTopLevelDeclarations: true}) {
+ // Prepare the patch files to apply.
+ List<String> patchPaths;
+ {
+ // TODO(scheglov) add support for patching parts
+ String uriStr = source.uri.toString();
+ SdkLibrary sdkLibrary = sdk.getSdkLibrary(uriStr);
+ if (sdkLibrary == null) {
+ throw new ArgumentError(
+ 'The library $uriStr is not defined in the SDK.');
+ }
+ patchPaths = sdkLibrary.getPatches(platform);
+ }
+
+ bool strongMode = sdk.analysisOptions.strongMode;
+ Context pathContext = sdk.resourceProvider.pathContext;
+ for (String path in patchPaths) {
+ String pathInLib = pathContext.joinAll(path.split('/'));
+ File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib);
+ if (!patchFile.exists) {
+ throw new ArgumentError(
+ 'The patch file ${patchFile.path} does not exist.');
+ }
+ Source patchSource = patchFile.createSource();
+ CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener);
+ _patchTopLevelDeclarations(
+ source, unit, patchSource, patchUnit, addNewTopLevelDeclarations);
+ }
+ }
+
+ void _failExternalKeyword(Source source, String name, int offset) {
+ throw new ArgumentError(
+ 'The keyword "external" was expected for "$name" in $source @ $offset.');
+ }
+
+ void _patchTopLevelDeclarations(
+ Source baseSource,
+ CompilationUnit baseUnit,
+ Source patchSource,
+ CompilationUnit patchUnit,
+ bool addNewTopLevelDeclarations) {
+ List<CompilationUnitMember> declarationsToAppend = [];
+ for (CompilationUnitMember patchDeclaration in patchUnit.declarations) {
+ if (patchDeclaration is FunctionDeclaration) {
+ String name = patchDeclaration.name.name;
+ if (_hasPatchAnnotation(patchDeclaration.metadata)) {
+ for (CompilationUnitMember baseDeclaration in baseUnit.declarations) {
+ if (patchDeclaration is FunctionDeclaration &&
+ baseDeclaration is FunctionDeclaration &&
+ baseDeclaration.name.name == name) {
+ if (_hasPatchAnnotation(patchDeclaration.metadata)) {
+ // Remove the "external" keyword.
+ if (baseDeclaration.externalKeyword != null) {
+ baseDeclaration.externalKeyword = null;
+ } else {
+ _failExternalKeyword(
+ baseSource, name, baseDeclaration.offset);
+ }
+ // Replace the body.
+ baseDeclaration.functionExpression.body =
+ patchDeclaration.functionExpression.body;
+ }
+ }
+ }
+ } else if (addNewTopLevelDeclarations) {
+ // No @patch, must be private.
+ if (!Identifier.isPrivateName(name)) {
+ throw new ArgumentError(
+ 'The patch file $patchSource attempts to append '
+ 'a non-private declaration "$name".');
+ }
+ declarationsToAppend.add(patchDeclaration);
+ }
+ }
+ }
+ // Append new top-level declarations.
+ baseUnit.declarations.addAll(declarationsToAppend);
+ }
+
+ /**
+ * Parse the given [source] into AST.
+ */
+ @visibleForTesting
+ static CompilationUnit parse(
+ Source source, bool strong, AnalysisErrorListener errorListener) {
+ String code = source.contents.data;
+
+ CharSequenceReader reader = new CharSequenceReader(code);
+ Scanner scanner = new Scanner(source, reader, errorListener);
+ scanner.scanGenericMethodComments = strong;
+ Token token = scanner.tokenize();
+ LineInfo lineInfo = new LineInfo(scanner.lineStarts);
+
+ Parser parser = new Parser(source, errorListener);
+ parser.parseGenericMethodComments = strong;
+ CompilationUnit unit = parser.parseCompilationUnit(token);
+ unit.lineInfo = lineInfo;
+ return unit;
+ }
+
+ /**
+ * Return `true` if [metadata] has the `@patch` annotation.
+ */
+ static bool _hasPatchAnnotation(List<Annotation> metadata) {
+ return metadata.any((annotation) {
+ Identifier name = annotation.name;
+ return annotation.constructorName == null &&
+ name is SimpleIdentifier &&
+ name.name == 'patch';
+ });
+ }
+}
« 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