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

Unified Diff: pkg/checked_mirrors/lib/src/checker.dart

Issue 111643015: Preliminary checked mirrors (not ready for review yet) (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years 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 | « pkg/checked_mirrors/lib/control.dart ('k') | pkg/checked_mirrors/lib/src/utils.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/checked_mirrors/lib/src/checker.dart
diff --git a/pkg/checked_mirrors/lib/src/checker.dart b/pkg/checked_mirrors/lib/src/checker.dart
new file mode 100644
index 0000000000000000000000000000000000000000..885949d52ae64ba74a81333129c440a13f1ff4b8
--- /dev/null
+++ b/pkg/checked_mirrors/lib/src/checker.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2013, 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.
+
+/// Defines rules and a chacker to determine if MirrorsUsed was used correctly.
+library checked_mirrors.src.checker;
+
+import "dart:mirrors";
+import "dart:async";
+
+import 'package:checked_mirrors/control.dart';
+import 'package:logging/logging.dart';
+
+import 'utils.dart';
+
+/// Controller used to log warnings detected by this checker.
+var _warnings = new StreamController<MirrorsUsedWarning>.broadcast(sync: true);
+var _logger = new Logger('checked_mirrors');
+
+/// Stream of warnings detected by this checker.
+Stream<MirrorsUsedWarning> get onWarning => _warnings.stream;
+
+/// A singleton checker used by the checked_mirrors system.
+MirrorsUsedChecker checker = new MirrorsUsedChecker();
+
+/// Symbol used to search for the MirrorUsed annotation. One dartbug.com/10360
+/// is fixed, we can just put the annotation directly in the checked_mirrors
+/// import.
+const _MIRROR_USED_LOCATION = #checked_mirrors_workaround_for_issue_10360;
+
+
+/// A warning generated by the checked_mirrors system.
+class MirrorsUsedWarning {
+
+ /// The kind of warning (undeclared symbol, target not marked).
+ final MirrorsUsedWarningKind kind;
+
+ /// The target of the mirror system that was not marked correctly. Or the
+ /// object containing the target (if symbol is not null).
+ final object;
+
+ final symbol;
+
+ MirrorsUsedWarning(this.kind, this.object, this.symbol);
+}
+
+/// Enumeration of warning kinds.
+class MirrorsUsedWarningKind {
+ final int _value;
+ const MirrorsUsedWarningKind(this._value);
+
+ /// Warning when reading a symbol that was not declared via
+ /// @MirrorUsed(symbols: ...)
+ static const UNDECLARED_SYMBOL = const MirrorsUsedWarningKind(1);
+
+ /// Warning when accesing a target that was not declared via
+ /// @MirrorUsed(targets: ...) or @MirrorUsed(metaTargets: ...)
+ static const TARGET_NOT_MARKED = const MirrorsUsedWarningKind(2);
+}
+
+/// Contains rules to check when mirrors are used, but they are not annotated
+/// appropriately.
+class MirrorsUsedChecker {
+ bool isInitialized = false;
+ bool _throwOnWarning = false;
+
+ Set symbols = new Set();
+ List globalRules = [];
+
+ Map<Uri, List> libraryRules = new Map<Uri, List>();
+
+ void init({throwOnWarning}) {
+ if (isInitialized) return;
+ _throwOnWarning = throwOnWarning;
+ var system = currentMirrorSystem();
+ system.libraries.forEach((uri, lib) {
+ if (uri.scheme == 'dart') return;
+ // TODO(sigmund): this should lookup the import instead. dartbug.com/10360
+ var target = lib.declarations[_MIRROR_USED_LOCATION];
+ if (target == null) return;
+ for (var metaMirror in target.metadata) {
+ var meta = metaMirror.reflectee;
+ if (meta is MirrorsUsed) {
+ addRule(uri, meta);
+ }
+ }
+ });
+ isInitialized = true;
+ }
+
+ /// Add a [rule] that was seen in [location].
+ void addRule(Uri location, MirrorsUsed annotation) {
+
+ // Symbols are handled separately since they are always global.
+ _loadSymbols(annotation.symbols);
+
+ // Direct target rules to specific libraries, if that's how they are
+ // declared.
+ var rule = new _Rule(annotation);
+ if (annotation.override == '*') {
+ globalRules.add(rule);
+ return;
+ }
+
+ if (annotation.override == null) {
+ _addToLibrary(location, rule);
+ return;
+ }
+
+ for (var name in annotation.override.split(',')) {
+ var symbol = MirrorSystem.getSymbol(name);
+ for (var lib in currentMirrorSystem().libraries.values) {
+ if (lib.simpleName == symbol) _addToLibrary(lib.uri, rule);
+ }
+ }
+ }
+
+ void _loadSymbols(value) {
+ if (value == null) return;
+ if (value is String) {
+ for (var s in value.split(',')) {
+ symbols.add(MirrorSystem.getSymbol(s));
+ }
+ } else if (value is Symbol) {
+ symbols.add(value);
+ } else if (value is List) {
+ for (var s in value) _loadSymbols(s);
+ } else {
+ _logger.warning('MirrorsUsed symbol not understood: $value');
+ }
+ }
+
+
+ void _addToLibrary(uri, rule) {
+ var rules = libraryRules[uri];
+ if (rules == null) {
+ rules = [];
+ libraryRules[uri] = rules;
+ }
+ rules.add(rule);
+ }
+
+ void useSymbol(Symbol symbol) {
+ if (!isInitialized || symbols.contains(symbol)) return;
+ var warning = new MirrorsUsedWarning(
+ MirrorsUsedWarningKind.UNDECLARED_SYMBOL, null, symbol);
+ _warnings.add(warning);
+ if (_throwOnWarning) throw warning;
+ }
+
+ void access(Mirror object, Symbol symbol) {
+ if (!isInitialized) return;
+
+ for (var rule in globalRules) {
+ if (rule.access(object, symbol)) return;
+ }
+
+ var uri = getLibraryUriOf(object);
+ var rules = libraryRules[uri];
+ if (rules != null) {
+ for (var rule in rules) {
+ if (rule.access(object, symbol)) return;
+ }
+ }
+
+ // No rule determined that it's ok to access [symbol]. Report an error.
+ var warning = new MirrorsUsedWarning(
+ MirrorsUsedWarningKind.TARGET_NOT_MARKED, object, symbol);
+ _warnings.add(warning);
+ if (_throwOnWarning) throw warning;
+ }
+}
+
+/// Contains a single rule, which is derived from a [MirrorsUsed] annotation.
+/// The information here is very similar to that of [MirrorsUsed] except it's
+/// normalized to make it easy to use and apply in the checker logic.
+class _Rule {
+ List<Mirror> targets = [];
+ List<Type> metaTargets = [];
+
+ _Rule(MirrorsUsed annotation) {
+ _loadTargets(annotation.targets);
+ _loadMetaTargets(annotation.metaTargets);
+ }
+
+ void _loadTargets(value) {
+ if (value == null) return;
+ if (value is Type) {
+ targets.add(value);
+ } else if (value is List) {
+ for (var t in value) _loadTargets(t);
+ } else {
+ _logger.warning('MirrorsUsed target not understood: $value');
+ }
+ }
+ void _loadMetaTargets(value) {
+ if (value == null) return;
+ if (value is Type) {
+ metaTargets.add(value);
+ } else if (value is List) {
+ for (var t in value) _loadMetaTargets(t);
+ } else {
+ _logger.warning('MirrorsUsed metaTarget not understood: $value');
+ }
+ }
+
+ bool access(object, Symbol symbol) {
+ // Explicitly declared targets/types
+ var declaration = getDeclarationOf(object);
+ if (declaration == null) return false;
+ if (targets.contains(declaration.reflectedType)) return true;
+
+ // Meta-targets on classes
+ for (var meta in declaration.metadata) {
+ var type = meta.type.reflectedType;
+ if (metaTargets.contains(type)) return true;
+ }
+
+ if (symbol == null) return false;
+
+ // Meta-targets on fields
+ var symbolDeclaration = declaration.declarations[symbol];
+ if (symbolDeclaration == null) return false;
+ for (var meta in symbolDeclaration.metadata) {
+ var type = meta.type.reflectedType;
+ if (metaTargets.contains(type)) return true;
+ }
+ }
+}
« no previous file with comments | « pkg/checked_mirrors/lib/control.dart ('k') | pkg/checked_mirrors/lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698