Update dart packagae
Fixed build
Change-Id: I9c9e25409b2be8c3f191c038eb37e9d1476394a9
diff --git a/pub/archive/BUILD.gn b/pub/archive/BUILD.gn
index 8b72178..2da32bf 100644
--- a/pub/archive/BUILD.gn
+++ b/pub/archive/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for archive-1.0.27
+# This file is generated by importer.py for archive-1.0.28
import("//build/dart/dart_package.gni")
diff --git a/pub/archive/README.md b/pub/archive/README.md
index d2cc090..4f10c0b 100755
--- a/pub/archive/README.md
+++ b/pub/archive/README.md
@@ -1,4 +1,5 @@
# archive
+[![Build Status](https://travis-ci.org/brendan-duncan/archive.svg?branch=master)](https://travis-ci.org/brendan-duncan/archive)
## Overview
diff --git a/pub/archive/lib/src/gzip_encoder.dart b/pub/archive/lib/src/gzip_encoder.dart
index a88936b..f769c6d 100755
--- a/pub/archive/lib/src/gzip_encoder.dart
+++ b/pub/archive/lib/src/gzip_encoder.dart
@@ -24,7 +24,7 @@
static const int OS_NTFS = 11;
static const int OS_QDOS = 12;
static const int OS_ACORN_RISCOS = 13;
- static const int OS_UNKNOWN = 25;
+ static const int OS_UNKNOWN = 255;
List<int> encode(List<int> data, {int level}) {
OutputStream output = new OutputStream();
diff --git a/pub/archive/lib/src/util/mem_ptr.dart b/pub/archive/lib/src/util/mem_ptr.dart
index b5d4038..f14fea1 100755
--- a/pub/archive/lib/src/util/mem_ptr.dart
+++ b/pub/archive/lib/src/util/mem_ptr.dart
@@ -46,7 +46,7 @@
operator[]=(int index, int value) => buffer[offset + index] = value;
/**
- * The number of bytes remianing in the buffer.
+ * The number of bytes remaining in the buffer.
*/
int get length => _length - offset;
@@ -67,7 +67,7 @@
/**
* Set a range of bytes in this buffer to [value], at [start] offset from the
- * current read poisiton, and [length] number of bytes.
+ * current read position, and [length] number of bytes.
*/
void memset(int start, int length, int value) {
buffer.fillRange(offset + start, offset + start + length, value);
diff --git a/pub/archive/lib/src/util/output_stream.dart b/pub/archive/lib/src/util/output_stream.dart
index e9fccea..37aada6 100755
--- a/pub/archive/lib/src/util/output_stream.dart
+++ b/pub/archive/lib/src/util/output_stream.dart
@@ -133,7 +133,8 @@
blockSize = required;
}
}
- Uint8List newBuffer = new Uint8List(_buffer.length + blockSize);
+ int newLength = (_buffer.length + blockSize) * 2;
+ Uint8List newBuffer = new Uint8List(newLength);
newBuffer.setRange(0, _buffer.length, _buffer);
_buffer = newBuffer;
}
diff --git a/pub/archive/lib/src/zlib/deflate.dart b/pub/archive/lib/src/zlib/deflate.dart
index a20b547..7e2025b 100755
--- a/pub/archive/lib/src/zlib/deflate.dart
+++ b/pub/archive/lib/src/zlib/deflate.dart
@@ -37,6 +37,17 @@
}
/**
+ * Get the resulting compressed bytes without storing the resulting data to
+ * minimize memory usage.
+ */
+ List<int> takeBytes() {
+ _flushPending();
+ List<int> bytes = _output.getBytes();
+ _output.clear();
+ return bytes;
+ }
+
+ /**
* Add more data to be deflated.
*/
void addBytes(List<int> bytes, {int flush: FINISH}) {
diff --git a/pub/archive/pubspec.yaml b/pub/archive/pubspec.yaml
index 78f2be7..040247e 100755
--- a/pub/archive/pubspec.yaml
+++ b/pub/archive/pubspec.yaml
@@ -1,5 +1,5 @@
name: archive
-version: 1.0.27
+version: 1.0.28
author: Brendan Duncan <brendanduncan@gmail.com>
description: Provides encoders and decoders for various archive and compression formats such as zip, tar, bzip2, gzip, and zlib.
homepage: https://github.com/brendan-duncan/archive
diff --git a/pub/async/BUILD.gn b/pub/async/BUILD.gn
index e1ec0a4..df52430 100644
--- a/pub/async/BUILD.gn
+++ b/pub/async/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for async-1.12.0
+# This file is generated by importer.py for async-1.13.0
import("//build/dart/dart_package.gni")
diff --git a/pub/async/CHANGELOG.md b/pub/async/CHANGELOG.md
index c49a4bd..5685503 100644
--- a/pub/async/CHANGELOG.md
+++ b/pub/async/CHANGELOG.md
@@ -1,8 +1,22 @@
+## 1.13.0
+
+* Add `collectBytes` and `collectBytesCancelable` functions which collects
+ list-of-byte events into a single byte list.
+
+* Fix a bug where rejecting a `StreamQueueTransaction` would throw a
+ `StateError` if `StreamQueue.rest` had been called on one of its child queues.
+
+* `StreamQueue.withTransaction()` now properly returns whether or not the
+ transaction was committed.
+
## 1.12.0
* Add an `AsyncCache` class that caches asynchronous operations for a period of
time.
+* Add `StreamQueue.peek` and `StreamQueue.lookAheead`.
+ These allow users to look at events without consuming them.
+
* Add `StreamQueue.startTransaction()` and `StreamQueue.withTransaction()`.
These allow users to conditionally consume events based on their values.
diff --git a/pub/async/lib/async.dart b/pub/async/lib/async.dart
index c11f0b5..e090032 100644
--- a/pub/async/lib/async.dart
+++ b/pub/async/lib/async.dart
@@ -4,6 +4,7 @@
export "src/async_cache.dart";
export "src/async_memoizer.dart";
+export "src/byte_collector.dart";
export "src/cancelable_operation.dart";
export "src/delegate/event_sink.dart";
export "src/delegate/future.dart";
diff --git a/pub/async/lib/src/byte_collector.dart b/pub/async/lib/src/byte_collector.dart
new file mode 100644
index 0000000..a8d2dbf
--- /dev/null
+++ b/pub/async/lib/src/byte_collector.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2017, 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.
+
+import "dart:async";
+import "dart:typed_data";
+import "cancelable_operation.dart";
+
+/// Collects an asynchronous sequence of byte lists into a single list of bytes.
+///
+/// If the [source] stream emits an error event,
+/// the collection fails and the returned future completes with the same error.
+///
+/// If any of the input data are not valid bytes, they will be truncated to
+/// an eight-bit unsigned value in the resulting list.
+Future<Uint8List> collectBytes(Stream<List<int>> source) {
+ return _collectBytes(source, (_, result) => result);
+}
+
+/// Collects an asynchronous sequence of byte lists into a single list of bytes.
+///
+/// Returns a [CancelableOperation] that provides the result future and a way
+/// to cancel the collection early.
+///
+/// If the [source] stream emits an error event,
+/// the collection fails and the returned future completes with the same error.
+///
+/// If any of the input data are not valid bytes, they will be truncated to
+/// an eight-bit unsigned value in the resulting list.
+CancelableOperation<Uint8List> collectBytesCancelable(
+ Stream<List<int>> source) {
+ return _collectBytes(source, (subscription, result) =>
+ new CancelableOperation.fromFuture(result, onCancel: subscription.cancel)
+ );
+}
+
+/// Generalization over [collectBytes] and [collectBytesCancelable].
+///
+/// Performs all the same operations, but the final result is created
+/// by the [result] function, which has access to the stream subscription
+/// so it can cancel the operation.
+T _collectBytes<T>(
+ Stream<List<int>> source,
+ T result(StreamSubscription<List<int>> subscription,
+ Future<Uint8List> result)) {
+ var byteLists = <List<int>>[];
+ var length = 0;
+ var completer = new Completer<Uint8List>.sync();
+ var subscription = source.listen(
+ (bytes) {
+ byteLists.add(bytes);
+ length += bytes.length;
+ },
+ onError: completer.completeError,
+ onDone: () {
+ completer.complete(_collect(length, byteLists));
+ },
+ cancelOnError: true);
+ return result(subscription, completer.future);
+}
+
+// Join a lists of bytes with a known total length into a single [Uint8List].
+Uint8List _collect(int length, List<List<int>> byteLists) {
+ var result = new Uint8List(length);
+ int i = 0;
+ for (var byteList in byteLists) {
+ var end = i + byteList.length;
+ result.setRange(i, end, byteList);
+ i = end;
+ }
+ return result;
+}
diff --git a/pub/async/lib/src/stream_queue.dart b/pub/async/lib/src/stream_queue.dart
index 7b73eac..99e7436 100644
--- a/pub/async/lib/src/stream_queue.dart
+++ b/pub/async/lib/src/stream_queue.dart
@@ -131,6 +131,22 @@
throw _failClosed();
}
+
+ /// Look at the next [count] data events without consuming them.
+ ///
+ /// Works like [take] except that the events are left in the queue.
+ /// If one of the next [count] events is an error, the returned future
+ /// completes with this error, and the error is still left in the queue.
+ Future<List<T>> lookAhead(int count) {
+ if (count < 0) throw new RangeError.range(count, 0, null, "count");
+ if (!_isClosed) {
+ var request = new _LookAheadRequest<T>(count);
+ _addRequest(request);
+ return request.future;
+ }
+ throw _failClosed();
+ }
+
/// Requests the next (yet unrequested) event from the stream.
///
/// When the requested event arrives, the returned future is completed with
@@ -154,6 +170,19 @@
throw _failClosed();
}
+ /// Looks at the next (yet unrequested) event from the stream.
+ ///
+ /// Like [next] except that the event is not consumed.
+ /// If the next event is an error event, it stays in the queue.
+ Future<T> get peek {
+ if (!_isClosed) {
+ var nextRequest = new _PeekRequest<T>();
+ _addRequest(nextRequest);
+ return nextRequest.future;
+ }
+ throw _failClosed();
+ }
+
/// Returns a stream of all the remaning events of the source stream.
///
/// All requested [next], [skip] or [take] operations are completed
@@ -299,6 +328,7 @@
} else {
transaction.reject();
}
+ return result;
}, onError: (error) {
transaction.commit(queue);
throw error;
@@ -353,8 +383,8 @@
/// `cancel`.
///
/// After calling `cancel`, no further events can be requested.
- /// None of [next], [rest], [skip], [take] or [cancel] may be
- /// called again.
+ /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel]
+ /// may be called again.
Future cancel({bool immediate: false}) {
if (_isClosed) throw _failClosed();
_isClosed = true;
@@ -519,6 +549,7 @@
if (_isDone) {
return new Stream<T>.empty();
}
+ _isDone = true;
if (_subscription == null) {
return _sourceStream;
@@ -526,7 +557,6 @@
var subscription = _subscription;
_subscription = null;
- _isDone = true;
var wasPaused = subscription.isPaused;
var result = new SubscriptionStream<T>(subscription);
@@ -692,15 +722,42 @@
return true;
}
if (isDone) {
- var errorFuture =
- new Future.sync(() => throw new StateError("No elements"));
- _completer.complete(errorFuture);
+ _completer.completeError(new StateError("No elements"),
+ StackTrace.current);
return true;
}
return false;
}
}
+
+/// Request for a [StreamQueue.peek] call.
+///
+/// Completes the returned future when receiving the first event,
+/// and is then complete, but doesn't consume the event.
+class _PeekRequest<T> implements _EventRequest<T> {
+ /// Completer for the future returned by [StreamQueue.next].
+ final _completer = new Completer<T>();
+
+ _PeekRequest();
+
+ Future<T> get future => _completer.future;
+
+ bool update(QueueList<Result<T>> events, bool isDone) {
+ if (events.isNotEmpty) {
+ events.first.complete(_completer);
+ return true;
+ }
+ if (isDone) {
+ _completer.completeError(new StateError("No elements"),
+ StackTrace.current);
+ return true;
+ }
+ return false;
+ }
+}
+
+
/// Request for a [StreamQueue.skip] call.
class _SkipRequest<T> implements _EventRequest<T> {
/// Completer for the future returned by the skip call.
@@ -738,8 +795,8 @@
}
}
-/// Request for a [StreamQueue.take] call.
-class _TakeRequest<T> implements _EventRequest<T> {
+/// Common superclass for [_TakeRequest] and [_LookAheadRequest].
+abstract class _ListRequest<T> implements _EventRequest<T> {
/// Completer for the future returned by the take call.
final _completer = new Completer<List<T>>();
@@ -752,10 +809,16 @@
/// this value.
final int _eventsToTake;
- _TakeRequest(this._eventsToTake);
+ _ListRequest(this._eventsToTake);
/// The future completed when the correct number of events have been captured.
Future<List<T>> get future => _completer.future;
+}
+
+
+/// Request for a [StreamQueue.take] call.
+class _TakeRequest<T> extends _ListRequest<T> {
+ _TakeRequest(int eventsToTake) : super(eventsToTake);
bool update(QueueList<Result<T>> events, bool isDone) {
while (_list.length < _eventsToTake) {
@@ -766,7 +829,7 @@
var event = events.removeFirst();
if (event.isError) {
- _completer.completeError(event.asError.error, event.asError.stackTrace);
+ event.asError.complete(_completer);
return true;
}
_list.add(event.asValue.value);
@@ -776,6 +839,30 @@
}
}
+
+/// Request for a [StreamQueue.lookAhead] call.
+class _LookAheadRequest<T> extends _ListRequest<T> {
+ _LookAheadRequest(int eventsToTake) : super(eventsToTake);
+
+ bool update(QueueList<Result<T>> events, bool isDone) {
+ while (_list.length < _eventsToTake) {
+ if (events.length == _list.length) {
+ if (isDone) break;
+ return false;
+ }
+ var event = events.elementAt(_list.length);
+ if (event.isError) {
+ event.asError.complete(_completer);
+ return true;
+ }
+ _list.add(event.asValue.value);
+ }
+ _completer.complete(_list);
+ return true;
+ }
+}
+
+
/// Request for a [StreamQueue.cancel] call.
///
/// The request needs no events, it just waits in the request queue
@@ -874,7 +961,7 @@
/// Request for a [StreamQueue.startTransaction] call.
///
/// This request isn't complete until the user calls
-/// [StreamQueueTransaction.commit] or [StreamQueue.rejectTransaction], at which
+/// [StreamQueueTransaction.commit] or [StreamQueueTransaction.reject], at which
/// point it manually removes itself from the request queue and calls
/// [StreamQueue._updateRequests].
class _TransactionRequest<T> implements _EventRequest<T> {
diff --git a/pub/async/pubspec.yaml b/pub/async/pubspec.yaml
index ec62ff2..6fce524 100644
--- a/pub/async/pubspec.yaml
+++ b/pub/async/pubspec.yaml
@@ -1,5 +1,5 @@
name: async
-version: 1.12.0
+version: 1.13.0
author: Dart Team <misc@dartlang.org>
description: Utility functions and classes related to the 'dart:async' library.
homepage: https://www.github.com/dart-lang/async
@@ -10,4 +10,4 @@
stack_trace: "^1.0.0"
test: "^0.12.0"
environment:
- sdk: ">=1.19.0 <2.0.0"
+ sdk: ">=1.21.0 <2.0.0"
diff --git a/pub/dart_style/.status b/pub/dart_style/.status
deleted file mode 100644
index 5333c45..0000000
--- a/pub/dart_style/.status
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2014, 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.
-
-# Don't run any test-like files that show up in packages directories. It
-# shouldn't be necessary to run "pub install" in these packages, but if you do
-# it shouldn't break the tests.
-packages/*/*: Skip
-*/packages/*/*: Skip
-*/*/packages/*/*: Skip
-*/*/*/packages/*/*: Skip
-*/*/*/*/packages/*/*: Skip
-*/*/*/*/*/packages/*/*: Skip
-
-# No need to run any of the built output.
-build/test/*: Skip
-
-[ $browser ]
-test/*: Skip # Tests only run on the VM since they use dart:io.
diff --git a/pub/dart_style/.test_config b/pub/dart_style/.test_config
new file mode 100644
index 0000000..531426a
--- /dev/null
+++ b/pub/dart_style/.test_config
@@ -0,0 +1,5 @@
+{
+ "test_package": {
+ "platforms": ["vm"]
+ }
+}
\ No newline at end of file
diff --git a/pub/dart_style/BUILD.gn b/pub/dart_style/BUILD.gn
index 0841c51..d04ab7a 100644
--- a/pub/dart_style/BUILD.gn
+++ b/pub/dart_style/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for dart_style-0.2.15
+# This file is generated by importer.py for dart_style-0.2.16
import("//build/dart/dart_package.gni")
diff --git a/pub/dart_style/CHANGELOG.md b/pub/dart_style/CHANGELOG.md
index 8795385..ec5fdd7 100644
--- a/pub/dart_style/CHANGELOG.md
+++ b/pub/dart_style/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.16
+
+* Don't discard type arguments on method calls with closure arguments (#582).
+
# 0.2.15
* Support `covariant` modifier on methods.
diff --git a/pub/dart_style/bin/format.dart b/pub/dart_style/bin/format.dart
index b43bf5d..e1eabbc 100644
--- a/pub/dart_style/bin/format.dart
+++ b/pub/dart_style/bin/format.dart
@@ -14,7 +14,7 @@
import 'package:dart_style/src/source_code.dart';
// Note: The following line of code is modified by tool/grind.dart.
-const version = "0.2.15";
+const version = "0.2.16";
void main(List<String> args) {
var parser = new ArgParser(allowTrailingOptions: true);
diff --git a/pub/dart_style/lib/src/call_chain_visitor.dart b/pub/dart_style/lib/src/call_chain_visitor.dart
index e1fec05..9c40dca 100644
--- a/pub/dart_style/lib/src/call_chain_visitor.dart
+++ b/pub/dart_style/lib/src/call_chain_visitor.dart
@@ -410,6 +410,7 @@
void _writeBlockCall(MethodInvocation invocation) {
_visitor.token(invocation.operator);
_visitor.token(invocation.methodName.token);
+ _visitor.visit(invocation.typeArguments);
_visitor.visit(invocation.argumentList);
}
diff --git a/pub/dart_style/pubspec.yaml b/pub/dart_style/pubspec.yaml
index ad7f415..20c7310 100644
--- a/pub/dart_style/pubspec.yaml
+++ b/pub/dart_style/pubspec.yaml
@@ -1,6 +1,6 @@
name: dart_style
# Note: See tool/grind.dart for how to bump the version.
-version: 0.2.15
+version: 0.2.16
author: Dart Team <misc@dartlang.org>
description: Opinionated, automatic Dart source code formatter.
homepage: https://github.com/dart-lang/dart_style
diff --git a/pub/front_end/.analysis_options b/pub/front_end/.analysis_options
index a10d4c5..f0ac32f 100644
--- a/pub/front_end/.analysis_options
+++ b/pub/front_end/.analysis_options
@@ -1,2 +1,4 @@
analyzer:
strong-mode: true
+ language:
+ enableSuperMixins: true
diff --git a/pub/front_end/.history/pubspec_20170131134120.yaml b/pub/front_end/.history/pubspec_20170131134120.yaml
new file mode 100644
index 0000000..5f15860
--- /dev/null
+++ b/pub/front_end/.history/pubspec_20170131134120.yaml
@@ -0,0 +1,18 @@
+name: front_end
+version: 0.1.0-alpha.1
+author: Dart Team <misc@dartlang.org>
+description: Front end for compilation of Dart code.
+homepage: https://github.com/dart-lang/sdk/tree/master/pkg/front_end
+environment:
+ sdk: '>=1.12.0 <2.0.0'
+dependencies:
+ analyzer: 0.30.0-alpha.1
+ path: '^1.3.9'
+ source_span: '^1.2.3'
+dev_dependencies:
+ # TODO(sigmund): update to a version constraint once we roll the latest kernel
+ # to the repo.
+ kernel: {path: ../../pkg/kernel}
+ package_config: '^1.0.0'
+ test: ^0.12.0
+ test_reflective_loader: ^0.1.0
diff --git a/pub/front_end/.vscode/settings.json b/pub/front_end/.vscode/settings.json
new file mode 100644
index 0000000..14a8645
--- /dev/null
+++ b/pub/front_end/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "files.exclude": {}
+}
\ No newline at end of file
diff --git a/pub/front_end/BUILD.gn b/pub/front_end/BUILD.gn
index c610197..1f24e3a 100644
--- a/pub/front_end/BUILD.gn
+++ b/pub/front_end/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for front_end-0.1.0-alpha.0
+# This file is generated by importer.py for front_end-0.1.0-alpha.1
import("//build/dart/dart_package.gni")
diff --git a/pub/front_end/lib/compilation_error.dart b/pub/front_end/lib/compilation_error.dart
index a238575..5e30da1 100644
--- a/pub/front_end/lib/compilation_error.dart
+++ b/pub/front_end/lib/compilation_error.dart
@@ -18,8 +18,8 @@
/// A text description of how the user can fix the error. May be `null`.
String get correction;
- /// The source location where the error occurred.
- SourceSpan get location;
+ /// The source span where the error occurred.
+ SourceSpan get span;
/// A text description of the compile error.
String get message;
diff --git a/pub/front_end/lib/compiler_options.dart b/pub/front_end/lib/compiler_options.dart
index f9f2f29..5c0cb11 100644
--- a/pub/front_end/lib/compiler_options.dart
+++ b/pub/front_end/lib/compiler_options.dart
@@ -6,6 +6,10 @@
import 'compilation_error.dart';
import 'file_system.dart';
+import 'physical_file_system.dart';
+
+/// Default error handler used by [CompielerOptions.onError].
+void defaultErrorHandler(CompilationError error) => throw error;
/// Callback used to report errors encountered during compilation.
typedef void ErrorHandler(CompilationError error);
@@ -14,76 +18,72 @@
///
/// Not intended to be implemented or extended by clients.
class CompilerOptions {
- /// The path to the Dart SDK.
+ /// The URI of the root of the Dart SDK (typically a "file:" URI).
///
/// If `null`, the SDK will be searched for using
/// [Platform.resolvedExecutable] as a starting point.
///
/// This option is mutually exclusive with [sdkSummary].
- String sdkPath;
+ Uri sdkRoot;
/// Callback to which compilation errors should be delivered.
///
- /// If `null`, the first error will be reported by throwing an exception of
+ /// By default, the first error will be reported by throwing an exception of
/// type [CompilationError].
- ErrorHandler onError;
+ ErrorHandler onError = defaultErrorHandler;
- /// Path to the ".packages" file.
+ /// URI of the ".packages" file (typically a "file:" URI).
///
/// If `null`, the ".packages" file will be found via the standard
/// package_config search algorithm.
- String packagesFilePath;
+ ///
+ /// If the URI's path component is empty (e.g. `new Uri()`), no packages file
+ /// will be used.
+ Uri packagesFileUri;
- /// Paths to the input summary files (excluding the SDK summary). These files
- /// should all be linked summaries. They should also be closed, in the sense
- /// that any libraries they reference should also appear in [inputSummaries]
- /// or [sdkSummary].
- List<String> inputSummaries = [];
+ /// URIs of input summary files (excluding the SDK summary; typically these
+ /// will be "file:" URIs). These files should all be linked summaries. They
+ /// should also be closed, in the sense that any libraries they reference
+ /// should also appear in [inputSummaries] or [sdkSummary].
+ List<Uri> inputSummaries = [];
- /// Path to the SDK summary file.
+ /// URI of the SDK summary file (typically a "file:" URI).
///
/// This should be a linked summary. If `null`, the SDK summary will be
- /// searched for at a default location within [sdkPath].
+ /// searched for at a default location within [sdkRoot].
///
- /// This option is mutually exclusive with [sdkPath]. TODO(paulberry): if the
+ /// This option is mutually exclusive with [sdkRoot]. TODO(paulberry): if the
/// VM does not contain a pickled copy of the SDK, we might need to change
/// this.
- String sdkSummary;
+ Uri sdkSummary;
/// URI override map.
///
- /// This is a map from Uri to file path. Any URI override listed in this map
- /// takes precedence over the URI resolution that would be implied by the
- /// packages file (see [packagesFilePath]) and/or [bazelRoots].
+ /// This is a map from URIs that might appear in import/export/part statements
+ /// to URIs that should be used to locate the corresponding files in the
+ /// [fileSystem]. Any URI override listed in this map takes precedence over
+ /// the URI resolution that would be implied by the packages file (see
+ /// [packagesFileUri]) and/or [multiRoots].
///
/// If a URI is not listed in this map, then the normal URI resolution
/// algorithm will be used.
///
/// TODO(paulberry): transition analyzer and dev_compiler to use the
- /// "file:///bazel-root" mechanism, and then remove this.
+ /// "multi-root:" mechanism, and then remove this.
@deprecated
- Map<Uri, String> uriOverride = {};
+ Map<Uri, Uri> uriOverride = {};
- /// Bazel roots.
+ /// Multi-roots.
///
- /// Any Uri that resolves to "file:///bazel-root/$rest" will be searched for
- /// at "$root/$rest" ("$root\\$rest" in Windows), where "$root" is drawn from
- /// this list. If the file is not found at any of those locations, the URI
- /// "file:///bazel-root/$rest" will be used directly.
+ /// Any Uri that resolves to "multi-root:///$rest" will be searched for
+ /// at "$root/$rest", where "$root" is drawn from this list.
///
- /// Intended use: if the Bazel workspace is located at path "$workspace", this
- /// could be set to `['$workspace', '$workspace/bazel-bin',
- /// '$workspace/bazel-genfiles']`, effectively overlaying source and generated
- /// files.
- List<String> bazelRoots = [];
-
- /// Sets the platform bit, which determines which patch files should be
- /// applied to the SDK.
- ///
- /// The value should be a power of two, and should match the `PLATFORM` bit
- /// flags in sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart. If
- /// zero, no patch files will be applied.
- int platformBit;
+ /// Intended use: if the user has a Bazel workspace located at path
+ /// "$workspace", this could be set to the file URIs corresponding to the
+ /// paths for "$workspace", "$workspace/bazel-bin",
+ /// and "$workspace/bazel-genfiles", effectively overlaying source and
+ /// generated files.
+ List<Uri> multiRoots = [];
/// The declared variables for use by configurable imports and constant
/// evaluation.
@@ -91,12 +91,40 @@
/// The [FileSystem] which should be used by the front end to access files.
///
- /// TODO(paulberry): once an implementation of [FileSystem] has been created
- /// which uses the actual physical file system, make that the default.
- ///
/// All file system access performed by the front end goes through this
/// mechanism, with one exception: if no value is specified for
- /// [packagesFilePath], the packages file is located using the actual physical
+ /// [packagesFileUri], the packages file is located using the actual physical
/// file system. TODO(paulberry): fix this.
- FileSystem fileSystem;
+ FileSystem fileSystem = PhysicalFileSystem.instance;
+
+ /// Whether to generate code for the SDK when compiling a whole-program.
+ bool compileSdk = false;
+
+ /// Whether a modular build compiles only the files listed explicitly or if it
+ /// compiles dependencies as well.
+ ///
+ /// This option is intended only for modular APIs like `kernelForBuildUnit`.
+ /// These APIs by default ensure that builds are hermetic, where all files
+ /// that will be compiled are listed explicitly and all other dependencies
+ /// are covered by summary files.
+ ///
+ /// When this option is true, these APIs will treat any dependency that is
+ /// not described in a summary as if it was explictly listed as an input.
+ bool chaseDependencies = false;
+
+ /// Whether to intepret Dart sources in strong-mode.
+ bool strongMode = true;
+
+ // All options below are target-specific options.
+ //
+ // TODO(sigmund): revisit the right layout for these options. We might want to
+ // split them out into a separate bag of options or provide factories for
+ // common combinations of these options.
+
+ /// Patch files to apply on the core libraries for a specific target platform.
+ ///
+ /// Keys on this map are expected to be `dart:*` URIs. The values can be
+ /// either absolute or relative URIs. Absolute URIs are read directly, while
+ /// relative URIs are resolved from the [sdkRoot].
+ Map<Uri, List<Uri>> targetPatches = {};
}
diff --git a/pub/front_end/lib/dependency_grapher.dart b/pub/front_end/lib/dependency_grapher.dart
new file mode 100644
index 0000000..6120490
--- /dev/null
+++ b/pub/front_end/lib/dependency_grapher.dart
@@ -0,0 +1,54 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:front_end/compiler_options.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/dependency_grapher_impl.dart' as impl;
+
+/// Generates a representation of the dependency graph of a program.
+///
+/// Given the Uri of one or more files, this function follows `import`,
+/// `export`, and `part` declarations to discover a graph of all files involved
+/// in the program.
+Future<Graph> graphForProgram(List<Uri> sources, CompilerOptions options) {
+ var processedOptions = new ProcessedOptions(options);
+ return impl.graphForProgram(sources, processedOptions);
+}
+
+/// A representation of the dependency graph of a program.
+///
+/// Not intended to be extended, implemented, or mixed in by clients.
+class Graph {
+ /// A list of all library cycles in the program, in topologically sorted order
+ /// (each cycle only depends on libraries in the cycles that precede it).
+ final topologicallySortedCycles = <LibraryCycleNode>[];
+}
+
+/// A representation of a single library cycle in the dependency graph of a
+/// program.
+///
+/// Not intended to be extended, implemented, or mixed in by clients.
+class LibraryCycleNode {
+ /// A map of all the libraries in the cycle, keyed by the URI of their
+ /// defining compilation unit.
+ final libraries = <Uri, LibraryNode>{};
+}
+
+/// A representation of a single library in the dependency graph of a program.
+///
+/// Not intended to be extended, implemented, or mixed in by clients.
+class LibraryNode {
+ /// The URI of this library's defining compilation unit.
+ final Uri uri;
+
+ /// A list of the URIs of all of this library's "part" files.
+ final parts = <Uri>[];
+
+ /// A list of all the other libraries this library directly depends on.
+ final dependencies = <LibraryNode>[];
+
+ LibraryNode(this.uri);
+}
diff --git a/pub/front_end/lib/file_system.dart b/pub/front_end/lib/file_system.dart
index cadf82f..6e57c26 100644
--- a/pub/front_end/lib/file_system.dart
+++ b/pub/front_end/lib/file_system.dart
@@ -18,22 +18,17 @@
/// Not intended to be implemented or extended by clients.
abstract class FileSystem {
/// Returns a path context suitable for use with this [FileSystem].
+ ///
+ /// TODO(paulberry): try to eliminate all usages of this. Since the
+ /// FileSystem API now uses URIs rather than paths, it should not be needed.
path.Context get context;
- /// Returns a [FileSystemEntity] corresponding to the given [path].
- ///
- /// Uses of `..` and `.` in path are normalized before returning (so, for
- /// example, `entityForPath('./foo')` and `entityForPath('foo')` are
- /// equivalent). Relative paths are also converted to absolute paths.
- ///
- /// Does not check whether a file or folder exists at the given location.
- FileSystemEntity entityForPath(String path);
-
/// Returns a [FileSystemEntity] corresponding to the given [uri].
///
/// Uses of `..` and `.` in the URI are normalized before returning.
///
- /// If [uri] is not an absolute `file:` URI, an [Error] will be thrown.
+ /// If the URI scheme is not supported by this file system, an [Error] will be
+ /// thrown.
///
/// Does not check whether a file or folder exists at the given location.
FileSystemEntity entityForUri(Uri uri);
@@ -46,14 +41,12 @@
///
/// Not intended to be implemented or extended by clients.
abstract class FileSystemEntity {
- /// Returns the absolute normalized path represented by this file system
+ /// Returns the absolute normalized URI represented by this file system
/// entity.
///
- /// Note: if the [FileSystemEntity] was created using
- /// [FileSystem.entityForPath], this is not necessarily the same as the path
- /// that was used to create the object, since the path might have been
- /// normalized.
- String get path;
+ /// Note: this is not necessarily the same as the URI that was passed to
+ /// [FileSystem.entityForUri], since the URI might have been normalized.
+ Uri get uri;
/// Attempts to access this file system entity as a file and read its contents
/// as raw bytes.
diff --git a/pub/front_end/lib/incremental_kernel_generator.dart b/pub/front_end/lib/incremental_kernel_generator.dart
new file mode 100644
index 0000000..3b3b429
--- /dev/null
+++ b/pub/front_end/lib/incremental_kernel_generator.dart
@@ -0,0 +1,102 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/incremental_kernel_generator_impl.dart';
+import 'package:kernel/kernel.dart';
+
+import 'compiler_options.dart';
+
+/// Represents the difference between "old" and "new" states of a program.
+///
+/// Not intended to be implemented or extended by clients.
+class DeltaProgram {
+ /// The new state of the program.
+ ///
+ /// Libraries whose kernel representation is known to be unchanged since the
+ /// last [DeltaProgram] are not included.
+ final Map<Uri, Program> newState;
+
+ DeltaProgram(this.newState);
+
+ /// TODO(paulberry): add information about libraries that were removed.
+}
+
+/// Interface for generating an initial kernel representation of a program and
+/// keeping it up to date as incremental changes are made.
+///
+/// This class maintains an internal "previous program state"; each
+/// time [computeDelta] is called, it updates the previous program state and
+/// produces a representation of what has changed. When there are few changes,
+/// a call to [computeDelta] should be much faster than compiling the whole
+/// program from scratch.
+///
+/// This class also maintains a set of "valid sources", which is a (possibly
+/// empty) subset of the sources constituting the previous program state. Files
+/// in this set are assumed to be unchanged since the last call to
+/// [computeDelta].
+///
+/// Behavior is undefined if the client does not obey the following concurrency
+/// restrictions:
+/// - no two invocations of [computeDelta] may be outstanding at any given time.
+/// - neither [invalidate] nor [invalidateAll] may be called while an invocation
+/// of [computeDelta] is outstanding.
+///
+/// Not intended to be implemented or extended by clients.
+abstract class IncrementalKernelGenerator {
+ /// Creates an [IncrementalKernelGenerator] which is prepared to generate
+ /// kernel representations of the program whose main library is in the given
+ /// [source].
+ ///
+ /// No file system access is performed by this constructor; the initial
+ /// "previous program state" is an empty program containing no code, and the
+ /// initial set of valid sources is empty. To obtain a kernel representation
+ /// of the program, call [computeDelta].
+ factory IncrementalKernelGenerator(Uri source, CompilerOptions options) =>
+ new IncrementalKernelGeneratorImpl(source, new ProcessedOptions(options));
+
+ /// Generates a kernel representation of the changes to the program, assuming
+ /// that all valid sources are unchanged since the last call to
+ /// [computeDelta].
+ ///
+ /// Source files in the set of valid sources are guaranteed not to be re-read
+ /// from disk; they are assumed to be unchanged regardless of the state of the
+ /// filesystem.
+ ///
+ /// If [watch] is not `null`, then when a source file is first used
+ /// by [computeDelta], [watch] is called with the Uri of that source
+ /// file and `used` == `true` indicating that the source file is being
+ /// used when compiling the program. The content of the file is not read
+ /// until the Future returned by [watch] completes. If during a subsequent
+ /// call to [computeDelta], a source file that was being used is no longer
+ /// used, then [watch] is called with the Uri of that source file and
+ /// `used` == `false` indicating that the source file is no longer needed.
+ /// Multiple invocations of [watch] may be running concurrently.
+ ///
+ /// If the future completes successfully, the previous file state is updated
+ /// and the set of valid sources is set to the set of all sources in the
+ /// program.
+ ///
+ /// If the future completes with an error (due to errors in the compiled
+ /// source code), the caller may consider the previous file state and the set
+ /// of valid sources to be unchanged; this means that once the user fixes the
+ /// errors, it is safe to call [computeDelta] again.
+ Future<DeltaProgram> computeDelta({Future<Null> watch(Uri uri, bool used)});
+
+ /// Remove any source file(s) associated with the given file path from the set
+ /// of valid sources. This guarantees that those files will be re-read on the
+ /// next call to [computeDelta]).
+ void invalidate(String path);
+
+ /// Remove all source files from the set of valid sources. This guarantees
+ /// that all files will be re-read on the next call to [computeDelta].
+ ///
+ /// Note that this does not erase the previous program state; the next time
+ /// [computeDelta] is called, if parts of the program are discovered to be
+ /// unchanged, parts of the previous program state will still be re-used to
+ /// speed up compilation.
+ void invalidateAll();
+}
diff --git a/pub/front_end/lib/incremental_resolved_ast_generator.dart b/pub/front_end/lib/incremental_resolved_ast_generator.dart
new file mode 100644
index 0000000..daeb8c1
--- /dev/null
+++ b/pub/front_end/lib/incremental_resolved_ast_generator.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/incremental_resolved_ast_generator_impl.dart';
+
+import 'compiler_options.dart';
+
+/// Represents the difference between "old" and "new" states of a program.
+///
+/// Not intended to be implemented or extended by clients.
+class DeltaLibraries {
+ /// The new state of the program, as a two-layer map.
+ ///
+ /// The outer map key is the library URI. The inner map key is the
+ /// compilation unit (part) URI. The map values are the resolved compilation
+ /// units.
+ ///
+ /// Libraries whose resolved AST is known to be unchanged since the last
+ /// [DeltaLibraries] are not included.
+ final Map<Uri, Map<Uri, CompilationUnit>> newState;
+
+ DeltaLibraries(this.newState);
+
+ /// TODO(paulberry): add information about libraries that were removed.
+}
+
+/// Interface for generating an initial resolved representation of a program and
+/// keeping it up to date as incremental changes are made.
+///
+/// This class maintains an internal "previous program state"; each
+/// time [computeDelta] is called, it updates the previous program state and
+/// produces a representation of what has changed. When there are few changes,
+/// a call to [computeDelta] should be much faster than compiling the whole
+/// program from scratch.
+///
+/// This class also maintains a set of "valid sources", which is a (possibly
+/// empty) subset of the sources constituting the previous program state. Files
+/// in this set are assumed to be unchanged since the last call to
+/// [computeDelta].
+///
+/// Behavior is undefined if the client does not obey the following concurrency
+/// restrictions:
+/// - no two invocations of [computeDelta] may be outstanding at any given time.
+/// - neither [invalidate] nor [invalidateAll] may be called while an invocation
+/// of [computeDelta] is outstanding.
+///
+/// Not intended to be implemented or extended by clients.
+abstract class IncrementalResolvedAstGenerator {
+ /// Creates an [IncrementalResolvedAstGenerator] which is prepared to generate
+ /// resolved ASTs for the program whose main library is in the given
+ /// [source].
+ ///
+ /// No file system access is performed by this constructor; the initial
+ /// "previous program state" is an empty program containing no code, and the
+ /// initial set of valid sources is empty. To obtain a resolved AST
+ /// representation of the program, call [computeDelta].
+ factory IncrementalResolvedAstGenerator(
+ Uri source, CompilerOptions options) =>
+ new IncrementalResolvedAstGeneratorImpl(
+ source, new ProcessedOptions(options));
+
+ /// Generates a resolved AST representation of the changes to the program,
+ /// assuming that all valid sources are unchanged since the last call to
+ /// [computeDelta].
+ ///
+ /// Source files in the set of valid sources are guaranteed not to be re-read
+ /// from disk; they are assumed to be unchanged regardless of the state of the
+ /// filesystem.
+ ///
+ /// If the future completes successfully, the previous file state is updated
+ /// and the set of valid sources is set to the set of all sources in the
+ /// program.
+ ///
+ /// If the future completes with an error (due to errors in the compiled
+ /// source code), the caller may consider the previous file state and the set
+ /// of valid sources to be unchanged; this means that once the user fixes the
+ /// errors, it is safe to call [computeDelta] again.
+ Future<DeltaLibraries> computeDelta();
+
+ /// Remove any source file(s) associated with the given file path from the set
+ /// of valid sources. This guarantees that those files will be re-read on the
+ /// next call to [computeDelta]).
+ void invalidate(String path);
+
+ /// Remove all source files from the set of valid sources. This guarantees
+ /// that all files will be re-read on the next call to [computeDelta].
+ ///
+ /// Note that this does not erase the previous program state; the next time
+ /// [computeDelta] is called, if parts of the program are discovered to be
+ /// unchanged, parts of the previous program state will still be re-used to
+ /// speed up compilation.
+ void invalidateAll();
+}
diff --git a/pub/front_end/lib/kernel_generator.dart b/pub/front_end/lib/kernel_generator.dart
index 3b35086..0ae2fb3 100644
--- a/pub/front_end/lib/kernel_generator.dart
+++ b/pub/front_end/lib/kernel_generator.dart
@@ -5,9 +5,18 @@
/// Defines the front-end API for converting source code to Dart Kernel objects.
library front_end.kernel_generator;
-import 'dart:async';
+import 'compilation_error.dart';
import 'compiler_options.dart';
-import 'package:kernel/kernel.dart' as kernel;
+import 'dart:async';
+
+import 'package:analyzer/src/generated/source.dart' show SourceKind;
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
+import 'package:analyzer/src/summary/package_bundle_reader.dart'
+ show InSummarySource;
+// TODO(sigmund): move loader logic under front_end/lib/src/kernel/
+import 'package:kernel/analyzer/loader.dart';
+import 'package:kernel/kernel.dart';
+import 'package:source_span/source_span.dart' show SourceSpan;
/// Generates a kernel representation of the program whose main library is in
/// the given [source].
@@ -18,41 +27,172 @@
/// follows `import`, `export`, and `part` declarations to discover the whole
/// program, and converts the result to Dart Kernel format.
///
-/// If summaries are provided in [options], they may be used to speed up
-/// analysis, but they will not take the place of Dart source code (since the
-/// Dart source code is still needed to access the contents of method bodies).
+/// If `compileSdk` in [options] is true, the generated program will include
+/// code for the SDK.
///
-/// TODO(paulberry): will the VM have a pickled version of the SDK inside it? If
-/// so, then maybe this method should not convert SDK libraries to kernel.
-Future<kernel.Program> kernelForProgram(Uri source, CompilerOptions options) =>
- throw new UnimplementedError();
+/// If summaries are provided in [options], they may be used to speed up
+/// analysis. If in addition `compileSdk` is false, this will speed up
+/// compilation, as no source of the sdk will be generated. Note however, that
+/// summaries for application code can also speed up analysis, but they will not
+/// take the place of Dart source code (since the Dart source code is still
+/// needed to access the contents of method bodies).
+Future<Program> kernelForProgram(Uri source, CompilerOptions options) async {
+ var loader = await _createLoader(options, entry: source);
+ // TODO(sigmund): delete this. At this time we have no need to explicitly list
+ // VM libraries, since they are normally found by chasing dependencies.
+ // `dart:_builtin` is an exception because it is used by the kernel
+ // transformers to inform the VM about where the main entrypoint is. This is
+ // expected to change, and we should be able to remove these lines at that
+ // point. We check for the presense of `dart:developer` in the targetPatches
+ // to ensure we only load this library while running on the VM.
+ if (options.compileSdk &&
+ options.targetPatches.containsKey(Uri.parse('dart:developer'))) {
+ loader.loadLibrary(Uri.parse('dart:_builtin'));
+ }
+ // TODO(sigmund): merge what we have in loadEverything and the logic below in
+ // kernelForBuildUnit so there is a single place where we crawl for
+ // dependencies.
+ Program program = loader.loadProgram(source, compileSdk: options.compileSdk);
+ _reportErrors(loader.errors, options.onError);
+ return program;
+}
-/// Generates a kernel representation of the build unit whose source files are
-/// in [sources].
+/// Generates a kernel representation for a build unit.
///
/// Intended for modular compilation.
///
-/// [sources] should be the complete set of source files for a build unit
-/// (including both library and part files). All of the library files are
-/// transformed into Dart Kernel Library objects.
+/// The build unit by default contains only the source files in [sources]
+/// (including library and part files), but if
+/// [CompilerOptions.chaseDependencies] is true, it may include some additional
+/// source files. All of the library files are transformed into Dart Kernel
+/// Library objects.
///
-/// The compilation process is hermetic, meaning that the only files which will
-/// be read are those listed in [sources], [CompilerOptions.inputSummaries], and
-/// [CompilerOptions.sdkSummary]. If a source file attempts to refer to a file
-/// which is not obtainable from these paths, that will result in an error, even
-/// if the file exists on the filesystem.
+/// By default, the compilation process is hermetic, meaning that the only files
+/// which will be read are those listed in [sources],
+/// [CompilerOptions.inputSummaries], and [CompilerOptions.sdkSummary]. If a
+/// source file attempts to refer to a file which is not obtainable from these
+/// URIs, that will result in an error, even if the file exists on the
+/// filesystem.
+///
+/// When [CompilerOptions.chaseDependencies] is true, this default behavior
+/// changes, and any dependency of [sources] that is not listed in
+/// [CompilerOptions.inputSummaries] and [CompilerOptions.sdkSummary] is treated
+/// as an additional source file for the build unit.
///
/// Any `part` declarations found in [sources] must refer to part files which
-/// are also listed in [sources], otherwise an error results. (It is not
-/// permitted to refer to a part file declared in another build unit).
+/// are also listed in the build unit sources, otherwise an error results. (It
+/// is not permitted to refer to a part file declared in another build unit).
///
-/// The return value is a [kernel.Program] object with no main method set.
+/// The return value is a [Program] object with no main method set.
/// TODO(paulberry): would it be better to define a data type in kernel to
/// represent a bundle of all the libraries in a given build unit?
///
/// TODO(paulberry): does additional information need to be output to allow the
/// caller to match up referenced elements to the summary files they were
/// obtained from?
-Future<kernel.Program> kernelForBuildUnit(
- List<Uri> sources, CompilerOptions options) =>
- throw new UnimplementedError();
+Future<Program> kernelForBuildUnit(
+ List<Uri> sources, CompilerOptions options) async {
+ var repository = new Repository();
+ var loader = await _createLoader(options, repository: repository);
+ var context = loader.context;
+
+ // Process every library in the build unit.
+ for (var uri in sources) {
+ var source = context.sourceFactory.forUri2(uri);
+ // We ignore part files, those are handled by their enclosing library.
+ if (context.computeKindOf(source) == SourceKind.PART) {
+ // TODO(sigmund): record it and ensure that this part is within a provided
+ // library.
+ continue;
+ }
+ loader.loadLibrary(uri);
+ }
+
+ // Check whether all dependencies were included in [sources].
+ // TODO(sigmund): we should look for dependencies using import, export, and
+ // part directives intead of relying on the dartk-loader. In particular, if a
+ // library is imported but not used, the logic below will not detect it.
+ for (int i = 0; i < repository.libraries.length; ++i) {
+ // Note: we don't use a for-in loop because repository.libraries grows as
+ // the loader processes libraries.
+ var lib = repository.libraries[i];
+ var source = context.sourceFactory.forUri2(lib.importUri);
+ if (source is InSummarySource) continue;
+ if (options.chaseDependencies) {
+ loader.ensureLibraryIsLoaded(lib);
+ } else if (lib.isExternal) {
+ // Default behavior: the build should be hermetic and all dependencies
+ // should be listed.
+ options.onError(new _DartkError('hermetic build error: '
+ 'no source or summary was given for ${lib.importUri}'));
+ }
+ }
+
+ Program program = new Program(repository.libraries);
+ _reportErrors(loader.errors, options.onError);
+ return program;
+}
+
+/// Create a [DartLoader] using the provided [options].
+///
+/// If [options] contain no configuration to resolve `.packages`, the [entry]
+/// file will be used to search for a `.packages` file.
+Future<DartLoader> _createLoader(CompilerOptions options,
+ {Repository repository, Uri entry}) async {
+ var kernelOptions = _convertOptions(options);
+ var packages = await createPackages(
+ _uriToPath(options.packagesFileUri, options),
+ discoveryPath: entry?.path);
+ var loader = new DartLoader(
+ repository ?? new Repository(), kernelOptions, packages);
+ var patchPaths = <String, List<String>>{};
+
+ // TODO(sigmund,paulberry): use ProcessedOptions so that we can resolve the
+ // URIs correctly even if sdkRoot is inferred and not specified explicitly.
+ String resolve(Uri patch) =>
+ options.fileSystem.context.fromUri(options.sdkRoot.resolveUri(patch));
+
+ options.targetPatches.forEach((uri, patches) {
+ patchPaths['$uri'] = patches.map(resolve).toList();
+ });
+ AnalysisOptionsImpl analysisOptions = loader.context.analysisOptions;
+ analysisOptions.patchPaths = patchPaths;
+ return loader;
+}
+
+DartOptions _convertOptions(CompilerOptions options) {
+ return new DartOptions(
+ strongMode: options.strongMode,
+ sdk: _uriToPath(options.sdkRoot, options),
+ // TODO(sigmund): make it possible to use summaries and still compile the
+ // sdk sources.
+ sdkSummary:
+ options.compileSdk ? null : _uriToPath(options.sdkSummary, options),
+ packagePath: _uriToPath(options.packagesFileUri, options),
+ customUriMappings: options.uriOverride,
+ declaredVariables: options.declaredVariables);
+}
+
+void _reportErrors(List errors, ErrorHandler onError) {
+ if (onError == null) return;
+ for (var error in errors) {
+ onError(new _DartkError(error));
+ }
+}
+
+String _uriToPath(Uri uri, CompilerOptions options) {
+ if (uri == null) return null;
+ if (uri.scheme != 'file') {
+ throw new StateError('Only file URIs are supported');
+ }
+ return options.fileSystem.context.fromUri(uri);
+}
+
+// TODO(sigmund): delete this class. Dartk should not format errors itself, we
+// should just pass them along.
+class _DartkError implements CompilationError {
+ String get correction => null;
+ SourceSpan get span => null;
+ final String message;
+ _DartkError(this.message);
+}
diff --git a/pub/front_end/lib/memory_file_system.dart b/pub/front_end/lib/memory_file_system.dart
index 267cbe3..e7382ad 100644
--- a/pub/front_end/lib/memory_file_system.dart
+++ b/pub/front_end/lib/memory_file_system.dart
@@ -20,26 +20,29 @@
@override
final p.Context context;
- final Map<String, Uint8List> _files = {};
+ final Map<Uri, Uint8List> _files = {};
/// The "current directory" in the in-memory virtual file system.
///
- /// This is used to convert relative paths to absolute paths.
- String currentDirectory;
+ /// This is used to convert relative URIs to absolute URIs.
+ ///
+ /// Always ends in a trailing '/'.
+ Uri currentDirectory;
- MemoryFileSystem(this.context, this.currentDirectory);
-
- @override
- MemoryFileSystemEntity entityForPath(String path) =>
- new MemoryFileSystemEntity._(
- this, context.normalize(context.join(currentDirectory, path)));
+ MemoryFileSystem(this.context, Uri currentDirectory)
+ : currentDirectory = _addTrailingSlash(currentDirectory);
@override
MemoryFileSystemEntity entityForUri(Uri uri) {
- if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
- // Note: we don't have to verify that the URI's path is absolute, because
- // URIs with non-empty schemes always have absolute paths.
- return entityForPath(context.fromUri(uri));
+ return new MemoryFileSystemEntity._(
+ this, currentDirectory.resolveUri(uri).normalizePath());
+ }
+
+ static Uri _addTrailingSlash(Uri uri) {
+ if (!uri.path.endsWith('/')) {
+ uri = uri.replace(path: uri.path + '/');
+ }
+ return uri;
}
}
@@ -49,22 +52,22 @@
final MemoryFileSystem _fileSystem;
@override
- final String path;
+ final Uri uri;
- MemoryFileSystemEntity._(this._fileSystem, this.path);
+ MemoryFileSystemEntity._(this._fileSystem, this.uri);
@override
- int get hashCode => path.hashCode;
+ int get hashCode => uri.hashCode;
@override
bool operator ==(Object other) =>
other is MemoryFileSystemEntity &&
- other.path == path &&
+ other.uri == uri &&
identical(other._fileSystem, _fileSystem);
@override
Future<List<int>> readAsBytes() async {
- List<int> contents = _fileSystem._files[path];
+ List<int> contents = _fileSystem._files[uri];
if (contents != null) {
return contents.toList();
}
@@ -82,7 +85,7 @@
/// If no file exists, one is created. If a file exists already, it is
/// overwritten.
void writeAsBytesSync(List<int> bytes) {
- _fileSystem._files[path] = new Uint8List.fromList(bytes);
+ _fileSystem._files[uri] = new Uint8List.fromList(bytes);
}
/// Writes the given string to this file system entity.
@@ -95,6 +98,6 @@
// Note: the return type of UTF8.encode is List<int>, but in practice it
// always returns Uint8List. We rely on that for efficiency, so that we
// don't have to make an extra copy.
- _fileSystem._files[path] = UTF8.encode(s) as Uint8List;
+ _fileSystem._files[uri] = UTF8.encode(s) as Uint8List;
}
}
diff --git a/pub/front_end/lib/physical_file_system.dart b/pub/front_end/lib/physical_file_system.dart
index 0ed5d8b..f90abc9 100644
--- a/pub/front_end/lib/physical_file_system.dart
+++ b/pub/front_end/lib/physical_file_system.dart
@@ -24,15 +24,11 @@
p.Context get context => p.context;
@override
- FileSystemEntity entityForPath(String path) =>
- new _PhysicalFileSystemEntity(context.normalize(context.absolute(path)));
-
- @override
FileSystemEntity entityForUri(Uri uri) {
- if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
- // Note: we don't have to verify that the URI's path is absolute, because
- // URIs with non-empty schemes always have absolute paths.
- return entityForPath(context.fromUri(uri));
+ if (uri.scheme != 'file' && uri.scheme != '') {
+ throw new ArgumentError('File URI expected');
+ }
+ return new _PhysicalFileSystemEntity(Uri.base.resolveUri(uri));
}
}
@@ -40,20 +36,20 @@
/// [PhysicalFileSystem].
class _PhysicalFileSystemEntity implements FileSystemEntity {
@override
- final String path;
+ final Uri uri;
- _PhysicalFileSystemEntity(this.path);
+ _PhysicalFileSystemEntity(this.uri);
@override
- int get hashCode => path.hashCode;
+ int get hashCode => uri.hashCode;
@override
bool operator ==(Object other) =>
- other is _PhysicalFileSystemEntity && other.path == path;
+ other is _PhysicalFileSystemEntity && other.uri == uri;
@override
- Future<List<int>> readAsBytes() => new io.File(path).readAsBytes();
+ Future<List<int>> readAsBytes() => new io.File.fromUri(uri).readAsBytes();
@override
- Future<String> readAsString() => new io.File(path).readAsString();
+ Future<String> readAsString() => new io.File.fromUri(uri).readAsString();
}
diff --git a/pub/front_end/lib/src/async_dependency_walker.dart b/pub/front_end/lib/src/async_dependency_walker.dart
new file mode 100644
index 0000000..b0c6535
--- /dev/null
+++ b/pub/front_end/lib/src/async_dependency_walker.dart
@@ -0,0 +1,172 @@
+// 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.
+
+import 'dart:async';
+
+/**
+ * An instance of [AsyncDependencyWalker] contains the core algorithms for
+ * walking a dependency graph and evaluating nodes in a safe order.
+ *
+ * Computation of dependencies and evaluation of nodes may be asynchronous.
+ */
+abstract class AsyncDependencyWalker<NodeType extends Node<NodeType>> {
+ /**
+ * Called by [walk] to evaluate a single non-cyclical node, after
+ * all that node's dependencies have been evaluated.
+ */
+ Future<Null> evaluate(NodeType v);
+
+ /**
+ * Called by [walk] to evaluate a strongly connected component
+ * containing one or more nodes. All dependencies of the strongly
+ * connected component have been evaluated.
+ */
+ Future<Null> evaluateScc(List<NodeType> scc);
+
+ /**
+ * Walk the dependency graph starting at [startingPoint], finding
+ * strongly connected components and evaluating them in a safe order
+ * by calling [evaluate] and [evaluateScc].
+ *
+ * This is an implementation of Tarjan's strongly connected
+ * components algorithm
+ * (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm).
+ *
+ * TODO(paulberry): Consider switching to an algorithm that allows
+ * dependencies to be computed in parallel, and nodes to be evaluated in
+ * parallel.
+ */
+ Future<Null> walk(NodeType startingPoint) async {
+ // TODO(paulberry): consider rewriting in a non-recursive way so
+ // that long dependency chains don't cause stack overflow.
+
+ // TODO(paulberry): in the event that an exception occurs during
+ // the walk, restore the state of the [Node] data structures so
+ // that further evaluation will be safe.
+
+ // The index which will be assigned to the next node that is
+ // freshly visited.
+ int index = 1;
+
+ // Stack of nodes which have been seen so far and whose strongly
+ // connected component is still being determined. Nodes are only
+ // popped off the stack when they are evaluated, so sometimes the
+ // stack contains nodes that were visited after the current node.
+ List<NodeType> stack = <NodeType>[];
+
+ Future strongConnect(NodeType node) async {
+ bool hasTrivialCycle = false;
+
+ // Assign the current node an index and add it to the stack. We
+ // haven't seen any of its dependencies yet, so set its lowLink
+ // to its index, indicating that so far it is the only node in
+ // its strongly connected component.
+ node._index = node._lowLink = index++;
+ stack.add(node);
+
+ // Consider the node's dependencies one at a time.
+ var dependencies =
+ node._dependencies ??= await node.computeDependencies();
+ for (NodeType dependency in dependencies) {
+ // If the dependency has already been evaluated, it can't be
+ // part of this node's strongly connected component, so we can
+ // skip it.
+ if (dependency._isEvaluated) {
+ continue;
+ }
+ if (identical(node, dependency)) {
+ // If a node includes itself as a dependency, there is no need to
+ // explore the dependency further.
+ hasTrivialCycle = true;
+ } else if (dependency._index == 0) {
+ // The dependency hasn't been seen yet, so recurse on it.
+ await strongConnect(dependency);
+ // If the dependency's lowLink refers to a node that was
+ // visited before the current node, that means that the
+ // current node, the dependency, and the node referred to by
+ // the dependency's lowLink are all part of the same
+ // strongly connected component, so we need to update the
+ // current node's lowLink accordingly.
+ if (dependency._lowLink < node._lowLink) {
+ node._lowLink = dependency._lowLink;
+ }
+ } else {
+ // The dependency has already been seen, so it is part of
+ // the current node's strongly connected component. If it
+ // was visited earlier than the current node's lowLink, then
+ // it is a new addition to the current node's strongly
+ // connected component, so we need to update the current
+ // node's lowLink accordingly.
+ if (dependency._index < node._lowLink) {
+ node._lowLink = dependency._index;
+ }
+ }
+ }
+
+ // If the current node's lowLink is the same as its index, then
+ // we have finished visiting a strongly connected component, so
+ // pop the stack and evaluate it before moving on.
+ if (node._lowLink == node._index) {
+ // The strongly connected component has only one node. If there is a
+ // cycle, it's a trivial one.
+ if (identical(stack.last, node)) {
+ stack.removeLast();
+ node._isEvaluated = true;
+ if (hasTrivialCycle) {
+ await evaluateScc(<NodeType>[node]);
+ } else {
+ await evaluate(node);
+ }
+ } else {
+ // There are multiple nodes in the strongly connected
+ // component.
+ List<NodeType> scc = <NodeType>[];
+ while (true) {
+ NodeType otherNode = stack.removeLast();
+ scc.add(otherNode);
+ otherNode._isEvaluated = true;
+ if (identical(otherNode, node)) {
+ break;
+ }
+ }
+ await evaluateScc(scc);
+ }
+ }
+ }
+
+ // Kick off the algorithm starting with the starting point.
+ await strongConnect(startingPoint);
+ }
+}
+
+/**
+ * Instances of [Node] represent nodes in a dependency graph. The
+ * type parameter, [NodeType], is the derived type (this affords some
+ * extra type safety by making it difficult to accidentally construct
+ * bridges between unrelated dependency graphs).
+ */
+abstract class Node<NodeType> {
+ /**
+ * Index used by Tarjan's strongly connected components algorithm.
+ * Zero means the node has not been visited yet; a nonzero value
+ * counts the order in which the node was visited.
+ */
+ int _index = 0;
+
+ /**
+ * Low link used by Tarjan's strongly connected components
+ * algorithm. This represents the smallest [_index] of all the nodes
+ * in the strongly connected component to which this node belongs.
+ */
+ int _lowLink = 0;
+
+ List<NodeType> _dependencies;
+
+ bool _isEvaluated = false;
+
+ /**
+ * Compute the dependencies of this node.
+ */
+ Future<List<NodeType>> computeDependencies();
+}
diff --git a/pub/front_end/lib/src/base/analysis_target.dart b/pub/front_end/lib/src/base/analysis_target.dart
new file mode 100644
index 0000000..ab50efd
--- /dev/null
+++ b/pub/front_end/lib/src/base/analysis_target.dart
@@ -0,0 +1,26 @@
+// 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.
+
+import 'package:front_end/src/base/source.dart';
+
+/**
+ * An object with which an analysis result can be associated.
+ *
+ * Clients may implement this class when creating new kinds of targets.
+ * Instances of this type are used in hashed data structures, so subtypes are
+ * required to correctly implement [==] and [hashCode].
+ */
+abstract class AnalysisTarget {
+ /**
+ * If this target is associated with a library, return the source of the
+ * library's defining compilation unit; otherwise return `null`.
+ */
+ Source get librarySource;
+
+ /**
+ * Return the source associated with this target, or `null` if this target is
+ * not associated with a source.
+ */
+ Source get source;
+}
diff --git a/pub/front_end/lib/src/base/errors.dart b/pub/front_end/lib/src/base/errors.dart
new file mode 100644
index 0000000..d39953f
--- /dev/null
+++ b/pub/front_end/lib/src/base/errors.dart
@@ -0,0 +1,282 @@
+// 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.
+
+/**
+ * An error code associated with an [AnalysisError].
+ *
+ * Generally, we want to provide messages that consist of three sentences. From
+ * the user's perspective these sentences should explain:
+ *
+ * 1. what is wrong,
+ * 2. why is it wrong, and
+ * 3. how do I fix it.
+ *
+ * However, we combine the first two in the [message] and the last in the
+ * [correction].
+ *
+ * When composing messages (including correction messages) keep the following
+ * guidelines in mind.
+ *
+ * 1. The message should be a complete sentence starting with an uppercase
+ * letter, and ending with a period.
+ *
+ * 2. Reserved words and embedded identifiers should be in single quotes, so
+ * prefer double quotes for the complete message. For example,
+ * ```
+ * "The class '{0}' can't use 'super'."
+ * ```
+ * Notice that the word 'class' in the preceding message is not quoted as it
+ * refers to the concept 'class', not the reserved word. On the other hand,
+ * 'super' refers to the reserved word. Do not quote 'null' and numeric literals.
+ *
+ * 3. Do not try to compose messages, as it can make translating them hard.
+ *
+ * 4. Try to keep the error messages short, but informative.
+ *
+ * 5. Use simple words and terminology, assume the reader of the message doesn't
+ * have an advanced degree in math, and that English is not the reader's native
+ * language. Do not assume any formal computer science training. For example, do
+ * not use Latin abbreviations (prefer "that is" over "i.e.", and "for example"
+ * over "e.g."). Also avoid phrases such as "if and only if" and "iff"; that
+ * level of precision is unnecessary.
+ *
+ * 6. Prefer contractions when they are in common use, for example, prefer
+ * "can't" over "cannot". Using "cannot", "must not", "shall not", etc. is
+ * off-putting to people new to programming.
+ *
+ * 7. Use common terminology, preferably from the Dart Language Specification.
+ * This increases the user's chance of finding a good explanation on the web.
+ *
+ * 8. Do not try to be cute or funny. It is extremely frustrating to work on a
+ * product that crashes with a "tongue-in-cheek" message, especially if you did
+ * not want to use this product to begin with.
+ *
+ * 9. Do not lie, that is, do not write error messages containing phrases like
+ * "can't happen". If the user ever saw this message, it would be a lie. Prefer
+ * messages like: "Internal error: This function should not be called when 'x'
+ * is null.".
+ *
+ * 10. Prefer to not use the imperative tone. That is, the message should not
+ * sound accusing or like it is ordering the user around. The computer should
+ * describe the problem, not criticize the user for violating the specification.
+ */
+abstract class ErrorCode {
+ /**
+ * The name of the error code.
+ */
+ final String name;
+
+ /**
+ * The template used to create the message to be displayed for this error. The
+ * message should indicate what is wrong and why it is wrong.
+ */
+ final String message;
+
+ /**
+ * The template used to create the correction to be displayed for this error,
+ * or `null` if there is no correction information for this error. The
+ * correction should indicate how the user can fix the error.
+ */
+ final String correction;
+
+ /**
+ * Initialize a newly created error code to have the given [name]. The message
+ * associated with the error will be created from the given [message]
+ * template. The correction associated with the error will be created from the
+ * given [correction] template.
+ */
+ const ErrorCode(this.name, this.message, [this.correction]);
+
+ /**
+ * The severity of the error.
+ */
+ ErrorSeverity get errorSeverity;
+
+ /**
+ * The type of the error.
+ */
+ ErrorType get type;
+
+ /**
+ * The unique name of this error code.
+ */
+ String get uniqueName => "$runtimeType.$name";
+
+ @override
+ String toString() => uniqueName;
+}
+
+/**
+ * The severity of an [ErrorCode].
+ */
+class ErrorSeverity implements Comparable<ErrorSeverity> {
+ /**
+ * The severity representing a non-error. This is never used for any error
+ * code, but is useful for clients.
+ */
+ static const ErrorSeverity NONE = const ErrorSeverity('NONE', 0, " ", "none");
+
+ /**
+ * The severity representing an informational level analysis issue.
+ */
+ static const ErrorSeverity INFO = const ErrorSeverity('INFO', 1, "I", "info");
+
+ /**
+ * The severity representing a warning. Warnings can become errors if the `-Werror` command
+ * line flag is specified.
+ */
+ static const ErrorSeverity WARNING =
+ const ErrorSeverity('WARNING', 2, "W", "warning");
+
+ /**
+ * The severity representing an error.
+ */
+ static const ErrorSeverity ERROR =
+ const ErrorSeverity('ERROR', 3, "E", "error");
+
+ static const List<ErrorSeverity> values = const [NONE, INFO, WARNING, ERROR];
+
+ /**
+ * The name of this error code.
+ */
+ final String name;
+
+ /**
+ * The ordinal value of the error code.
+ */
+ final int ordinal;
+
+ /**
+ * The name of the severity used when producing machine output.
+ */
+ final String machineCode;
+
+ /**
+ * The name of the severity used when producing readable output.
+ */
+ final String displayName;
+
+ /**
+ * Initialize a newly created severity with the given names.
+ */
+ const ErrorSeverity(
+ this.name, this.ordinal, this.machineCode, this.displayName);
+
+ @override
+ int get hashCode => ordinal;
+
+ @override
+ int compareTo(ErrorSeverity other) => ordinal - other.ordinal;
+
+ /**
+ * Return the severity constant that represents the greatest severity.
+ */
+ ErrorSeverity max(ErrorSeverity severity) =>
+ this.ordinal >= severity.ordinal ? this : severity;
+
+ @override
+ String toString() => name;
+}
+
+/**
+ * The type of an [ErrorCode].
+ */
+class ErrorType implements Comparable<ErrorType> {
+ /**
+ * Task (todo) comments in user code.
+ */
+ static const ErrorType TODO = const ErrorType('TODO', 0, ErrorSeverity.INFO);
+
+ /**
+ * Extra analysis run over the code to follow best practices, which are not in
+ * the Dart Language Specification.
+ */
+ static const ErrorType HINT = const ErrorType('HINT', 1, ErrorSeverity.INFO);
+
+ /**
+ * Compile-time errors are errors that preclude execution. A compile time
+ * error must be reported by a Dart compiler before the erroneous code is
+ * executed.
+ */
+ static const ErrorType COMPILE_TIME_ERROR =
+ const ErrorType('COMPILE_TIME_ERROR', 2, ErrorSeverity.ERROR);
+
+ /**
+ * Checked mode compile-time errors are errors that preclude execution in
+ * checked mode.
+ */
+ static const ErrorType CHECKED_MODE_COMPILE_TIME_ERROR = const ErrorType(
+ 'CHECKED_MODE_COMPILE_TIME_ERROR', 3, ErrorSeverity.ERROR);
+
+ /**
+ * Static warnings are those warnings reported by the static checker. They
+ * have no effect on execution. Static warnings must be provided by Dart
+ * compilers used during development.
+ */
+ static const ErrorType STATIC_WARNING =
+ const ErrorType('STATIC_WARNING', 4, ErrorSeverity.WARNING);
+
+ /**
+ * Many, but not all, static warnings relate to types, in which case they are
+ * known as static type warnings.
+ */
+ static const ErrorType STATIC_TYPE_WARNING =
+ const ErrorType('STATIC_TYPE_WARNING', 5, ErrorSeverity.WARNING);
+
+ /**
+ * Syntactic errors are errors produced as a result of input that does not
+ * conform to the grammar.
+ */
+ static const ErrorType SYNTACTIC_ERROR =
+ const ErrorType('SYNTACTIC_ERROR', 6, ErrorSeverity.ERROR);
+
+ /**
+ * Lint warnings describe style and best practice recommendations that can be
+ * used to formalize a project's style guidelines.
+ */
+ static const ErrorType LINT = const ErrorType('LINT', 7, ErrorSeverity.INFO);
+
+ static const List<ErrorType> values = const [
+ TODO,
+ HINT,
+ COMPILE_TIME_ERROR,
+ CHECKED_MODE_COMPILE_TIME_ERROR,
+ STATIC_WARNING,
+ STATIC_TYPE_WARNING,
+ SYNTACTIC_ERROR,
+ LINT
+ ];
+
+ /**
+ * The name of this error type.
+ */
+ final String name;
+
+ /**
+ * The ordinal value of the error type.
+ */
+ final int ordinal;
+
+ /**
+ * The severity of this type of error.
+ */
+ final ErrorSeverity severity;
+
+ /**
+ * Initialize a newly created error type to have the given [name] and
+ * [severity].
+ */
+ const ErrorType(this.name, this.ordinal, this.severity);
+
+ String get displayName => name.toLowerCase().replaceAll('_', ' ');
+
+ @override
+ int get hashCode => ordinal;
+
+ @override
+ int compareTo(ErrorType other) => ordinal - other.ordinal;
+
+ @override
+ String toString() => name;
+}
diff --git a/pub/front_end/lib/src/base/file_repository.dart b/pub/front_end/lib/src/base/file_repository.dart
new file mode 100644
index 0000000..d4d4f7c
--- /dev/null
+++ b/pub/front_end/lib/src/base/file_repository.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2017, 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.
+
+/// Data structure storing an association between URI and file contents.
+///
+/// Each URI is also associated with a unique arbitrary path ending in ".dart".
+/// This allows interfacing with analyzer code that expects to manipulate paths
+/// rather than URIs.
+class FileRepository {
+ /// Regular expression matching the arbitrary file paths generated by
+ /// [_pathForIndex].
+ static final _pathRegexp = new RegExp(r'^/[0-9]+\.dart$');
+
+ /// The URIs currently stored in the repository.
+ final _uris = <Uri>[];
+
+ /// Map from a URI to its index in [_uris].
+ final _uriToIndexMap = <Uri, int>{};
+
+ /// The file contents associated with the URIs in [_uris].
+ final _contents = <String>[];
+
+ /// Clear any contents stored in the file repository. The association between
+ /// URI and arbitrary path is preserved.
+ ///
+ /// Subsequent calls to [contentsForPath] will have undefined results until
+ /// new contents are stored using [store].
+ void clearContents() {
+ for (var i = 0; i < _contents.length; i++) {
+ _contents[i] = null;
+ }
+ }
+
+ /// Return the contents of the file whose arbitary path is [path].
+ ///
+ /// The path must have been returned by a previous call to [store] or
+ /// [pathForUri].
+ String contentsForPath(String path) {
+ var contents = _contents[_indexForPath(path)];
+ assert(contents != null);
+ return contents;
+ }
+
+ /// For testing purposes, return the contents of all files stored in the file
+ /// repository, as a map from path to contents string.
+ Map<String, String> getContentsForTesting() {
+ var result = <String, String>{};
+ for (var i = 0; i < _contents.length; i++) {
+ if (_contents[i] != null) result[_pathForIndex(i)] = _contents[i];
+ }
+ return result;
+ }
+
+ /// Return the arbitrary path associated with [uri].
+ ///
+ /// If [allocate] is `false` (the default), the uri must have previously been
+ /// allocated a corresponding path, e.g. via a call to [store]. If [allocate]
+ /// is `true`, then a new path will be allocated if necessary.
+ String pathForUri(Uri uri, {bool allocate: false}) {
+ return _pathForIndex(_indexForUri(uri, allocate));
+ }
+
+ /// Associate the given [uri] with file [contents].
+ ///
+ /// The arbitrary path associated with the file is returned.
+ String store(Uri uri, String contents) {
+ int index = _indexForUri(uri, true);
+ _contents[index] = contents;
+ return _pathForIndex(index);
+ }
+
+ /// Return the URI for the file whose arbitrary path is [path].
+ ///
+ /// The path must have been returned by a previous call to [store] or
+ /// [pathForUri].
+ Uri uriForPath(String path) => _uris[_indexForPath(path)];
+
+ /// Return the index into [_uris] and [_contents] matching the arbitrary path
+ /// [path].
+ int _indexForPath(String path) {
+ assert(_pathRegexp.hasMatch(path));
+ return int.parse(path.substring(1, path.length - 5));
+ }
+
+ int _indexForUri(Uri uri, bool allocate) {
+ int index = _uriToIndexMap[uri];
+ assert(allocate || index != null);
+ if (index == null) {
+ index = _uris.length;
+ _uris.add(uri);
+ _uriToIndexMap[uri] = index;
+ _contents.add(null);
+ }
+ return index;
+ }
+
+ /// Return the arbitrary path associated with the given index.
+ String _pathForIndex(int index) => '/$index.dart';
+}
diff --git a/pub/front_end/lib/src/base/jenkins_smi_hash.dart b/pub/front_end/lib/src/base/jenkins_smi_hash.dart
new file mode 100644
index 0000000..8901ba4
--- /dev/null
+++ b/pub/front_end/lib/src/base/jenkins_smi_hash.dart
@@ -0,0 +1,53 @@
+// 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.
+
+/// Jenkins hash function, optimized for small integers.
+///
+/// Static methods borrowed from sdk/lib/math/jenkins_smi_hash.dart. Non-static
+/// methods are an enhancement for the "front_end" package.
+///
+/// Where performance is critical, use [hash2], [hash3], or [hash4], or the
+/// pattern `finish(combine(combine(...combine(0, a), b)..., z))`, where a..z
+/// are hash codes to be combined.
+///
+/// For ease of use, you may also use this pattern:
+/// `(new JenkinsSmiHash()..add(a)..add(b)....add(z)).hashCode`, where a..z are
+/// the sub-objects whose hashes should be combined. This pattern performs the
+/// same operations as the performance critical variant, but allocates an extra
+/// object.
+class JenkinsSmiHash {
+ /// Accumulates the hash code [value] into the running hash [hash].
+ static int combine(int hash, int value) {
+ hash = 0x1fffffff & (hash + value);
+ hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+ return hash ^ (hash >> 6);
+ }
+
+ /// Finalizes a running hash produced by [combine].
+ static int finish(int hash) {
+ hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+ hash = hash ^ (hash >> 11);
+ return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+ }
+
+ /// Combines together two hash codes.
+ static int hash2(a, b) => finish(combine(combine(0, a), b));
+
+ /// Combines together three hash codes.
+ static int hash3(a, b, c) => finish(combine(combine(combine(0, a), b), c));
+
+ /// Combines together four hash codes.
+ static int hash4(a, b, c, d) =>
+ finish(combine(combine(combine(combine(0, a), b), c), d));
+
+ int _hash = 0;
+
+ /// Accumulates the object [o] into the hash.
+ void add(Object o) {
+ _hash = combine(_hash, o.hashCode);
+ }
+
+ /// Finalizes the hash and return the resulting hashcode.
+ int get hashCode => finish(_hash);
+}
diff --git a/pub/front_end/lib/src/base/library_info.dart b/pub/front_end/lib/src/base/library_info.dart
new file mode 100644
index 0000000..a66b6d5
--- /dev/null
+++ b/pub/front_end/lib/src/base/library_info.dart
@@ -0,0 +1,159 @@
+// Copyright (c) 2017, 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.
+
+/// A bit flag used by [LibraryInfo] indicating that a library is used by
+/// dart2js.
+///
+/// This declaration duplicates the declaration in the SDK's "libraries.dart".
+const int DART2JS_PLATFORM = 1;
+
+/// A bit flag used by [LibraryInfo] indicating that a library is used by the
+/// VM.
+///
+/// This declaration duplicates the declaration in the SDK's "libraries.dart".
+const int VM_PLATFORM = 2;
+
+/// Parse a category string in the SDK's "libraries.dart".
+///
+/// This declaration duplicates the declaration in the SDK's "libraries.dart".
+Category parseCategory(String name) {
+ switch (name) {
+ case 'Client':
+ return Category.client;
+ case 'Server':
+ return Category.server;
+ case 'Embedded':
+ return Category.embedded;
+ }
+ return null;
+}
+
+/// The contexts that a library can be used from.
+///
+/// This declaration duplicates the declaration in the SDK's "libraries.dart".
+enum Category {
+ /// Indicates that a library can be used in a browser context.
+ client,
+
+ /// Indicates that a lbirary can be used in a command line context.
+ server,
+
+ /// Indicates that a library can be used from embedded devices.
+ embedded
+}
+
+/// Information about a "dart:" library gleaned from the SDK's "libraries.dart"
+/// file.
+///
+/// This declaration duplicates the declaration in "libraries.dart".
+class LibraryInfo {
+ /// Path to the library's *.dart file relative to the SDK's "lib" directory.
+ final String path;
+
+ /// The categories in which the library can be used, encoded as a
+ /// comma-separated String.
+ final String _categories;
+
+ /// Path to the dart2js library's *.dart file relative to the SDK's "lib"
+ /// directory, or null if dart2js uses the common library path defined above.
+ final String dart2jsPath;
+
+ /// Path to the dart2js library's patch file relative to the SDK's "lib"
+ /// directory, or null if no dart2js patch file associated with this library.
+ final String dart2jsPatchPath;
+
+ /// True if this library is documented and should be shown to the user.
+ final bool documented;
+
+ /// Bit flags indicating which platforms consume this library. See
+ /// [DART2JS_LIBRARY] and [VM_LIBRARY].
+ final int platforms;
+
+ /// True if the library contains implementation details for another library.
+ /// The implication is that these libraries are less commonly used and that
+ /// tools like the analysis server should not show these libraries in a list
+ /// of all libraries unless the user specifically asks the tool to do so.
+ final bool implementation;
+
+ /// States the current maturity of this library.
+ final Maturity maturity;
+
+ const LibraryInfo(this.path,
+ {String categories: "",
+ this.dart2jsPath,
+ this.dart2jsPatchPath,
+ this.implementation: false,
+ this.documented: true,
+ this.maturity: Maturity.UNSPECIFIED,
+ this.platforms: DART2JS_PLATFORM | VM_PLATFORM})
+ : _categories = categories;
+
+ /// The categories in which the library can be used.
+ ///
+ /// If no categories are specified, the library is internal and cannot be
+ /// loaded by user code.
+ List<Category> get categories {
+ // `''.split(',')` returns [''], not [], so we handle that case separately.
+ if (_categories.isEmpty) return const <Category>[];
+ return _categories.split(',').map(parseCategory).toList();
+ }
+
+ /// The original "categories" String that was passed to the constructor.
+ ///
+ /// Can be used to construct a slightly modified copy of this LibraryInfo.
+ String get categoriesString {
+ return _categories;
+ }
+
+ bool get isDart2jsLibrary => (platforms & DART2JS_PLATFORM) != 0;
+
+ bool get isInternal => categories.isEmpty;
+
+ bool get isVmLibrary => (platforms & VM_PLATFORM) != 0;
+}
+
+/// Abstraction to capture the maturity of a library.
+class Maturity {
+ static const Maturity DEPRECATED = const Maturity(0, "Deprecated",
+ "This library will be remove before next major release.");
+ static const Maturity EXPERIMENTAL = const Maturity(
+ 1,
+ "Experimental",
+ "This library is experimental and will likely change or be removed\n"
+ "in future versions.");
+ static const Maturity UNSTABLE = const Maturity(
+ 2,
+ "Unstable",
+ "This library is in still changing and have not yet endured\n"
+ "sufficient real-world testing.\n"
+ "Backwards-compatibility is NOT guaranteed.");
+
+ static const Maturity WEB_STABLE = const Maturity(
+ 3,
+ "Web Stable",
+ "This library is tracking the DOM evolution as defined by WC3.\n"
+ "Backwards-compatibility is NOT guaranteed.");
+
+ static const Maturity STABLE = const Maturity(
+ 4,
+ "Stable",
+ "The library is stable. API backwards-compatibility is guaranteed.\n"
+ "However implementation details might change.");
+
+ static const Maturity LOCKED = const Maturity(5, "Locked",
+ "This library will not change except when serious bugs are encountered.");
+
+ static const Maturity UNSPECIFIED = const Maturity(-1, "Unspecified",
+ "The maturity for this library has not been specified.");
+
+ final int level;
+
+ final String name;
+
+ final String description;
+
+ const Maturity(this.level, this.name, this.description);
+
+ String toString() => "$name: $level\n$description\n";
+}
diff --git a/pub/front_end/lib/src/base/processed_options.dart b/pub/front_end/lib/src/base/processed_options.dart
new file mode 100644
index 0000000..60ef2ec
--- /dev/null
+++ b/pub/front_end/lib/src/base/processed_options.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:front_end/compiler_options.dart';
+import 'package:front_end/file_system.dart';
+import 'package:front_end/src/base/uri_resolver.dart';
+import 'package:package_config/packages_file.dart' as package_config;
+
+/// Wrapper around [CompilerOptions] which exposes the options in a form useful
+/// to the front end implementation.
+///
+/// The intent is that the front end should immediately wrap any incoming
+/// [CompilerOptions] object in this class before doing further processing, and
+/// should thereafter access all options via the wrapper. This ensures that
+/// options are interpreted in a consistent way and that data derived from
+/// options is not unnecessarily recomputed.
+class ProcessedOptions {
+ /// The raw [CompilerOptions] which this class wraps.
+ final CompilerOptions _raw;
+
+ /// The package map derived from the options, or `null` if the package map has
+ /// not been computed yet.
+ Map<String, Uri> _packages;
+
+ /// A URI resolver based on the options, or `null` if the URI resolver has not
+ /// been computed yet.
+ UriResolver _uriResolver;
+
+ /// The summary bundle for the SDK, or `null` if it has not been read yet.
+ PackageBundle _sdkSummary;
+
+ /// The location of the SDK, or `null` if the location hasn't been determined
+ /// yet.
+ Uri _sdkRoot;
+
+ /// Initializes a [ProcessedOptions] object wrapping the given [rawOptions].
+ ProcessedOptions(CompilerOptions rawOptions) : this._raw = rawOptions;
+
+ /// Determine whether to generate code for the SDK when compiling a
+ /// whole-program.
+ bool get compileSdk => _raw.compileSdk;
+
+ /// Get the [FileSystem] which should be used by the front end to access
+ /// files.
+ ///
+ /// If the client supplied roots using [CompilerOptions.multiRoots], the
+ /// returned [FileSystem] will automatically perform the appropriate mapping.
+ FileSystem get fileSystem {
+ // TODO(paulberry): support multiRoots.
+ assert(_raw.multiRoots.isEmpty);
+ return _raw.fileSystem;
+ }
+
+ /// Get the summary bundle for the SDK.
+ ///
+ /// This is an asynchronous getter since file system operations are required.
+ Future<PackageBundle> getSdkSummary() async {
+ if (_sdkSummary == null) {
+ Uri summaryLocation;
+ if (_raw.sdkSummary != null) {
+ // Options sdkSummary and sdkRoot are mutually exclusive.
+ assert(_raw.sdkRoot == null);
+ // No need to look for the SDK; we were told where the SDK summary is.
+ summaryLocation = _raw.sdkSummary;
+ } else {
+ // Need to look for the SDK summary inside the SDK.
+ var sdkRoot = await _getSdkRoot();
+ summaryLocation = sdkRoot.resolve(
+ 'lib/_internal/' + (_raw.strongMode ? 'strong.sum' : 'spec.sum'));
+ }
+ var summaryBytes =
+ await fileSystem.entityForUri(summaryLocation).readAsBytes();
+ _sdkSummary = new PackageBundle.fromBuffer(summaryBytes);
+ }
+ return _sdkSummary;
+ }
+
+ /// Get the [UriResolver] which resolves "package:" and "dart:" URIs.
+ ///
+ /// This is an asynchronous getter since file system operations may be
+ /// required to locate/read the packages file as well as SDK metadata.
+ Future<UriResolver> getUriResolver() async {
+ if (_uriResolver == null) {
+ await _getPackages();
+ var sdkLibraries =
+ <String, Uri>{}; // TODO(paulberry): support SDK libraries
+ _uriResolver = new UriResolver(_packages, sdkLibraries);
+ }
+ return _uriResolver;
+ }
+
+ /// Get the package map which maps package names to URIs.
+ ///
+ /// This is an asynchronous getter since file system operations may be
+ /// required to locate/read the packages file.
+ Future<Map<String, Uri>> _getPackages() async {
+ if (_packages == null) {
+ if (_raw.packagesFileUri == null) {
+ throw new UnimplementedError(); // TODO(paulberry): search for .packages
+ } else if (_raw.packagesFileUri.path.isEmpty) {
+ _packages = {};
+ } else {
+ var contents =
+ await fileSystem.entityForUri(_raw.packagesFileUri).readAsBytes();
+ _packages = package_config.parse(contents, _raw.packagesFileUri);
+ }
+ }
+ return _packages;
+ }
+
+ /// Get the location of the SDK.
+ ///
+ /// This is an asynchronous getter since file system operations may be
+ /// required to locate the SDK.
+ Future<Uri> _getSdkRoot() async {
+ if (_sdkRoot == null) {
+ // If an SDK summary location was provided, the SDK itself should not be
+ // needed.
+ assert(_raw.sdkSummary == null);
+ if (_raw.sdkRoot == null) {
+ // TODO(paulberry): implement the algorithm for finding the SDK
+ // automagically.
+ throw new UnimplementedError();
+ }
+ _sdkRoot = _raw.sdkRoot;
+ if (!_sdkRoot.path.endsWith('/')) {
+ _sdkRoot = _sdkRoot.replace(path: _sdkRoot.path + '/');
+ }
+ }
+ return _sdkRoot;
+ }
+}
diff --git a/pub/front_end/lib/src/base/resolve_relative_uri.dart b/pub/front_end/lib/src/base/resolve_relative_uri.dart
new file mode 100644
index 0000000..cafcbf2
--- /dev/null
+++ b/pub/front_end/lib/src/base/resolve_relative_uri.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2017, 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.
+
+/**
+ * Resolve the [containedUri] against [baseUri] using Dart rules.
+ *
+ * This function behaves similarly to [Uri.resolveUri], except that it properly
+ * handles situations like the following:
+ *
+ * resolveRelativeUri(dart:core, bool.dart) -> dart:core/bool.dart
+ * resolveRelativeUri(package:a/b.dart, ../c.dart) -> package:a/c.dart
+ */
+Uri resolveRelativeUri(Uri baseUri, Uri containedUri) {
+ if (containedUri.isAbsolute) {
+ return containedUri;
+ }
+ String scheme = baseUri.scheme;
+ // dart:core => dart:core/core.dart
+ if (scheme == 'dart') {
+ String part = baseUri.path;
+ if (part.indexOf('/') < 0) {
+ baseUri = Uri.parse('$scheme:$part/$part.dart');
+ }
+ }
+ return baseUri.resolveUri(containedUri);
+}
diff --git a/pub/front_end/lib/src/base/source.dart b/pub/front_end/lib/src/base/source.dart
new file mode 100644
index 0000000..a87f933
--- /dev/null
+++ b/pub/front_end/lib/src/base/source.dart
@@ -0,0 +1,175 @@
+// 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.
+
+import 'package:front_end/src/base/analysis_target.dart';
+import 'package:front_end/src/base/timestamped_data.dart';
+import 'package:front_end/src/base/uri_kind.dart';
+import 'package:path/path.dart' as pathos;
+
+/// Base class providing implementations for the methods in [Source] that don't
+/// require filesystem access.
+abstract class BasicSource extends Source {
+ final Uri uri;
+
+ BasicSource(this.uri);
+
+ @override
+ String get encoding => uri.toString();
+
+ @override
+ String get fullName => encoding;
+
+ @override
+ int get hashCode => uri.hashCode;
+
+ @override
+ bool get isInSystemLibrary => uri.scheme == 'dart';
+
+ @override
+ String get shortName => pathos.basename(fullName);
+
+ @override
+ bool operator ==(Object object) => object is Source && object.uri == uri;
+}
+
+/**
+ * The interface `Source` defines the behavior of objects representing source code that can be
+ * analyzed by the analysis engine.
+ *
+ * Implementations of this interface need to be aware of some assumptions made by the analysis
+ * engine concerning sources:
+ * * Sources are not required to be unique. That is, there can be multiple instances representing
+ * the same source.
+ * * Sources are long lived. That is, the engine is allowed to hold on to a source for an extended
+ * period of time and that source must continue to report accurate and up-to-date information.
+ * Because of these assumptions, most implementations will not maintain any state but will delegate
+ * to an authoritative system of record in order to implement this API. For example, a source that
+ * represents files on disk would typically query the file system to determine the state of the
+ * file.
+ *
+ * If the instances that implement this API are the system of record, then they will typically be
+ * unique. In that case, sources that are created that represent non-existent files must also be
+ * retained so that if those files are created at a later date the long-lived sources representing
+ * those files will know that they now exist.
+ */
+abstract class Source implements AnalysisTarget {
+ /**
+ * An empty list of sources.
+ */
+ static const List<Source> EMPTY_LIST = const <Source>[];
+
+ /**
+ * Get the contents and timestamp of this source.
+ *
+ * Clients should consider using the method [AnalysisContext.getContents]
+ * because contexts can have local overrides of the content of a source that the source is not
+ * aware of.
+ *
+ * @return the contents and timestamp of the source
+ * @throws Exception if the contents of this source could not be accessed
+ */
+ TimestampedData<String> get contents;
+
+ /**
+ * Return an encoded representation of this source that can be used to create a source that is
+ * equal to this source.
+ *
+ * @return an encoded representation of this source
+ * See [SourceFactory.fromEncoding].
+ */
+ String get encoding;
+
+ /**
+ * Return the full (long) version of the name that can be displayed to the user to denote this
+ * source. For example, for a source representing a file this would typically be the absolute path
+ * of the file.
+ *
+ * @return a name that can be displayed to the user to denote this source
+ */
+ String get fullName;
+
+ /**
+ * Return a hash code for this source.
+ *
+ * @return a hash code for this source
+ * See [Object.hashCode].
+ */
+ @override
+ int get hashCode;
+
+ /**
+ * Return `true` if this source is in one of the system libraries.
+ *
+ * @return `true` if this is in a system library
+ */
+ bool get isInSystemLibrary;
+
+ @override
+ Source get librarySource => null;
+
+ /**
+ * Return the modification stamp for this source, or a negative value if the
+ * source does not exist. A modification stamp is a non-negative integer with
+ * the property that if the contents of the source have not been modified
+ * since the last time the modification stamp was accessed then the same value
+ * will be returned, but if the contents of the source have been modified one
+ * or more times (even if the net change is zero) the stamps will be different.
+ *
+ * Clients should consider using the method
+ * [AnalysisContext.getModificationStamp] because contexts can have local
+ * overrides of the content of a source that the source is not aware of.
+ */
+ int get modificationStamp;
+
+ /**
+ * Return a short version of the name that can be displayed to the user to denote this source. For
+ * example, for a source representing a file this would typically be the name of the file.
+ *
+ * @return a name that can be displayed to the user to denote this source
+ */
+ String get shortName;
+
+ @override
+ Source get source => this;
+
+ /**
+ * Return the URI from which this source was originally derived.
+ *
+ * @return the URI from which this source was originally derived
+ */
+ Uri get uri;
+
+ /**
+ * Return the kind of URI from which this source was originally derived. If this source was
+ * created from an absolute URI, then the returned kind will reflect the scheme of the absolute
+ * URI. If it was created from a relative URI, then the returned kind will be the same as the kind
+ * of the source against which the relative URI was resolved.
+ *
+ * @return the kind of URI from which this source was originally derived
+ */
+ UriKind get uriKind;
+
+ /**
+ * Return `true` if the given object is a source that represents the same source code as
+ * this source.
+ *
+ * @param object the object to be compared with this object
+ * @return `true` if the given object is a source that represents the same source code as
+ * this source
+ * See [Object.==].
+ */
+ @override
+ bool operator ==(Object object);
+
+ /**
+ * Return `true` if this source exists.
+ *
+ * Clients should consider using the method [AnalysisContext.exists] because
+ * contexts can have local overrides of the content of a source that the source is not aware of
+ * and a source with local content is considered to exist even if there is no file on disk.
+ *
+ * @return `true` if this source exists
+ */
+ bool exists();
+}
diff --git a/pub/front_end/lib/src/scanner/syntactic_entity.dart b/pub/front_end/lib/src/base/syntactic_entity.dart
similarity index 100%
rename from pub/front_end/lib/src/scanner/syntactic_entity.dart
rename to pub/front_end/lib/src/base/syntactic_entity.dart
diff --git a/pub/front_end/lib/src/base/timestamped_data.dart b/pub/front_end/lib/src/base/timestamped_data.dart
new file mode 100644
index 0000000..4c76dc5
--- /dev/null
+++ b/pub/front_end/lib/src/base/timestamped_data.dart
@@ -0,0 +1,24 @@
+// 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.
+
+/**
+ * Analysis data for which we have a modification time.
+ */
+class TimestampedData<E> {
+ /**
+ * The modification time of the source from which the data was created.
+ */
+ final int modificationTime;
+
+ /**
+ * The data that was created from the source.
+ */
+ final E data;
+
+ /**
+ * Initialize a newly created holder to associate the given [data] with the
+ * given [modificationTime].
+ */
+ TimestampedData(this.modificationTime, this.data);
+}
diff --git a/pub/front_end/lib/src/base/uri_kind.dart b/pub/front_end/lib/src/base/uri_kind.dart
new file mode 100644
index 0000000..0e6d007
--- /dev/null
+++ b/pub/front_end/lib/src/base/uri_kind.dart
@@ -0,0 +1,87 @@
+// 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.
+
+/**
+ * The enumeration `UriKind` defines the different kinds of URI's that are known to the
+ * analysis engine. These are used to keep track of the kind of URI associated with a given source.
+ */
+class UriKind implements Comparable<UriKind> {
+ /**
+ * A 'dart:' URI.
+ */
+ static const UriKind DART_URI = const UriKind('DART_URI', 0, 0x64);
+
+ /**
+ * A 'file:' URI.
+ */
+ static const UriKind FILE_URI = const UriKind('FILE_URI', 1, 0x66);
+
+ /**
+ * A 'package:' URI.
+ */
+ static const UriKind PACKAGE_URI = const UriKind('PACKAGE_URI', 2, 0x70);
+
+ static const List<UriKind> values = const [DART_URI, FILE_URI, PACKAGE_URI];
+
+ /**
+ * The name of this URI kind.
+ */
+ final String name;
+
+ /**
+ * The ordinal value of the URI kind.
+ */
+ final int ordinal;
+
+ /**
+ * The single character encoding used to identify this kind of URI.
+ */
+ final int encoding;
+
+ /**
+ * Initialize a newly created URI kind to have the given encoding.
+ */
+ const UriKind(this.name, this.ordinal, this.encoding);
+
+ @override
+ int get hashCode => ordinal;
+
+ @override
+ int compareTo(UriKind other) => ordinal - other.ordinal;
+
+ @override
+ String toString() => name;
+
+ /**
+ * Return the URI kind represented by the given [encoding], or `null` if there
+ * is no kind with the given encoding.
+ */
+ static UriKind fromEncoding(int encoding) {
+ while (true) {
+ if (encoding == 0x64) {
+ return DART_URI;
+ } else if (encoding == 0x66) {
+ return FILE_URI;
+ } else if (encoding == 0x70) {
+ return PACKAGE_URI;
+ }
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Return the URI kind corresponding to the given scheme string.
+ */
+ static UriKind fromScheme(String scheme) {
+ if (scheme == 'package') {
+ return UriKind.PACKAGE_URI;
+ } else if (scheme == 'dart') {
+ return UriKind.DART_URI;
+ } else if (scheme == 'file') {
+ return UriKind.FILE_URI;
+ }
+ return UriKind.FILE_URI;
+ }
+}
diff --git a/pub/front_end/lib/src/base/uri_resolver.dart b/pub/front_end/lib/src/base/uri_resolver.dart
new file mode 100644
index 0000000..0c6a420
--- /dev/null
+++ b/pub/front_end/lib/src/base/uri_resolver.dart
@@ -0,0 +1,68 @@
+// 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.
+
+/// The class `UriResolver` implements the rules for resolving "dart:" and
+/// "package:" URIs.
+class UriResolver {
+ /// The URI scheme used for "package" URIs.
+ static const PACKAGE_SCHEME = 'package';
+
+ /// The URI scheme used for "dart" URIs.
+ static const DART_SCHEME = 'dart';
+
+ /// A map from package name to the file URI of the "lib" directory of the
+ /// corresponding package. This is equivalent to the format returned by
+ /// the "package_config" package's parse() function.
+ final Map<String, Uri> packages;
+
+ /// A map from SDK library name (e.g. `core` for `dart:core`) to the file URI
+ /// of the defining compilation unit of the SDK library.
+ final Map<String, Uri> sdkLibraries;
+
+ UriResolver(this.packages, this.sdkLibraries);
+
+ /// Converts "package:" and "dart:" URIs to the locations of the corresponding
+ /// files.
+ ///
+ /// If the given URI is a "package:" or "dart:" URI, is well formed, and names
+ /// a package or dart library that is recognized, returns the URI it resolves
+ /// to. If the given URI is a "package:" or "dart:" URI, and is ill-formed
+ /// or names a package or dart library that is not recognized, returns `null`.
+ ///
+ /// If the given URI has any scheme other than "package:" or "dart:", it is
+ /// returned unchanged.
+ ///
+ /// It is not necessary for the URI to be absolute (relative URIs will be
+ /// passed through unchanged).
+ ///
+ /// Note that no I/O is performed; the URI that is returned will be
+ /// independent of whether or not any particular file exists on the file
+ /// system.
+ Uri resolve(Uri uri) {
+ if (uri.scheme == DART_SCHEME || uri.scheme == PACKAGE_SCHEME) {
+ var path = uri.path;
+ var slashIndex = path.indexOf('/');
+ String prefix;
+ String rest;
+ if (slashIndex >= 0) {
+ prefix = path.substring(0, slashIndex);
+ rest = path.substring(slashIndex + 1);
+ } else {
+ prefix = path;
+ rest = '';
+ }
+ Uri libUri;
+ if (uri.scheme == PACKAGE_SCHEME) {
+ if (slashIndex < 0) return null;
+ libUri = packages[prefix];
+ } else if (uri.scheme == DART_SCHEME) {
+ libUri = sdkLibraries[prefix];
+ }
+ if (libUri == null) return null;
+ return libUri.resolve(rest);
+ } else {
+ return uri;
+ }
+ }
+}
diff --git a/pub/front_end/lib/src/dependency_grapher_impl.dart b/pub/front_end/lib/src/dependency_grapher_impl.dart
new file mode 100644
index 0000000..66aa443
--- /dev/null
+++ b/pub/front_end/lib/src/dependency_grapher_impl.dart
@@ -0,0 +1,151 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/scanner/reader.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:front_end/dependency_grapher.dart';
+import 'package:front_end/src/async_dependency_walker.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/base/uri_resolver.dart';
+import 'package:front_end/src/scanner/scanner.dart';
+
+/// Generates a representation of the dependency graph of a program.
+///
+/// Given the Uri of one or more files, this function follows `import`,
+/// `export`, and `part` declarations to discover a graph of all files involved
+/// in the program.
+///
+/// If a [fileReader] is supplied, it is used to read file contents; otherwise
+/// they are read directly from `options.fileSystem`.
+///
+/// This is intended for internal use by the front end. Clients should use
+/// package:front_end/dependency_grapher.dart.
+Future<Graph> graphForProgram(List<Uri> sources, ProcessedOptions options,
+ {FileReader fileReader}) async {
+ var uriResolver = await options.getUriResolver();
+ fileReader ??= (originalUri, resolvedUri) =>
+ options.fileSystem.entityForUri(resolvedUri).readAsString();
+ var walker = new _Walker(fileReader, uriResolver, options.compileSdk);
+ var startingPoint = new _StartingPoint(walker, sources);
+ await walker.walk(startingPoint);
+ return walker.graph;
+}
+
+/// Type of the callback function used by [graphForProgram] to read file
+/// contents.
+typedef Future<String> FileReader(Uri originalUri, Uri resolvedUri);
+
+class _Scanner extends Scanner {
+ _Scanner(String contents) : super(new CharSequenceReader(contents)) {
+ preserveComments = false;
+ }
+
+ @override
+ void reportError(errorCode, int offset, List<Object> arguments) {
+ // TODO(paulberry): report errors.
+ }
+}
+
+class _StartingPoint extends _WalkerNode {
+ final List<Uri> sources;
+
+ _StartingPoint(_Walker walker, this.sources) : super(walker, null);
+
+ @override
+ Future<List<_WalkerNode>> computeDependencies() async =>
+ sources.map(walker.nodeForUri).toList();
+}
+
+class _Walker extends AsyncDependencyWalker<_WalkerNode> {
+ final FileReader fileReader;
+ final UriResolver uriResolver;
+ final _nodesByUri = <Uri, _WalkerNode>{};
+ final graph = new Graph();
+ final bool compileSdk;
+
+ _Walker(this.fileReader, this.uriResolver, this.compileSdk);
+
+ @override
+ Future<Null> evaluate(_WalkerNode v) {
+ if (v is _StartingPoint) return new Future.value();
+ return evaluateScc([v]);
+ }
+
+ @override
+ Future<Null> evaluateScc(List<_WalkerNode> scc) {
+ var cycle = new LibraryCycleNode();
+ for (var walkerNode in scc) {
+ cycle.libraries[walkerNode.uri] = walkerNode.library;
+ }
+ graph.topologicallySortedCycles.add(cycle);
+ return new Future.value();
+ }
+
+ _WalkerNode nodeForUri(Uri referencedUri) {
+ var dependencyNode = _nodesByUri.putIfAbsent(
+ referencedUri, () => new _WalkerNode(this, referencedUri));
+ return dependencyNode;
+ }
+}
+
+class _WalkerNode extends Node<_WalkerNode> {
+ static final dartCoreUri = Uri.parse('dart:core');
+ final _Walker walker;
+ final Uri uri;
+ final LibraryNode library;
+
+ _WalkerNode(this.walker, Uri uri)
+ : uri = uri,
+ library = new LibraryNode(uri);
+
+ @override
+ Future<List<_WalkerNode>> computeDependencies() async {
+ var dependencies = <_WalkerNode>[];
+ // TODO(paulberry): add error recovery if the file can't be read.
+ var resolvedUri = walker.uriResolver.resolve(uri);
+ if (resolvedUri == null) {
+ // TODO(paulberry): If an error reporter was provided, report the error
+ // in the proper way and continue.
+ throw new StateError('Invalid URI: $uri');
+ }
+ var contents = await walker.fileReader(uri, resolvedUri);
+ var scanner = new _Scanner(contents);
+ var token = scanner.tokenize();
+ // TODO(paulberry): report errors.
+ var parser = new Parser(null, AnalysisErrorListener.NULL_LISTENER);
+ var unit = parser.parseDirectives(token);
+ bool coreUriFound = false;
+ void handleDependency(Uri referencedUri) {
+ _WalkerNode dependencyNode = walker.nodeForUri(referencedUri);
+ library.dependencies.add(dependencyNode.library);
+ if (referencedUri.scheme != 'dart' || walker.compileSdk) {
+ dependencies.add(dependencyNode);
+ }
+ if (referencedUri == dartCoreUri) {
+ coreUriFound = true;
+ }
+ }
+
+ for (var directive in unit.directives) {
+ if (directive is UriBasedDirective) {
+ // TODO(paulberry): when we support SDK libraries, we'll need more
+ // complex logic here to find SDK parts correctly.
+ var referencedUri = uri.resolve(directive.uri.stringValue);
+ if (directive is PartDirective) {
+ library.parts.add(referencedUri);
+ } else {
+ handleDependency(referencedUri);
+ }
+ }
+ }
+ if (!coreUriFound) {
+ handleDependency(dartCoreUri);
+ }
+ return dependencies;
+ }
+}
diff --git a/pub/front_end/lib/src/dependency_walker.dart b/pub/front_end/lib/src/dependency_walker.dart
new file mode 100644
index 0000000..7a27acb
--- /dev/null
+++ b/pub/front_end/lib/src/dependency_walker.dart
@@ -0,0 +1,168 @@
+// 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.
+
+/**
+ * Instances of [Node] represent nodes in a dependency graph. The
+ * type parameter, [NodeType], is the derived type (this affords some
+ * extra type safety by making it difficult to accidentally construct
+ * bridges between unrelated dependency graphs).
+ */
+abstract class Node<NodeType> {
+ /**
+ * Index used by Tarjan's strongly connected components algorithm.
+ * Zero means the node has not been visited yet; a nonzero value
+ * counts the order in which the node was visited.
+ */
+ int _index = 0;
+
+ /**
+ * Low link used by Tarjan's strongly connected components
+ * algorithm. This represents the smallest [_index] of all the nodes
+ * in the strongly connected component to which this node belongs.
+ */
+ int _lowLink = 0;
+
+ List<NodeType> _dependencies;
+
+ /**
+ * Retrieve the dependencies of this node.
+ */
+ List<NodeType> get dependencies => _dependencies ??= computeDependencies();
+
+ /**
+ * Indicates whether this node has been evaluated yet.
+ */
+ bool get isEvaluated;
+
+ /**
+ * Compute the dependencies of this node.
+ */
+ List<NodeType> computeDependencies();
+}
+
+/**
+ * An instance of [DependencyWalker] contains the core algorithms for
+ * walking a dependency graph and evaluating nodes in a safe order.
+ */
+abstract class DependencyWalker<NodeType extends Node<NodeType>> {
+ /**
+ * Called by [walk] to evaluate a single non-cyclical node, after
+ * all that node's dependencies have been evaluated.
+ */
+ void evaluate(NodeType v);
+
+ /**
+ * Called by [walk] to evaluate a strongly connected component
+ * containing one or more nodes. All dependencies of the strongly
+ * connected component have been evaluated.
+ */
+ void evaluateScc(List<NodeType> scc);
+
+ /**
+ * Walk the dependency graph starting at [startingPoint], finding
+ * strongly connected components and evaluating them in a safe order
+ * by calling [evaluate] and [evaluateScc].
+ *
+ * This is an implementation of Tarjan's strongly connected
+ * components algorithm
+ * (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm).
+ */
+ void walk(NodeType startingPoint) {
+ // TODO(paulberry): consider rewriting in a non-recursive way so
+ // that long dependency chains don't cause stack overflow.
+
+ // TODO(paulberry): in the event that an exception occurs during
+ // the walk, restore the state of the [Node] data structures so
+ // that further evaluation will be safe.
+
+ // The index which will be assigned to the next node that is
+ // freshly visited.
+ int index = 1;
+
+ // Stack of nodes which have been seen so far and whose strongly
+ // connected component is still being determined. Nodes are only
+ // popped off the stack when they are evaluated, so sometimes the
+ // stack contains nodes that were visited after the current node.
+ List<NodeType> stack = <NodeType>[];
+
+ void strongConnect(NodeType node) {
+ bool hasTrivialCycle = false;
+
+ // Assign the current node an index and add it to the stack. We
+ // haven't seen any of its dependencies yet, so set its lowLink
+ // to its index, indicating that so far it is the only node in
+ // its strongly connected component.
+ node._index = node._lowLink = index++;
+ stack.add(node);
+
+ // Consider the node's dependencies one at a time.
+ for (NodeType dependency in node.dependencies) {
+ // If the dependency has already been evaluated, it can't be
+ // part of this node's strongly connected component, so we can
+ // skip it.
+ if (dependency.isEvaluated) {
+ continue;
+ }
+ if (identical(node, dependency)) {
+ // If a node includes itself as a dependency, there is no need to
+ // explore the dependency further.
+ hasTrivialCycle = true;
+ } else if (dependency._index == 0) {
+ // The dependency hasn't been seen yet, so recurse on it.
+ strongConnect(dependency);
+ // If the dependency's lowLink refers to a node that was
+ // visited before the current node, that means that the
+ // current node, the dependency, and the node referred to by
+ // the dependency's lowLink are all part of the same
+ // strongly connected component, so we need to update the
+ // current node's lowLink accordingly.
+ if (dependency._lowLink < node._lowLink) {
+ node._lowLink = dependency._lowLink;
+ }
+ } else {
+ // The dependency has already been seen, so it is part of
+ // the current node's strongly connected component. If it
+ // was visited earlier than the current node's lowLink, then
+ // it is a new addition to the current node's strongly
+ // connected component, so we need to update the current
+ // node's lowLink accordingly.
+ if (dependency._index < node._lowLink) {
+ node._lowLink = dependency._index;
+ }
+ }
+ }
+
+ // If the current node's lowLink is the same as its index, then
+ // we have finished visiting a strongly connected component, so
+ // pop the stack and evaluate it before moving on.
+ if (node._lowLink == node._index) {
+ // The strongly connected component has only one node. If there is a
+ // cycle, it's a trivial one.
+ if (identical(stack.last, node)) {
+ stack.removeLast();
+ if (hasTrivialCycle) {
+ evaluateScc(<NodeType>[node]);
+ } else {
+ evaluate(node);
+ }
+ } else {
+ // There are multiple nodes in the strongly connected
+ // component.
+ List<NodeType> scc = <NodeType>[];
+ while (true) {
+ NodeType otherNode = stack.removeLast();
+ scc.add(otherNode);
+ if (identical(otherNode, node)) {
+ break;
+ }
+ }
+ evaluateScc(scc);
+ }
+ }
+ }
+
+ // Kick off the algorithm starting with the starting point.
+ strongConnect(startingPoint);
+ }
+}
diff --git a/pub/front_end/lib/src/incremental_kernel_generator_impl.dart b/pub/front_end/lib/src/incremental_kernel_generator_impl.dart
new file mode 100644
index 0000000..efd4ddd
--- /dev/null
+++ b/pub/front_end/lib/src/incremental_kernel_generator_impl.dart
@@ -0,0 +1,147 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/standard_resolution_map.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:front_end/incremental_kernel_generator.dart';
+import 'package:front_end/incremental_resolved_ast_generator.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/base/source.dart';
+import 'package:front_end/src/incremental_resolved_ast_generator_impl.dart';
+import 'package:kernel/analyzer/loader.dart';
+import 'package:kernel/kernel.dart' hide Source;
+import 'package:kernel/repository.dart';
+
+dynamic unimplemented() {
+ // TODO(paulberry): get rid of this.
+ throw new UnimplementedError();
+}
+
+DartOptions _convertOptions(ProcessedOptions options) {
+ // TODO(paulberry): make sure options.compileSdk is handled correctly.
+ return new DartOptions(
+ strongMode: true, // TODO(paulberry): options.strongMode,
+ sdk: null, // TODO(paulberry): _uriToPath(options.sdkRoot, options),
+ sdkSummary:
+ null, // TODO(paulberry): options.compileSdk ? null : _uriToPath(options.sdkSummary, options),
+ packagePath:
+ null, // TODO(paulberry): _uriToPath(options.packagesFileUri, options),
+ declaredVariables: null // TODO(paulberry): options.declaredVariables
+ );
+}
+
+/// Implementation of [IncrementalKernelGenerator].
+///
+/// Theory of operation: an instance of [IncrementalResolvedAstGenerator] is
+/// used to obtain resolved ASTs, and these are fed into kernel code generation
+/// logic.
+///
+/// Note that the kernel doesn't expect to take resolved ASTs as a direct input;
+/// it expects to request resolved ASTs from an [AnalysisContext]. To deal with
+/// this, we create [_AnalysisContextProxy] which returns the resolved ASTs when
+/// requested. TODO(paulberry): Make this unnecessary.
+class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
+ final IncrementalResolvedAstGenerator _resolvedAstGenerator;
+ final ProcessedOptions _options;
+
+ IncrementalKernelGeneratorImpl(Uri source, ProcessedOptions options)
+ : _resolvedAstGenerator =
+ new IncrementalResolvedAstGeneratorImpl(source, options),
+ _options = options;
+
+ @override
+ Future<DeltaProgram> computeDelta(
+ {Future<Null> watch(Uri uri, bool used)}) async {
+ var deltaLibraries = await _resolvedAstGenerator.computeDelta();
+ var kernelOptions = _convertOptions(_options);
+ var packages = null; // TODO(paulberry)
+ var kernels = <Uri, Program>{};
+ for (Uri uri in deltaLibraries.newState.keys) {
+ // The kernel generation code doesn't currently support building a kernel
+ // directly from resolved ASTs--it wants to query an analysis context. So
+ // we provide it with a proxy analysis context that feeds it the resolved
+ // ASTs.
+ var strongMode = true; // TODO(paulberry): set this correctly
+ var analysisOptions = new _AnalysisOptionsProxy(strongMode);
+ var context =
+ new _AnalysisContextProxy(deltaLibraries.newState, analysisOptions);
+ var repository = new Repository();
+ var loader =
+ new DartLoader(repository, kernelOptions, packages, context: context);
+ loader.loadLibrary(uri);
+ kernels[uri] = new Program(repository.libraries);
+ // TODO(paulberry) rework watch invocation to eliminate race condition,
+ // include part source files, and prevent watch from being a bottleneck
+ if (watch != null) await watch(uri, true);
+ }
+ // TODO(paulberry) invoke watch with used=false for each unused source
+ return new DeltaProgram(kernels);
+ }
+
+ @override
+ void invalidate(String path) => _resolvedAstGenerator.invalidate(path);
+
+ @override
+ void invalidateAll() => _resolvedAstGenerator.invalidateAll();
+}
+
+class _AnalysisContextProxy implements AnalysisContext {
+ final Map<Uri, Map<Uri, CompilationUnit>> _resolvedLibraries;
+
+ @override
+ final _SourceFactoryProxy sourceFactory = new _SourceFactoryProxy();
+
+ @override
+ final AnalysisOptions analysisOptions;
+
+ _AnalysisContextProxy(this._resolvedLibraries, this.analysisOptions);
+
+ List<AnalysisError> computeErrors(Source source) {
+ // TODO(paulberry): do we need to return errors sometimes?
+ return [];
+ }
+
+ LibraryElement computeLibraryElement(Source source) {
+ assert(_resolvedLibraries.containsKey(source.uri));
+ return resolutionMap
+ .elementDeclaredByCompilationUnit(
+ _resolvedLibraries[source.uri][source.uri])
+ .library;
+ }
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+
+ CompilationUnit resolveCompilationUnit(
+ Source unitSource, LibraryElement library) {
+ var unit = _resolvedLibraries[library.source.uri][unitSource.uri];
+ assert(unit != null);
+ return unit;
+ }
+}
+
+class _AnalysisOptionsProxy implements AnalysisOptions {
+ final bool strongMode;
+
+ _AnalysisOptionsProxy(this.strongMode);
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+}
+
+class _SourceFactoryProxy implements SourceFactory {
+ Source forUri2(Uri absoluteUri) => new _SourceProxy(absoluteUri);
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+}
+
+class _SourceProxy extends BasicSource {
+ _SourceProxy(Uri uri) : super(uri);
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+}
diff --git a/pub/front_end/lib/src/incremental_resolved_ast_generator_impl.dart b/pub/front_end/lib/src/incremental_resolved_ast_generator_impl.dart
new file mode 100644
index 0000000..d0b093b
--- /dev/null
+++ b/pub/front_end/lib/src/incremental_resolved_ast_generator_impl.dart
@@ -0,0 +1,302 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/context/context.dart';
+import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
+import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:analyzer/src/util/absolute_path.dart';
+import 'package:front_end/incremental_resolved_ast_generator.dart';
+import 'package:front_end/src/base/file_repository.dart';
+import 'package:front_end/src/base/processed_options.dart';
+import 'package:front_end/src/base/resolve_relative_uri.dart';
+import 'package:front_end/src/base/source.dart';
+import 'package:front_end/src/dependency_grapher_impl.dart';
+import 'package:path/src/context.dart';
+
+dynamic unimplemented() {
+ // TODO(paulberry): get rid of this.
+ throw new UnimplementedError();
+}
+
+/// Implementation of [IncrementalKernelGenerator].
+///
+/// Theory of operation: this class is a thin wrapper around
+/// [driver.AnalysisDriver]. When the client requests a new delta, we forward
+/// the request to the analysis driver. When the client calls an invalidate
+/// method, we ensure that the proper files will be re-read next time a delta is
+/// requested.
+///
+/// Note that the analysis driver expects to be able to read file contents
+/// synchronously based on filesystem path rather than asynchronously based on
+/// URI, so the file contents are first read into memory using the asynchronous
+/// FileSystem API, and then these are fed into the analysis driver using a
+/// proxy implementation of [ResourceProvider]. TODO(paulberry): make this (and
+/// other proxies in this file) unnecessary.
+class IncrementalResolvedAstGeneratorImpl
+ implements IncrementalResolvedAstGenerator {
+ driver.AnalysisDriverScheduler _scheduler;
+ final _fileRepository = new FileRepository();
+ _ResourceProviderProxy _resourceProvider;
+ driver.AnalysisDriver _driver;
+ bool _isInitialized = false;
+ final ProcessedOptions _options;
+ final Uri _source;
+ bool _schedulerStarted = false;
+ final _fileState = <Uri, String>{};
+
+ IncrementalResolvedAstGeneratorImpl(this._source, this._options);
+
+ @override
+ Future<DeltaLibraries> computeDelta() async {
+ if (!_isInitialized) {
+ await init();
+ }
+ // The analysis driver doesn't currently support an asynchronous file API,
+ // so we have to find all the files first to read their contents.
+ // TODO(paulberry): this is an unnecessary source of duplicate work and
+ // should be eliminated ASAP.
+ var graph =
+ await graphForProgram([_source], _options, fileReader: _fileReader);
+ // TODO(paulberry): collect no-longer-referenced files from _fileState and
+ // _fileRepository.
+ var libraries = <Uri, Map<Uri, CompilationUnit>>{};
+ if (!_schedulerStarted) {
+ _scheduler.start();
+ _schedulerStarted = true;
+ }
+ for (var libraryCycle in graph.topologicallySortedCycles) {
+ for (var libraryUri in libraryCycle.libraries.keys) {
+ var libraryNode = libraryCycle.libraries[libraryUri];
+ for (var partUri in libraryNode.parts) {
+ // TODO(paulberry): resolve the part URI.
+ _fileReader(partUri, partUri);
+ }
+ }
+ for (var libraryUri in libraryCycle.libraries.keys) {
+ var libraryNode = libraryCycle.libraries[libraryUri];
+ var result =
+ await _driver.getResult(_fileRepository.pathForUri(libraryUri));
+ // TODO(paulberry): handle errors.
+ var units = {libraryUri: result.unit};
+ for (var partUri in libraryNode.parts) {
+ // Really we ought to have a driver API that lets us request a
+ // specific part of a given library. Otherwise we will run into
+ // problems if a part is included in multiple libraries.
+ // TODO(paulberry): address this.
+ var partResult =
+ await _driver.getResult(_fileRepository.pathForUri(partUri));
+ // TODO(paulberry): handle errors.
+ units[partUri] = partResult.unit;
+ }
+ libraries[libraryUri] = units;
+ }
+ }
+ _driver.addFile(_fileRepository.pathForUri(_source));
+ // TODO(paulberry): stop the scheduler
+ return new DeltaLibraries(libraries);
+ }
+
+ Future<Null> init() async {
+ // TODO(paulberry): can we just use null?
+ var performanceLog = new driver.PerformanceLog(new _NullStringSink());
+ _scheduler = new driver.AnalysisDriverScheduler(performanceLog);
+ _resourceProvider = new _ResourceProviderProxy(_fileRepository);
+ // TODO(paulberry): MemoryByteStore leaks memory (it never discards data).
+ // Do something better here.
+ var byteStore = new MemoryByteStore();
+ // TODO(paulberry): can we just use null?
+ var fileContentOverlay = new FileContentOverlay();
+ var sdkContext = new AnalysisContextImpl();
+ var sdkBundle = await _options.getSdkSummary();
+ var dartSdk = new _DartSdkProxy(sdkBundle, sdkContext, _fileRepository);
+ sdkContext.sourceFactory =
+ new SourceFactory([new DartUriResolver(dartSdk)]);
+
+ var sourceFactory = new _SourceFactoryProxy(dartSdk, _fileRepository);
+ var analysisOptions = new AnalysisOptionsImpl();
+ _driver = new driver.AnalysisDriver(
+ _scheduler,
+ performanceLog,
+ _resourceProvider,
+ byteStore,
+ fileContentOverlay,
+ 'front_end',
+ sourceFactory,
+ analysisOptions,
+ sdkBundle: sdkBundle);
+ _isInitialized = true;
+ }
+
+ @override
+ void invalidate(String path) {
+ throw new UnimplementedError();
+ }
+
+ @override
+ void invalidateAll() {
+ _fileState.clear();
+ _fileRepository.clearContents();
+ // TODO(paulberry): verify that this has an effect (requires a multi-file
+ // test).
+ if (_isInitialized) {
+ _driver.knownFiles.forEach(_driver.changeFile);
+ }
+ }
+
+ Future<String> _fileReader(Uri originalUri, Uri resolvedUri) async {
+ String contents = _fileState[resolvedUri] ??=
+ await _options.fileSystem.entityForUri(resolvedUri).readAsString();
+ _fileRepository.store(originalUri, contents);
+ return contents;
+ }
+}
+
+class _DartSdkProxy implements DartSdk {
+ final PackageBundle summary;
+
+ final AnalysisContext context;
+
+ final FileRepository _fileRepository;
+
+ _DartSdkProxy(this.summary, this.context, this._fileRepository);
+
+ @override
+ PackageBundle getLinkedBundle() => summary;
+
+ @override
+ Source mapDartUri(String uriString) {
+ var uri = Uri.parse(uriString);
+ return new _SourceProxy(
+ uri, _fileRepository.pathForUri(uri, allocate: true));
+ }
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+}
+
+class _FileProxy implements File {
+ final _SourceProxy _source;
+
+ final _ResourceProviderProxy _resourceProvider;
+
+ _FileProxy(this._source, this._resourceProvider);
+
+ @override
+ String get path => _source.fullName;
+
+ @override
+ String get shortName => path;
+
+ @override
+ Source createSource([Uri uri]) {
+ assert(uri == null);
+ return _source;
+ }
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+
+ @override
+ String readAsStringSync() {
+ return _resourceProvider._fileRepository.contentsForPath(path);
+ }
+}
+
+/// A string sink that ignores everything written to it.
+class _NullStringSink implements StringSink {
+ void write(Object obj) {}
+ void writeAll(Iterable objects, [String separator = ""]) {}
+ void writeCharCode(int charCode) {}
+ void writeln([Object obj = ""]) {}
+}
+
+class _ResourceProviderProxy implements ResourceProvider {
+ final FileRepository _fileRepository;
+
+ _ResourceProviderProxy(this._fileRepository);
+
+ @override
+ AbsolutePathContext get absolutePathContext => throw new UnimplementedError();
+
+ @override
+ Context get pathContext => throw new UnimplementedError();
+
+ @override
+ File getFile(String path) {
+ return new _FileProxy(
+ new _SourceProxy(_fileRepository.uriForPath(path), path), this);
+ }
+
+ @override
+ Folder getFolder(String path) => throw new UnimplementedError();
+
+ @override
+ Future<List<int>> getModificationTimes(List<Source> sources) =>
+ throw new UnimplementedError();
+
+ @override
+ Resource getResource(String path) => throw new UnimplementedError();
+
+ @override
+ Folder getStateLocation(String pluginId) => throw new UnimplementedError();
+}
+
+class _SourceFactoryProxy implements SourceFactory {
+ @override
+ final DartSdk dartSdk;
+
+ final FileRepository _fileRepository;
+
+ @override
+ AnalysisContext context;
+
+ _SourceFactoryProxy(this.dartSdk, this._fileRepository);
+
+ @override
+ SourceFactory clone() => new _SourceFactoryProxy(dartSdk, _fileRepository);
+
+ @override
+ Source forUri(String absoluteUri) {
+ Uri uri = Uri.parse(absoluteUri);
+ return forUri2(uri);
+ }
+
+ @override
+ Source forUri2(Uri absoluteUri) {
+ return new _SourceProxy(
+ absoluteUri, _fileRepository.pathForUri(absoluteUri, allocate: true));
+ }
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+
+ Source resolveUri(Source containingSource, String containedUri) {
+ // TODO(paulberry): re-use code from dependency_grapher_impl, and support
+ // SDK URI resolution logic.
+ String absoluteUri =
+ resolveRelativeUri(containingSource?.uri, Uri.parse(containedUri))
+ .toString();
+ return forUri(absoluteUri);
+ }
+
+ @override
+ Uri restoreUri(Source source) => source.uri;
+}
+
+class _SourceProxy extends BasicSource {
+ @override
+ final String fullName;
+
+ _SourceProxy(Uri uri, this.fullName) : super(uri);
+
+ int get modificationStamp => 0;
+
+ noSuchMethod(Invocation invocation) => unimplemented();
+}
diff --git a/pub/front_end/lib/src/libraries_reader.dart b/pub/front_end/lib/src/libraries_reader.dart
new file mode 100644
index 0000000..ded5d9f
--- /dev/null
+++ b/pub/front_end/lib/src/libraries_reader.dart
@@ -0,0 +1,201 @@
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:front_end/src/base/library_info.dart';
+
+/// Decodes the contents of the SDK's "libraries.dart" file.
+///
+/// Caller should pass in the unlinked summary of the libraries.dart file. This
+/// function will materialize the "libraries" constant based on information in
+/// the summary.
+///
+/// Note that this code is not intended to be fully general; it makes some
+/// assumptions about the structure of the "libraries.dart" file (such as what
+/// declarations are expected to be present in it, and the types of those
+/// declarations).
+Map<String, LibraryInfo> readLibraries(UnlinkedUnit librariesUnit) {
+ var constContext = new _ConstContext(librariesUnit.references);
+ for (var variable in librariesUnit.variables) {
+ if (!variable.isConst) continue;
+ constContext.topLevelConstants[variable.name] =
+ new _ConstVariable(variable.initializer.bodyExpr, constContext);
+ }
+ for (var cls in librariesUnit.classes) {
+ if (cls.name == 'Maturity') {
+ for (var field in cls.fields) {
+ if (!field.isConst) continue;
+ constContext.maturityConstants[field.name] =
+ new _ConstVariable(field.initializer.bodyExpr, constContext);
+ }
+ }
+ }
+ return constContext.topLevelConstants['libraries'].value;
+}
+
+/// Function type used to invoke a constructor based on dynamic information.
+///
+/// Caller supplies two callbacks ([positional] and [named]) which can be used
+/// to query the arguments passed to the constructor. These callbacks will
+/// return the requested argument if it was provided; otherwise they will return
+/// the supplied default value.
+typedef dynamic _Constructor(dynamic positional(int i, [dynamic defaultValue]),
+ dynamic named(String name, [dynamic defaultValue]));
+
+/// Contextual information used to evaluate constants in the "libraries.dart"
+/// file.
+class _ConstContext {
+ /// Top level constants in the "libraries.dart" file.
+ final topLevelConstants = <String, _ConstVariable>{};
+
+ /// Static constants in "libraries.dart"'s "Maturity" class.
+ final maturityConstants = <String, _ConstVariable>{};
+
+ /// References from the unlinked summary of the "libraries.dart" file.
+ final List<UnlinkedReference> references;
+
+ _ConstContext(this.references);
+}
+
+/// Information necessary to evaluate a single constant from the
+/// "libraries.dart" file.
+class _ConstVariable {
+ /// The constant expression from the unlinked summary.
+ final UnlinkedExpr expr;
+
+ /// Contextual information necessary to evaluate the constant.
+ final _ConstContext context;
+
+ /// The evaluated value, or `null` if it hasn't been evaluated yet.
+ dynamic _value;
+
+ _ConstVariable(this.expr, this.context);
+
+ /// Evaluate the constant (if necessary) and return it.
+ dynamic get value => _value ??= _materialize();
+
+ /// Find the constructor referred to by [entityRef] and return a function
+ /// which may be used to invoke it.
+ _Constructor _findConstructor(EntityRef entityRef) {
+ // This method is not fully general; we only support the constructor
+ // invocations that we expect to find in LibraryInfo.
+ assert(entityRef.implicitFunctionTypeIndices.isEmpty);
+ assert(entityRef.paramReference == 0);
+ assert(entityRef.syntheticParams.isEmpty);
+ assert(entityRef.syntheticReturnType == null);
+ assert(entityRef.typeArguments.isEmpty);
+ var reference = context.references[entityRef.reference];
+ assert(reference.prefixReference == 0);
+ switch (reference.name) {
+ case 'LibraryInfo':
+ return (dynamic positional(int i, [dynamic defaultValue]),
+ dynamic named(String name, [dynamic defaultValue])) =>
+ new LibraryInfo(positional(0),
+ categories: named('categories', ''),
+ dart2jsPath: named('dart2jsPath'),
+ dart2jsPatchPath: named('dart2jsPatchPath'),
+ implementation: named('implementation', false),
+ documented: named('documented', true),
+ maturity: named('maturity', Maturity.UNSPECIFIED),
+ platforms: named('platforms', DART2JS_PLATFORM | VM_PLATFORM));
+ case 'Maturity':
+ return (dynamic positional(int i, [dynamic defaultValue]),
+ dynamic named(String name, [dynamic defaultValue])) =>
+ new Maturity(positional(0), positional(1), positional(2));
+ default:
+ throw new UnimplementedError(
+ 'Unexpected constructor reference: ${reference.name}');
+ }
+ }
+
+ /// Compute the value referred to by [entityRef].
+ dynamic _findReference(EntityRef entityRef) {
+ // This method is not fully general; we only support the references that we
+ // expect to find in LibraryInfo.
+ assert(entityRef.implicitFunctionTypeIndices.isEmpty);
+ assert(entityRef.paramReference == 0);
+ assert(entityRef.syntheticParams.isEmpty);
+ assert(entityRef.syntheticReturnType == null);
+ assert(entityRef.typeArguments.isEmpty);
+ var reference = context.references[entityRef.reference];
+ if (reference.prefixReference == 0) {
+ return context.topLevelConstants[reference.name].value;
+ } else {
+ assert(reference.prefixReference != 0);
+ var prefixReference = context.references[reference.prefixReference];
+ assert(prefixReference.name == 'Maturity');
+ assert(prefixReference.prefixReference == 0);
+ return context.maturityConstants[reference.name].value;
+ }
+ }
+
+ /// Compute the value of the constant.
+ dynamic _materialize() {
+ var stack = [];
+ var stringIndex = 0;
+ var intIndex = 0;
+ var referenceIndex = 0;
+ List popItems(int count) {
+ var items = stack.sublist(stack.length - count, stack.length);
+ stack.length -= count;
+ return items;
+ }
+
+ for (var operation in expr.operations) {
+ switch (operation) {
+ case UnlinkedExprOperation.pushString:
+ stack.add(expr.strings[stringIndex++]);
+ break;
+ case UnlinkedExprOperation.invokeConstructor:
+ var namedArgumentList = popItems(expr.ints[intIndex++]);
+ var namedArguments = <String, dynamic>{};
+ for (var namedArgument in namedArgumentList) {
+ namedArguments[expr.strings[stringIndex++]] = namedArgument;
+ }
+ var positionalArguments = popItems(expr.ints[intIndex++]);
+ stack.add(_findConstructor(expr.references[referenceIndex++])(
+ (i, [defaultValue]) => i < positionalArguments.length
+ ? positionalArguments[i]
+ : defaultValue,
+ (name, [defaultValue]) => namedArguments.containsKey(name)
+ ? namedArguments[name]
+ : defaultValue));
+ break;
+ case UnlinkedExprOperation.makeUntypedMap:
+ var map = {};
+ var numKeyValuePairs = expr.ints[intIndex++];
+ var keyValueList = popItems(numKeyValuePairs * 2);
+ for (var i = 0; i < numKeyValuePairs; i++) {
+ map[keyValueList[2 * i]] = keyValueList[2 * i + 1];
+ }
+ stack.add(map);
+ break;
+ case UnlinkedExprOperation.pushReference:
+ stack.add(_findReference(expr.references[referenceIndex++]));
+ break;
+ case UnlinkedExprOperation.pushInt:
+ stack.add(expr.ints[intIndex++]);
+ break;
+ case UnlinkedExprOperation.pushFalse:
+ stack.add(false);
+ break;
+ case UnlinkedExprOperation.pushTrue:
+ stack.add(true);
+ break;
+ case UnlinkedExprOperation.bitOr:
+ var y = stack.removeLast();
+ var x = stack.removeLast();
+ stack.add(x | y);
+ break;
+ default:
+ throw new UnimplementedError(
+ 'Unexpected expression in libraries.dart: $operation');
+ }
+ }
+ assert(stringIndex == expr.strings.length);
+ assert(intIndex == expr.ints.length);
+ assert(referenceIndex == expr.references.length);
+ assert(stack.length == 1);
+ if (stack[0] == null) {
+ throw new StateError('Unexpected null constant in libraries.dart');
+ }
+ return stack[0];
+ }
+}
diff --git a/pub/front_end/lib/src/scanner/errors.dart b/pub/front_end/lib/src/scanner/errors.dart
index 4688ccd..bcf0c56 100644
--- a/pub/front_end/lib/src/scanner/errors.dart
+++ b/pub/front_end/lib/src/scanner/errors.dart
@@ -2,307 +2,7 @@
// 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.
-import 'package:analyzer/error/error.dart'
- show errorCodeValues, errorCodeByUniqueName;
-
-/**
- * An error code associated with an [AnalysisError].
- *
- * Generally, we want to provide messages that consist of three sentences. From
- * the user's perspective these sentences should explain:
- *
- * 1. what is wrong,
- * 2. why is it wrong, and
- * 3. how do I fix it.
- *
- * However, we combine the first two in the [message] and the last in the
- * [correction].
- *
- * When composing messages (including correction messages) keep the following
- * guidelines in mind.
- *
- * 1. The message should be a complete sentence starting with an uppercase
- * letter, and ending with a period.
- *
- * 2. Reserved words and embedded identifiers should be in single quotes, so
- * prefer double quotes for the complete message. For example,
- * ```
- * "The class '{0}' can't use 'super'."
- * ```
- * Notice that the word 'class' in the preceding message is not quoted as it
- * refers to the concept 'class', not the reserved word. On the other hand,
- * 'super' refers to the reserved word. Do not quote 'null' and numeric literals.
- *
- * 3. Do not try to compose messages, as it can make translating them hard.
- *
- * 4. Try to keep the error messages short, but informative.
- *
- * 5. Use simple words and terminology, assume the reader of the message doesn't
- * have an advanced degree in math, and that English is not the reader's native
- * language. Do not assume any formal computer science training. For example, do
- * not use Latin abbreviations (prefer "that is" over "i.e.", and "for example"
- * over "e.g."). Also avoid phrases such as "if and only if" and "iff"; that
- * level of precision is unnecessary.
- *
- * 6. Prefer contractions when they are in common use, for example, prefer
- * "can't" over "cannot". Using "cannot", "must not", "shall not", etc. is
- * off-putting to people new to programming.
- *
- * 7. Use common terminology, preferably from the Dart Language Specification.
- * This increases the user's chance of finding a good explanation on the web.
- *
- * 8. Do not try to be cute or funny. It is extremely frustrating to work on a
- * product that crashes with a "tongue-in-cheek" message, especially if you did
- * not want to use this product to begin with.
- *
- * 9. Do not lie, that is, do not write error messages containing phrases like
- * "can't happen". If the user ever saw this message, it would be a lie. Prefer
- * messages like: "Internal error: This function should not be called when 'x'
- * is null.".
- *
- * 10. Prefer to not use the imperative tone. That is, the message should not
- * sound accusing or like it is ordering the user around. The computer should
- * describe the problem, not criticize the user for violating the specification.
- */
-abstract class ErrorCode {
- /**
- * Engine error code values.
- */
- @deprecated
- static const List<ErrorCode> values = errorCodeValues;
-
- /**
- * An empty list of error codes.
- */
- @deprecated
- static const List<ErrorCode> EMPTY_LIST = const <ErrorCode>[];
-
- /**
- * The name of the error code.
- */
- final String name;
-
- /**
- * The template used to create the message to be displayed for this error. The
- * message should indicate what is wrong and why it is wrong.
- */
- final String message;
-
- /**
- * The template used to create the correction to be displayed for this error,
- * or `null` if there is no correction information for this error. The
- * correction should indicate how the user can fix the error.
- */
- final String correction;
-
- /**
- * Initialize a newly created error code to have the given [name]. The message
- * associated with the error will be created from the given [message]
- * template. The correction associated with the error will be created from the
- * given [correction] template.
- */
- const ErrorCode(this.name, this.message, [this.correction]);
-
- /**
- * The severity of the error.
- */
- ErrorSeverity get errorSeverity;
-
- /**
- * The type of the error.
- */
- ErrorType get type;
-
- /**
- * The unique name of this error code.
- */
- String get uniqueName => "$runtimeType.$name";
-
- @override
- String toString() => uniqueName;
-
- /**
- * Return the [ErrorCode] with the given [uniqueName], or `null` if not
- * found.
- */
- @deprecated
- static ErrorCode byUniqueName(String uniqueName) =>
- errorCodeByUniqueName(uniqueName);
-}
-
-/**
- * The severity of an [ErrorCode].
- */
-class ErrorSeverity implements Comparable<ErrorSeverity> {
- /**
- * The severity representing a non-error. This is never used for any error
- * code, but is useful for clients.
- */
- static const ErrorSeverity NONE = const ErrorSeverity('NONE', 0, " ", "none");
-
- /**
- * The severity representing an informational level analysis issue.
- */
- static const ErrorSeverity INFO = const ErrorSeverity('INFO', 1, "I", "info");
-
- /**
- * The severity representing a warning. Warnings can become errors if the `-Werror` command
- * line flag is specified.
- */
- static const ErrorSeverity WARNING =
- const ErrorSeverity('WARNING', 2, "W", "warning");
-
- /**
- * The severity representing an error.
- */
- static const ErrorSeverity ERROR =
- const ErrorSeverity('ERROR', 3, "E", "error");
-
- static const List<ErrorSeverity> values = const [NONE, INFO, WARNING, ERROR];
-
- /**
- * The name of this error code.
- */
- final String name;
-
- /**
- * The ordinal value of the error code.
- */
- final int ordinal;
-
- /**
- * The name of the severity used when producing machine output.
- */
- final String machineCode;
-
- /**
- * The name of the severity used when producing readable output.
- */
- final String displayName;
-
- /**
- * Initialize a newly created severity with the given names.
- */
- const ErrorSeverity(
- this.name, this.ordinal, this.machineCode, this.displayName);
-
- @override
- int get hashCode => ordinal;
-
- @override
- int compareTo(ErrorSeverity other) => ordinal - other.ordinal;
-
- /**
- * Return the severity constant that represents the greatest severity.
- */
- ErrorSeverity max(ErrorSeverity severity) =>
- this.ordinal >= severity.ordinal ? this : severity;
-
- @override
- String toString() => name;
-}
-
-/**
- * The type of an [ErrorCode].
- */
-class ErrorType implements Comparable<ErrorType> {
- /**
- * Task (todo) comments in user code.
- */
- static const ErrorType TODO = const ErrorType('TODO', 0, ErrorSeverity.INFO);
-
- /**
- * Extra analysis run over the code to follow best practices, which are not in
- * the Dart Language Specification.
- */
- static const ErrorType HINT = const ErrorType('HINT', 1, ErrorSeverity.INFO);
-
- /**
- * Compile-time errors are errors that preclude execution. A compile time
- * error must be reported by a Dart compiler before the erroneous code is
- * executed.
- */
- static const ErrorType COMPILE_TIME_ERROR =
- const ErrorType('COMPILE_TIME_ERROR', 2, ErrorSeverity.ERROR);
-
- /**
- * Checked mode compile-time errors are errors that preclude execution in
- * checked mode.
- */
- static const ErrorType CHECKED_MODE_COMPILE_TIME_ERROR = const ErrorType(
- 'CHECKED_MODE_COMPILE_TIME_ERROR', 3, ErrorSeverity.ERROR);
-
- /**
- * Static warnings are those warnings reported by the static checker. They
- * have no effect on execution. Static warnings must be provided by Dart
- * compilers used during development.
- */
- static const ErrorType STATIC_WARNING =
- const ErrorType('STATIC_WARNING', 4, ErrorSeverity.WARNING);
-
- /**
- * Many, but not all, static warnings relate to types, in which case they are
- * known as static type warnings.
- */
- static const ErrorType STATIC_TYPE_WARNING =
- const ErrorType('STATIC_TYPE_WARNING', 5, ErrorSeverity.WARNING);
-
- /**
- * Syntactic errors are errors produced as a result of input that does not
- * conform to the grammar.
- */
- static const ErrorType SYNTACTIC_ERROR =
- const ErrorType('SYNTACTIC_ERROR', 6, ErrorSeverity.ERROR);
-
- /**
- * Lint warnings describe style and best practice recommendations that can be
- * used to formalize a project's style guidelines.
- */
- static const ErrorType LINT = const ErrorType('LINT', 7, ErrorSeverity.INFO);
-
- static const List<ErrorType> values = const [
- TODO,
- HINT,
- COMPILE_TIME_ERROR,
- CHECKED_MODE_COMPILE_TIME_ERROR,
- STATIC_WARNING,
- STATIC_TYPE_WARNING,
- SYNTACTIC_ERROR,
- LINT
- ];
-
- /**
- * The name of this error type.
- */
- final String name;
-
- /**
- * The ordinal value of the error type.
- */
- final int ordinal;
-
- /**
- * The severity of this type of error.
- */
- final ErrorSeverity severity;
-
- /**
- * Initialize a newly created error type to have the given [name] and
- * [severity].
- */
- const ErrorType(this.name, this.ordinal, this.severity);
-
- String get displayName => name.toLowerCase().replaceAll('_', ' ');
-
- @override
- int get hashCode => ordinal;
-
- @override
- int compareTo(ErrorType other) => ordinal - other.ordinal;
-
- @override
- String toString() => name;
-}
+import 'package:front_end/src/base/errors.dart';
/**
* The error codes used for errors detected by the scanner.
diff --git a/pub/front_end/lib/src/scanner/reader.dart b/pub/front_end/lib/src/scanner/reader.dart
index ee9d6c0..b079139 100644
--- a/pub/front_end/lib/src/scanner/reader.dart
+++ b/pub/front_end/lib/src/scanner/reader.dart
@@ -42,3 +42,92 @@
*/
int peek();
}
+
+/**
+ * A [CharacterReader] that reads characters from a character sequence.
+ */
+class CharSequenceReader implements CharacterReader {
+ /**
+ * The sequence from which characters will be read.
+ */
+ final String _sequence;
+
+ /**
+ * The number of characters in the string.
+ */
+ int _stringLength;
+
+ /**
+ * The index, relative to the string, of the next character to be read.
+ */
+ int _charOffset;
+
+ /**
+ * Initialize a newly created reader to read the characters in the given
+ * [_sequence].
+ */
+ CharSequenceReader(this._sequence) {
+ this._stringLength = _sequence.length;
+ this._charOffset = 0;
+ }
+
+ @override
+ int get offset => _charOffset - 1;
+
+ @override
+ void set offset(int offset) {
+ _charOffset = offset + 1;
+ }
+
+ @override
+ int advance() {
+ if (_charOffset >= _stringLength) {
+ return -1;
+ }
+ return _sequence.codeUnitAt(_charOffset++);
+ }
+
+ @override
+ String getString(int start, int endDelta) =>
+ _sequence.substring(start, _charOffset + endDelta);
+
+ @override
+ int peek() {
+ if (_charOffset >= _stringLength) {
+ return -1;
+ }
+ return _sequence.codeUnitAt(_charOffset);
+ }
+}
+
+/**
+ * A [CharacterReader] that reads characters from a character sequence, but adds
+ * a delta when reporting the current character offset so that the character
+ * sequence can be a subsequence from a larger sequence.
+ */
+class SubSequenceReader extends CharSequenceReader {
+ /**
+ * The offset from the beginning of the file to the beginning of the source
+ * being scanned.
+ */
+ final int _offsetDelta;
+
+ /**
+ * Initialize a newly created reader to read the characters in the given
+ * [sequence]. The [_offsetDelta] is the offset from the beginning of the file
+ * to the beginning of the source being scanned
+ */
+ SubSequenceReader(String sequence, this._offsetDelta) : super(sequence);
+
+ @override
+ int get offset => _offsetDelta + super.offset;
+
+ @override
+ void set offset(int offset) {
+ super.offset = offset - _offsetDelta;
+ }
+
+ @override
+ String getString(int start, int endDelta) =>
+ super.getString(start - _offsetDelta, endDelta);
+}
diff --git a/pub/front_end/lib/src/scanner/token.dart b/pub/front_end/lib/src/scanner/token.dart
index 10d4924..abd0194 100644
--- a/pub/front_end/lib/src/scanner/token.dart
+++ b/pub/front_end/lib/src/scanner/token.dart
@@ -6,11 +6,10 @@
* Defines the tokens that are produced by the scanner, used by the parser, and
* referenced from the [AST structure](ast.dart).
*/
-
import 'dart:collection';
+import 'package:front_end/src/base/syntactic_entity.dart';
import 'package:front_end/src/scanner/string_utilities.dart';
-import 'package:front_end/src/scanner/syntactic_entity.dart';
/**
* The opening half of a grouping pair of tokens. This is used for curly
@@ -167,6 +166,9 @@
static const Keyword CONTINUE = const Keyword._('CONTINUE', "continue");
+ static const Keyword COVARIANT =
+ const Keyword._('COVARIANT', "covariant", true);
+
static const Keyword DEFAULT = const Keyword._('DEFAULT', "default");
static const Keyword DEFERRED = const Keyword._('DEFERRED', "deferred", true);
@@ -258,6 +260,7 @@
CLASS,
CONST,
CONTINUE,
+ COVARIANT,
DEFAULT,
DEFERRED,
DO,
diff --git a/pub/front_end/pubspec.yaml b/pub/front_end/pubspec.yaml
index 8b75e23..5f15860 100644
--- a/pub/front_end/pubspec.yaml
+++ b/pub/front_end/pubspec.yaml
@@ -1,18 +1,18 @@
name: front_end
-version: 0.1.0-alpha.0
+version: 0.1.0-alpha.1
author: Dart Team <misc@dartlang.org>
description: Front end for compilation of Dart code.
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/front_end
environment:
sdk: '>=1.12.0 <2.0.0'
dependencies:
- analyzer: '^0.29.0'
+ analyzer: 0.30.0-alpha.1
path: '^1.3.9'
source_span: '^1.2.3'
dev_dependencies:
# TODO(sigmund): update to a version constraint once we roll the latest kernel
# to the repo.
- kernel: {path: ../../third_party/pkg/kernel}
+ kernel: {path: ../../pkg/kernel}
package_config: '^1.0.0'
test: ^0.12.0
test_reflective_loader: ^0.1.0
diff --git a/pub/front_end/tool/example.dart b/pub/front_end/tool/example.dart
new file mode 100644
index 0000000..d81f437
--- /dev/null
+++ b/pub/front_end/tool/example.dart
@@ -0,0 +1,29 @@
+import 'dart:async';
+
+import 'package:front_end/kernel_generator.dart';
+import 'package:front_end/compiler_options.dart';
+import 'package:kernel/binary/ast_to_binary.dart';
+import 'package:kernel/kernel.dart' show Program;
+
+Future dumpToSink(Program program, StreamSink<List<int>> sink) {
+ new BinaryPrinter(sink).writeProgramFile(program);
+ return sink.close();
+}
+
+Future kernelToSink(Uri entry, StreamSink<List<int>> sink) async {
+ var program = await kernelForProgram(
+ entry,
+ new CompilerOptions()
+ ..sdkRoot = new Uri.file('sdk')
+ ..packagesFileUri = new Uri.file('.packages')
+ ..onError = (e) => print(e.message));
+
+ await dumpToSink(program, sink);
+}
+
+main(args) async {
+ kernelToSink(
+ Uri.base.resolve(args[0]),
+ // TODO(sigmund,hausner): define memory type where to dump binary data.
+ new StreamController<List<int>>.broadcast().sink);
+}
diff --git a/pub/front_end/tool/perf.dart b/pub/front_end/tool/perf.dart
index ec306ca..7c4e4cd 100644
--- a/pub/front_end/tool/perf.dart
+++ b/pub/front_end/tool/perf.dart
@@ -6,171 +6,91 @@
library front_end.tool.perf;
import 'dart:async';
-import 'dart:io' show exit, stderr;
+import 'dart:io' show exit;
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' show ResourceUriResolver;
import 'package:analyzer/file_system/physical_file_system.dart'
show PhysicalResourceProvider;
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/context/builder.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' show FolderBasedDartSdk;
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
-import 'package:kernel/analyzer/loader.dart';
-import 'package:kernel/kernel.dart';
+import 'package:analyzer/src/summary/format.dart';
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:analyzer/src/summary/link.dart';
+import 'package:analyzer/src/summary/summarize_ast.dart';
+import 'package:front_end/compiler_options.dart';
+import 'package:front_end/kernel_generator.dart';
+import 'package:front_end/src/scanner/reader.dart';
+import 'package:front_end/src/scanner/scanner.dart';
+import 'package:front_end/src/scanner/token.dart';
+import 'package:kernel/kernel.dart' hide Source;
import 'package:package_config/discovery.dart';
-/// Cumulative total number of chars scanned.
-int scanTotalChars = 0;
-
-/// Cumulative time spent scanning.
-Stopwatch scanTimer = new Stopwatch();
-
-/// Factory to load and resolve app, packages, and sdk sources.
-SourceFactory sources;
-
main(List<String> args) async {
// TODO(sigmund): provide sdk folder as well.
if (args.length < 2) {
print('usage: perf.dart <bench-id> <entry.dart>');
exit(1);
}
- var totalTimer = new Stopwatch()..start();
var bench = args[0];
var entryUri = Uri.base.resolve(args[1]);
await setup(entryUri);
+ Set<Source> files = scanReachableFiles(entryUri);
var handlers = {
- 'scan': () async {
- Set<Source> files = scanReachableFiles(entryUri);
- // TODO(sigmund): replace the warmup with instrumented snapshots.
- for (int i = 0; i < 10; i++) scanFiles(files);
- },
- 'parse': () async {
- Set<Source> files = scanReachableFiles(entryUri);
- // TODO(sigmund): replace the warmup with instrumented snapshots.
- for (int i = 0; i < 10; i++) parseFiles(files);
- },
+ 'scan': () async => scanFiles(files),
+ 'parse': () async => parseFiles(files),
'kernel_gen_e2e': () async {
- // TODO(sigmund): remove. This is used to compute the input size, we
- // should extract input size from frontend instead.
- scanReachableFiles(entryUri);
- // TODO(sigmund): replace this warmup. Note that for very large programs,
- // the GC pressure on the VM seems to make this worse with time (maybe we
- // are leaking memory?). That's why we run it twice and not 10 times.
- for (int i = 0; i < 2; i++) await generateKernel(entryUri);
+ await generateKernel(entryUri, useSdkSummary: false);
},
+ 'kernel_gen_e2e_sum': () async {
+ await generateKernel(entryUri, useSdkSummary: true, compileSdk: false);
+ },
+ 'unlinked_summarize': () async => summarize(files),
+ 'unlinked_summarize_from_sources': () async => summarize(files),
+ 'prelinked_summarize': () async => summarize(files, prelink: true),
+ 'linked_summarize': () async => summarize(files, link: true),
};
var handler = handlers[bench];
if (handler == null) {
- // TODO(sigmund): implement the remaining benchmarks.
print('unsupported bench-id: $bench. Please specify one of the following: '
'${handlers.keys.join(", ")}');
exit(1);
}
- await handler();
- totalTimer.stop();
- report("total", totalTimer.elapsedMicroseconds);
-}
-
-/// Sets up analyzer to be able to load and resolve app, packages, and sdk
-/// sources.
-Future setup(Uri entryUri) async {
- var provider = PhysicalResourceProvider.INSTANCE;
- var packageMap = new ContextBuilder(provider, null, null)
- .convertPackagesToMap(await findPackages(entryUri));
- sources = new SourceFactory([
- new ResourceUriResolver(provider),
- new PackageMapUriResolver(provider, packageMap),
- new DartUriResolver(
- new FolderBasedDartSdk(provider, provider.getFolder("sdk"))),
- ]);
-}
-
-/// Load and scans all files we need to process: files reachable from the
-/// entrypoint and all core libraries automatically included by the VM.
-Set<Source> scanReachableFiles(Uri entryUri) {
- var files = new Set<Source>();
- var loadTimer = new Stopwatch()..start();
- collectSources(sources.forUri2(entryUri), files);
-
- var libs = [
- "dart:async",
- "dart:collection",
- "dart:convert",
- "dart:core",
- "dart:developer",
- "dart:_internal",
- "dart:isolate",
- "dart:math",
- "dart:mirrors",
- "dart:typed_data",
- "dart:io"
- ];
-
- for (var lib in libs) {
- collectSources(sources.forUri(lib), files);
+ // TODO(sigmund): replace the warmup with instrumented snapshots.
+ int iterations = bench.contains('kernel_gen') ? 2 : 10;
+ for (int i = 0; i < iterations; i++) {
+ var totalTimer = new Stopwatch()..start();
+ print('== iteration $i');
+ await handler();
+ totalTimer.stop();
+ report('total', totalTimer.elapsedMicroseconds);
}
-
- loadTimer.stop();
-
- print('input size: ${scanTotalChars} chars');
- var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
- report("load", loadTime);
- report("scan", scanTimer.elapsedMicroseconds);
- return files;
}
-/// Scans every file in [files] and reports the time spent doing so.
-void scanFiles(Set<Source> files) {
- // The code below will record again how many chars are scanned and how long it
- // takes to scan them, even though we already did so in [scanReachableFiles].
- // Recording and reporting this twice is unnecessary, but we do so for now to
- // validate that the results are consistent.
- scanTimer = new Stopwatch();
- var old = scanTotalChars;
- scanTotalChars = 0;
- for (var source in files) {
- tokenize(source);
- }
+/// Cumulative time spent parsing.
+Stopwatch parseTimer = new Stopwatch();
- // Report size and scanning time again. See discussion above.
- if (old != scanTotalChars) print('input size changed? ${old} chars');
- report("scan", scanTimer.elapsedMicroseconds);
-}
+/// Cumulative time spent building unlinked summaries.
+Stopwatch unlinkedSummarizeTimer = new Stopwatch();
-/// Parses every file in [files] and reports the time spent doing so.
-void parseFiles(Set<Source> files) {
- // The code below will record again how many chars are scanned and how long it
- // takes to scan them, even though we already did so in [scanReachableFiles].
- // Recording and reporting this twice is unnecessary, but we do so for now to
- // validate that the results are consistent.
- scanTimer = new Stopwatch();
- var old = scanTotalChars;
- scanTotalChars = 0;
- var parseTimer = new Stopwatch()..start();
- for (var source in files) {
- parseFull(source);
- }
- parseTimer.stop();
+/// Cumulative time spent scanning.
+Stopwatch scanTimer = new Stopwatch();
- // Report size and scanning time again. See discussion above.
- if (old != scanTotalChars) print('input size changed? ${old} chars');
- report("scan", scanTimer.elapsedMicroseconds);
+/// Size of all sources.
+int inputSize = 0;
- var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
- report("parse", pTime);
-}
+/// Factory to load and resolve app, packages, and sdk sources.
+SourceFactory sources;
/// Add to [files] all sources reachable from [start].
void collectSources(Source start, Set<Source> files) {
@@ -184,6 +104,87 @@
}
}
+Future<Program> generateKernel(Uri entryUri,
+ {bool useSdkSummary: false, bool compileSdk: true}) async {
+ // TODO(sigmund): this is here only to compute the input size,
+ // we should extract the input size from the frontend instead.
+ scanReachableFiles(entryUri);
+
+ var dartkTimer = new Stopwatch()..start();
+ // TODO(sigmund): add a constructor with named args to compiler options.
+ var options = new CompilerOptions()
+ ..strongMode = false
+ ..compileSdk = compileSdk
+ ..packagesFileUri = new Uri.file('.packages')
+ ..onError = ((e) => print('${e.message}'));
+ if (useSdkSummary) {
+ // TODO(sigmund): adjust path based on the benchmark runner architecture.
+ // Possibly let the runner make the file available at an architecture
+ // independent location.
+ options.sdkSummary =
+ new Uri.file('out/ReleaseX64/dart-sdk/lib/_internal/spec.sum');
+ } else {
+ options.sdkRoot = new Uri.file('sdk');
+ }
+ Program program = await kernelForProgram(entryUri, options);
+ dartkTimer.stop();
+ var suffix = useSdkSummary ? '_sum' : '';
+ report('kernel_gen_e2e${suffix}', dartkTimer.elapsedMicroseconds);
+ return program;
+}
+
+/// Generates unlinkmed summaries for all files in [files], and returns them in
+/// an [_UnlinkedSummaries] container.
+_UnlinkedSummaries generateUnlinkedSummaries(Set<Source> files) {
+ var unlinkedSummaries = new _UnlinkedSummaries();
+ for (var source in files) {
+ unlinkedSummaries.summariesByUri[source.uri.toString()] =
+ unlinkedSummarize(source);
+ }
+ return unlinkedSummaries;
+}
+
+/// Generates unlinked summaries for every file in [files] and, if requested via
+/// [prelink] or [link], generates the pre-linked and linked summaries as well.
+///
+/// This function also prints a report of the time spent on each action.
+void summarize(Set<Source> files, {bool prelink: false, bool link: false}) {
+ scanTimer = new Stopwatch();
+ parseTimer = new Stopwatch();
+ unlinkedSummarizeTimer = new Stopwatch();
+ var unlinkedSummaries = generateUnlinkedSummaries(files);
+ report('scan', scanTimer.elapsedMicroseconds);
+ report('parse', parseTimer.elapsedMicroseconds);
+ report('unlinked_summarize', unlinkedSummarizeTimer.elapsedMicroseconds);
+ report(
+ 'unlinked_summarize_from_sources',
+ unlinkedSummarizeTimer.elapsedMicroseconds +
+ parseTimer.elapsedMicroseconds +
+ scanTimer.elapsedMicroseconds);
+
+ if (prelink || link) {
+ var prelinkTimer = new Stopwatch()..start();
+ var prelinkedLibraries = prelinkSummaries(files, unlinkedSummaries);
+ prelinkTimer.stop();
+ report('prelinked_summarize', prelinkTimer.elapsedMicroseconds);
+
+ if (link) {
+ var linkTimer = new Stopwatch()..start();
+ LinkedLibrary getDependency(String uri) {
+ // getDependency should never be called because all dependencies are
+ // present in [prelinkedLibraries].
+ print('Warning: getDependency called for: $uri');
+ return null;
+ }
+
+ relink(prelinkedLibraries, getDependency, unlinkedSummaries.getUnit,
+ true /*strong*/);
+ linkTimer.stop();
+ report('linked_summarize', linkTimer.elapsedMicroseconds);
+ }
+ }
+}
+
/// Uses the diet-parser to parse only directives in [source].
CompilationUnit parseDirectives(Source source) {
var token = tokenize(source);
@@ -191,55 +192,158 @@
return parser.parseDirectives(token);
}
+/// Parses every file in [files] and reports the time spent doing so.
+void parseFiles(Set<Source> files) {
+ scanTimer = new Stopwatch();
+ parseTimer = new Stopwatch();
+ for (var source in files) {
+ parseFull(source);
+ }
+
+ report('scan', scanTimer.elapsedMicroseconds);
+ report('parse', parseTimer.elapsedMicroseconds);
+}
+
/// Parse the full body of [source] and return it's compilation unit.
CompilationUnit parseFull(Source source) {
var token = tokenize(source);
+ parseTimer.start();
var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER);
- return parser.parseCompilationUnit(token);
+ var unit = parser.parseCompilationUnit(token);
+ parseTimer.stop();
+ return unit;
+}
+
+/// Prelinks all the summaries for [files], using [unlinkedSummaries] to obtain
+/// their unlinked summaries.
+///
+/// The return value is suitable for passing to the summary linker.
+Map<String, LinkedLibraryBuilder> prelinkSummaries(
+ Set<Source> files, _UnlinkedSummaries unlinkedSummaries) {
+ Set<String> libraryUris = files.map((source) => '${source.uri}').toSet();
+
+ String getDeclaredVariable(String s) => null;
+ var prelinkedLibraries =
+ setupForLink(libraryUris, unlinkedSummaries.getUnit, getDeclaredVariable);
+ return prelinkedLibraries;
+}
+
+/// Report that metric [name] took [time] micro-seconds to process
+/// [inputSize] characters.
+void report(String name, int time) {
+ var sb = new StringBuffer();
+ var padding = ' ' * (20 - name.length);
+ sb.write('$name:$padding $time us, ${time ~/ 1000} ms');
+ var invSpeed = (time * 1000 / inputSize).toStringAsFixed(2);
+ sb.write(', $invSpeed ns/char');
+ print('$sb');
+}
+
+/// Scans every file in [files] and reports the time spent doing so.
+void scanFiles(Set<Source> files) {
+ // `tokenize` records how many chars are scanned and how long it takes to scan
+ // them. As this function is called repeatedly when running as a benchmark, we
+ // make sure to clear the data and compute it again every time.
+ scanTimer = new Stopwatch();
+ for (var source in files) {
+ tokenize(source);
+ }
+
+ report('scan', scanTimer.elapsedMicroseconds);
+}
+
+/// Load and scans all files we need to process: files reachable from the
+/// entrypoint and all core libraries automatically included by the VM.
+Set<Source> scanReachableFiles(Uri entryUri) {
+ var files = new Set<Source>();
+ var loadTimer = new Stopwatch()..start();
+ scanTimer = new Stopwatch();
+ collectSources(sources.forUri2(entryUri), files);
+
+ var libs = [
+ 'dart:async',
+ 'dart:collection',
+ 'dart:convert',
+ 'dart:core',
+ 'dart:developer',
+ 'dart:_internal',
+ 'dart:isolate',
+ 'dart:math',
+ 'dart:mirrors',
+ 'dart:typed_data',
+ 'dart:io'
+ ];
+
+ for (var lib in libs) {
+ collectSources(sources.forUri(lib), files);
+ }
+
+ loadTimer.stop();
+
+ inputSize = 0;
+ for (var s in files) inputSize += s.contents.data.length;
+ print('input size: ${inputSize} chars');
+ var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
+ report('load', loadTime);
+ report('scan', scanTimer.elapsedMicroseconds);
+ return files;
+}
+
+/// Sets up analyzer to be able to load and resolve app, packages, and sdk
+/// sources.
+Future setup(Uri entryUri) async {
+ var provider = PhysicalResourceProvider.INSTANCE;
+ var packageMap = new ContextBuilder(provider, null, null)
+ .convertPackagesToMap(await findPackages(entryUri));
+ sources = new SourceFactory([
+ new ResourceUriResolver(provider),
+ new PackageMapUriResolver(provider, packageMap),
+ new DartUriResolver(
+ new FolderBasedDartSdk(provider, provider.getFolder('sdk'))),
+ ]);
}
/// Scan [source] and return the first token produced by the scanner.
Token tokenize(Source source) {
scanTimer.start();
- var contents = source.contents.data;
- scanTotalChars += contents.length;
// TODO(sigmund): is there a way to scan from a random-access-file without
// first converting to String?
- var scanner = new Scanner(source, new CharSequenceReader(contents),
- AnalysisErrorListener.NULL_LISTENER)..preserveComments = false;
+ var scanner = new _Scanner(source.contents.data);
var token = scanner.tokenize();
scanTimer.stop();
return token;
}
-/// Report that metric [name] took [time] micro-seconds to process
-/// [scanTotalChars] characters.
-void report(String name, int time) {
- var sb = new StringBuffer();
- sb.write('$name: $time us, ${time ~/ 1000} ms');
- sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms');
- print('$sb');
+UnlinkedUnitBuilder unlinkedSummarize(Source source) {
+ var unit = parseFull(source);
+ unlinkedSummarizeTimer.start();
+ var unlinkedUnit = serializeAstUnlinked(unit);
+ unlinkedSummarizeTimer.stop();
+ return unlinkedUnit;
}
-// TODO(sigmund): replace this once kernel is produced by the frontend directly.
-Future<Program> generateKernel(Uri entryUri) async {
- var dartkTimer = new Stopwatch()..start();
- var options = new DartOptions(strongMode: false, sdk: 'sdk');
- var packages =
- await createPackages(options.packagePath, discoveryPath: entryUri.path);
- var repository = new Repository();
- DartLoader loader = new DartLoader(repository, options, packages);
+/// Simple container for a mapping from URI string to an unlinked summary.
+class _UnlinkedSummaries {
+ final summariesByUri = <String, UnlinkedUnit>{};
- Program program = loader.loadProgram(entryUri.path);
- List errors = loader.errors;
- if (errors.isNotEmpty) {
- const int errorLimit = 100;
- stderr.writeln(errors.take(errorLimit).join('\n'));
- if (errors.length > errorLimit) {
- stderr.writeln('[error] ${errors.length - errorLimit} errors not shown');
+ /// Get the unlinked summary for the given URI, and report a warning if it
+ /// can't be found.
+ UnlinkedUnit getUnit(String uri) {
+ var result = summariesByUri[uri];
+ if (result == null) {
+ print('Warning: no summary found for: $uri');
}
+ return result;
}
- dartkTimer.stop();
- report("kernel_gen_e2e", dartkTimer.elapsedMicroseconds);
- return program;
+}
+
+class _Scanner extends Scanner {
+ _Scanner(String contents) : super(new CharSequenceReader(contents)) {
+ preserveComments = false;
+ }
+
+ @override
+ void reportError(errorCode, int offset, List<Object> arguments) {
+ // ignore errors.
+ }
}
diff --git a/pub/front_end/tool/perf_test.dart b/pub/front_end/tool/perf_test.dart
index 5abc06f..66093ed 100644
--- a/pub/front_end/tool/perf_test.dart
+++ b/pub/front_end/tool/perf_test.dart
@@ -8,4 +8,4 @@
import 'perf.dart' as m;
-main() => print('done ${m.scanTotalChars}');
+main() => print('done ${m.inputSize}');
diff --git a/pub/isolate/BUILD.gn b/pub/isolate/BUILD.gn
index f1eb5f8..d9fcfc4 100644
--- a/pub/isolate/BUILD.gn
+++ b/pub/isolate/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for isolate-0.2.3
+# This file is generated by importer.py for isolate-1.0.0
import("//build/dart/dart_package.gni")
diff --git a/pub/isolate/CHANGELOG.md b/pub/isolate/CHANGELOG.md
index cadcf76..f69a8aa 100644
--- a/pub/isolate/CHANGELOG.md
+++ b/pub/isolate/CHANGELOG.md
@@ -1,22 +1,26 @@
-###0.2.3
+## 1.0.0
+
+* Change to using `package:test` for testing.
+
+## 0.2.3
* Fixed strong mode analysis errors.
* Migrated tests to package:test.
-###0.2.2
+## 0.2.2
* Made `Isolate.kill` parameter `priority` a named parameter.
-###0.2.1
+## 0.2.1
* Fixed spelling in a number of doc comments and the README.
-###0.2.0
+## 0.2.0
* Renamed library `isolaterunner.dart` to `isolate_runner.dart`.
* Renamed library `loadbalancer.dart' to `load_balancer.dart`.
-###0.1.0
+## 0.1.0
* Initial version
* Adds IsolateRunner as a helper around Isolate.
diff --git a/pub/isolate/pubspec.yaml b/pub/isolate/pubspec.yaml
index f68a76e..ae9da40 100644
--- a/pub/isolate/pubspec.yaml
+++ b/pub/isolate/pubspec.yaml
@@ -1,5 +1,5 @@
name: isolate
-version: 0.2.3
+version: 1.0.0
author: Dart Team <misc@dartlang.org>
description: Utility functions and classes related to the 'dart:isolate' library.
homepage: https://github.com/dart-lang/isolate
diff --git a/pub/usage/.analysis_options b/pub/usage/.analysis_options
index c702eea..a10d4c5 100644
--- a/pub/usage/.analysis_options
+++ b/pub/usage/.analysis_options
@@ -1,7 +1,2 @@
analyzer:
strong-mode: true
- language:
- enableConditionalDirectives: true
- exclude:
- - example/flutter_example/**
- - lib/src/usage_impl_flutter.dart
diff --git a/pub/usage/.gitignore b/pub/usage/.gitignore
index f8192d2..52f0c5b 100644
--- a/pub/usage/.gitignore
+++ b/pub/usage/.gitignore
@@ -1,8 +1,3 @@
-build/
-doc/api/
.packages
-packages
-.buildlog
-pubspec.lock
-.settings/
.pub/
+pubspec.lock
diff --git a/pub/usage/.project b/pub/usage/.project
deleted file mode 100644
index 5e76d1e..0000000
--- a/pub/usage/.project
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>usage</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.google.dart.tools.core.dartBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.google.dart.tools.core.dartNature</nature>
- </natures>
- <filteredResources>
- <filter>
- <id>1418770673943</id>
- <name></name>
- <type>30</type>
- <matcher>
- <id>com.google.dart.tools.core.packagesFolderMatcher</id>
- </matcher>
- </filter>
- </filteredResources>
-</projectDescription>
diff --git a/pub/usage/.travis.yml b/pub/usage/.travis.yml
index 35def79..9e27d1f 100644
--- a/pub/usage/.travis.yml
+++ b/pub/usage/.travis.yml
@@ -1,11 +1,3 @@
language: dart
-dart:
- - dev
sudo: false
-
-# before_install:
-# - "export CHROME_ARGS=--no-sandbox"
-# - "export DISPLAY=:99.0"
-# - "sh -e /etc/init.d/xvfb start"
-
script: ./tool/travis.sh
diff --git a/pub/usage/.vscode/launch.json b/pub/usage/.vscode/launch.json
new file mode 100644
index 0000000..3e69379
--- /dev/null
+++ b/pub/usage/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "name": "Dart command line",
+ "type": "dart-cli",
+ "request": "launch",
+ "cwd": "${workspaceRoot}",
+ "debugSettings": "${command.debugSettings}",
+ "program": "${workspaceRoot}/example/ga.dart",
+ "args": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pub/usage/BUILD.gn b/pub/usage/BUILD.gn
index f5ef48d..fb2aaa5 100644
--- a/pub/usage/BUILD.gn
+++ b/pub/usage/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for usage-2.2.2
+# This file is generated by importer.py for usage-3.0.0
import("//build/dart/dart_package.gni")
diff --git a/pub/usage/changelog.md b/pub/usage/changelog.md
index 0d5a5d7..16729df 100644
--- a/pub/usage/changelog.md
+++ b/pub/usage/changelog.md
@@ -1,5 +1,13 @@
# Changelog
+## 3.0.0
+- removed the use of configurable imports
+- removed the Flutter specific entry-point; Flutter apps can now use the
+ regular `dart:io` entrypoint (AnalyticsIO)
+- moved the uuid library from `lib/src/` to `lib/uuid/`
+- fixed an issue with reporting the user language for the dart:io provider
+- changed to send additional lines for reported exceptions
+
## 2.2.2
- adjust the Flutter usage client to Flutter API changes
diff --git a/pub/usage/example/example.dart b/pub/usage/example/example.dart
index 27fb5fc..06578c3 100644
--- a/pub/usage/example/example.dart
+++ b/pub/usage/example/example.dart
@@ -5,10 +5,9 @@
/// A simple web app to hand-test the usage library.
library usage_example;
-import 'dart:async';
import 'dart:html';
-import 'package:usage/usage.dart';
+import 'package:usage/usage_html.dart';
Analytics _analytics;
String _lastUa;
@@ -22,28 +21,28 @@
String _ua() => (querySelector('#ua') as InputElement).value.trim();
-Future<Analytics> getAnalytics() async {
+Analytics getAnalytics() {
if (_analytics == null || _lastUa != _ua()) {
_lastUa = _ua();
- _analytics = await Analytics.create(_lastUa, 'Test app', '1.0');
+ _analytics = new AnalyticsHtml(_lastUa, 'Test app', '1.0');
_analytics.sendScreenView(window.location.pathname);
}
return _analytics;
}
-Future _handleFoo() async {
- Analytics analytics = await getAnalytics();
+void _handleFoo() {
+ Analytics analytics = getAnalytics();
analytics.sendEvent('main', 'foo');
}
-Future _handleBar() async {
- Analytics analytics = await getAnalytics();
+void _handleBar() {
+ Analytics analytics = getAnalytics();
analytics.sendEvent('main', 'bar');
}
-Future _changePage() async {
- Analytics analytics = await getAnalytics();
+void _changePage() {
+ Analytics analytics = getAnalytics();
window.history.pushState(null, 'new page', '${++_count}.html');
analytics.sendScreenView(window.location.pathname);
}
diff --git a/pub/usage/example/flutter_example/.gitignore b/pub/usage/example/flutter_example/.gitignore
deleted file mode 100644
index 14c7d4c..0000000
--- a/pub/usage/example/flutter_example/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-.DS_Store
-.atom/
-.idea
-.packages
-.pub/
-build/
-ios/.generated/
-packages
-pubspec.lock
diff --git a/pub/usage/example/flutter_example/README.md b/pub/usage/example/flutter_example/README.md
deleted file mode 100644
index 7608a60..0000000
--- a/pub/usage/example/flutter_example/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Example of usage package with Flutter
-
-To run the Flutter example, ensure that the pubspec.yaml points to your Flutter
-path. Then run:
-
-```
-pub get
-flutter run
-```
diff --git a/pub/usage/example/flutter_example/android/AndroidManifest.xml b/pub/usage/example/flutter_example/android/AndroidManifest.xml
deleted file mode 100644
index 2e28bb3..0000000
--- a/pub/usage/example/flutter_example/android/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.yourcompany.flutterExample"
- android:versionCode="1"
- android:versionName="0.0.1">
-
- <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" />
- <uses-permission android:name="android.permission.INTERNET"/>
-
- <application android:name="org.domokit.sky.shell.SkyApplication" android:label="flutter_example" android:icon="@mipmap/ic_launcher">
- <activity android:name="org.domokit.sky.shell.SkyActivity"
- android:launchMode="singleTask"
- android:theme="@android:style/Theme.Black.NoTitleBar"
- android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
- android:hardwareAccelerated="true"
- android:windowSoftInputMode="adjustResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/pub/usage/example/flutter_example/flutter.yaml b/pub/usage/example/flutter_example/flutter.yaml
deleted file mode 100644
index 2f6cd44..0000000
--- a/pub/usage/example/flutter_example/flutter.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-name: flutter_example
-uses-material-design: true
diff --git a/pub/usage/example/flutter_example/lib/main.dart b/pub/usage/example/flutter_example/lib/main.dart
deleted file mode 100644
index ff2d78d..0000000
--- a/pub/usage/example/flutter_example/lib/main.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2016, the Flutter 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.
-
-import 'dart:async';
-
-import 'package:flutter/material.dart';
-import 'package:usage/usage.dart';
-
-Future main() async {
- runApp(new Container());
- Analytics ga = await Analytics.create('UA-67589403-4', 'ga_test', '1.0');
- runApp(new MaterialApp(
- title: 'Usage Example',
- theme: new ThemeData.dark(),
- routes: <String, WidgetBuilder>{
- '/': (BuildContext context) => new FlutterDemo(ga)
- }
- ));
-}
-
-class FlutterDemo extends StatefulWidget {
- FlutterDemo(this.ga);
- Analytics ga;
- @override
- State createState() => new _FlutterDemoState();
-}
-
-class _FlutterDemoState extends State<FlutterDemo> {
- int _times = 0;
-
- void _handleButtonPressed() {
- config.ga.sendEvent('button', 'pressed');
- setState(() {
- _times++;
- });
- }
-
- void _handleOptIn(bool value) {
- setState(() {
- config.ga.enabled = value;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return new Scaffold(
- appBar: new AppBar(
- title: new Text('Usage Example')
- ),
- body: new Column(
- children: <Widget>[
- new Center(
- child: new Text("Button pressed $_times times.")
- ),
- new ListItem(
- onTap: () => _handleOptIn(!config.ga.enabled),
- leading: new Checkbox(
- value: config.ga.enabled,
- onChanged: _handleOptIn
- ),
- title: new Text("Opt in to analytics")
- )
- ],
- mainAxisAlignment: MainAxisAlignment.spaceAround
- ),
- floatingActionButton: new FloatingActionButton(
- child: new Icon(icon: Icons.add),
- onPressed: _handleButtonPressed
- )
- );
- }
-}
diff --git a/pub/usage/example/flutter_example/pubspec.yaml b/pub/usage/example/flutter_example/pubspec.yaml
deleted file mode 100644
index e95d585..0000000
--- a/pub/usage/example/flutter_example/pubspec.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: flutter_example
-description: Create a new Flutter project.
-dependencies:
- usage:
- path: ../..
- flutter:
- path: ../../../flutter/packages/flutter
diff --git a/pub/usage/example/ga.dart b/pub/usage/example/ga.dart
index e905ea6..bf0807e 100644
--- a/pub/usage/example/ga.dart
+++ b/pub/usage/example/ga.dart
@@ -5,11 +5,9 @@
/// A simple command-line app to hand-test the usage library.
library usage_ga;
-import 'dart:async';
+import 'package:usage/usage_io.dart';
-import 'package:usage/usage.dart';
-
-Future main(List args) async {
+main(List args) async {
final String DEFAULT_UA = 'UA-55029513-1';
if (args.isEmpty) {
@@ -21,17 +19,13 @@
String ua = args.isEmpty ? DEFAULT_UA : args.first;
- Analytics ga = await Analytics.create(ua, 'ga_test', '1.0');
+ Analytics ga = new AnalyticsIO(ua, 'ga_test', '3.0');
- ga.sendScreenView('home').then((_) {
- return ga.sendScreenView('files');
- }).then((_) {
- return ga.sendException('foo exception, line 123:56');
- }).then((_) {
- return ga.sendTiming('writeDuration', 123);
- }).then((_) {
- return ga.sendEvent('create', 'consoleapp', label: 'Console App');
- }).then((_) {
- print('pinged ${ua}');
- });
+ await ga.sendScreenView('home');
+ await ga.sendScreenView('files');
+ await ga
+ .sendException('foo error:\n${sanitizeStacktrace(StackTrace.current)}');
+ await ga.sendTiming('writeDuration', 123);
+ await ga.sendEvent('create', 'consoleapp', label: 'Console App');
+ print('pinged ${ua}');
}
diff --git a/pub/usage/lib/src/usage_impl.dart b/pub/usage/lib/src/usage_impl.dart
index 290c5fb..105fe36 100644
--- a/pub/usage/lib/src/usage_impl.dart
+++ b/pub/usage/lib/src/usage_impl.dart
@@ -6,9 +6,7 @@
import 'dart:math' as math;
import '../usage.dart';
-import 'uuid.dart';
-
-final int _MAX_EXCEPTION_LENGTH = 100;
+import '../uuid/uuid.dart';
String postEncode(Map<String, dynamic> map) {
// &foo=bar
@@ -19,11 +17,11 @@
}
/**
- * A throttling algorithim. This models the throttling after a bucket with
+ * A throttling algorithm. This models the throttling after a bucket with
* water dripping into it at the rate of 1 drop per second. If the bucket has
* water when an operation is requested, 1 drop of water is removed and the
- * operation is performed. If not the operation is skipped. This algorithim
- * lets operations be peformed in bursts without throttling, but holds the
+ * operation is performed. If not the operation is skipped. This algorithm
+ * lets operations be performed in bursts without throttling, but holds the
* overall average rate of operations to 1 per second.
*/
class ThrottlingBucket {
@@ -59,7 +57,8 @@
}
class AnalyticsImpl implements Analytics {
- static const String _defaultAnalyticsUrl = 'https://www.google-analytics.com/collect';
+ static const String _defaultAnalyticsUrl =
+ 'https://www.google-analytics.com/collect';
/**
* Tracking ID / Property ID.
@@ -78,16 +77,13 @@
String _url;
- StreamController<Map<String, dynamic>> _sendController = new StreamController.broadcast(sync: true);
+ StreamController<Map<String, dynamic>> _sendController =
+ new StreamController.broadcast(sync: true);
- AnalyticsImpl(
- this.trackingId,
- this.properties,
- this.postHandler, {
- String applicationName,
- String applicationVersion,
- String analyticsUrl
- }) {
+ AnalyticsImpl(this.trackingId, this.properties, this.postHandler,
+ {String applicationName,
+ String applicationVersion,
+ String analyticsUrl}) {
assert(trackingId != null);
if (applicationName != null) setSessionValue('an', applicationName);
@@ -115,7 +111,9 @@
*/
bool get enabled {
bool optIn = analyticsOpt == AnalyticsOpt.optIn;
- return optIn ? properties['enabled'] == true : properties['enabled'] != false;
+ return optIn
+ ? properties['enabled'] == true
+ : properties['enabled'] != false;
}
/**
@@ -142,19 +140,25 @@
return _sendPayload('social', args);
}
- Future sendTiming(String variableName, int time, {String category, String label}) {
+ Future sendTiming(String variableName, int time,
+ {String category, String label}) {
Map<String, dynamic> args = {'utv': variableName, 'utt': time};
if (label != null) args['utl'] = label;
if (category != null) args['utc'] = category;
return _sendPayload('timing', args);
}
- AnalyticsTimer startTimer(String variableName, {String category, String label}) {
- return new AnalyticsTimer(this,
- variableName, category: category, label: label);
+ AnalyticsTimer startTimer(String variableName,
+ {String category, String label}) {
+ return new AnalyticsTimer(this, variableName,
+ category: category, label: label);
}
Future sendException(String description, {bool fatal}) {
+ // We trim exceptions to a max length; google analytics will apply it's own
+ // truncation, likely around 150 chars or so.
+ const int maxExceptionLength = 1000;
+
// In order to ensure that the client of this API is not sending any PII
// data, we strip out any stack trace that may reference a path on the
// user's drive (file:/...).
@@ -162,8 +166,10 @@
description = description.substring(0, description.indexOf('file:/'));
}
- if (description != null && description.length > _MAX_EXCEPTION_LENGTH) {
- description = description.substring(0, _MAX_EXCEPTION_LENGTH);
+ description = description.replaceAll('\n', '; ');
+
+ if (description.length > maxExceptionLength) {
+ description = description.substring(0, maxExceptionLength);
}
Map<String, dynamic> args = {'exd': description};
@@ -253,7 +259,7 @@
* of these injected into it. There are default implementations for `dart:io`
* and `dart:html` clients.
*
- * The [name] paramater is used to uniquely store these properties on disk /
+ * The [name] parameter is used to uniquely store these properties on disk /
* persistent storage.
*/
abstract class PersistentProperties {
@@ -261,8 +267,8 @@
PersistentProperties(this.name);
- dynamic operator[](String key);
- void operator[]=(String key, dynamic value);
+ dynamic operator [](String key);
+ void operator []=(String key, dynamic value);
}
/**
diff --git a/pub/usage/lib/src/usage_impl_default.dart b/pub/usage/lib/src/usage_impl_default.dart
deleted file mode 100644
index 78b3f46..0000000
--- a/pub/usage/lib/src/usage_impl_default.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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.
-
-import 'dart:async';
-
-import '../usage.dart';
-
-Future<Analytics> createAnalytics(
- String trackingId,
- String applicationName,
- String applicationVersion, {
- String analyticsUrl
-}) {
- throw new UnimplementedError();
-}
diff --git a/pub/usage/lib/src/usage_impl_flutter.dart b/pub/usage/lib/src/usage_impl_flutter.dart
deleted file mode 100644
index 58f90a3..0000000
--- a/pub/usage/lib/src/usage_impl_flutter.dart
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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.
-
-import 'dart:async';
-import 'dart:convert' show JSON;
-import 'dart:io';
-
-import 'package:flutter/http.dart' as http;
-import 'package:flutter/services.dart';
-import 'package:path/path.dart' as path;
-
-import '../usage.dart';
-import 'usage_impl.dart';
-
-Future<Analytics> createAnalytics(
- String trackingId,
- String applicationName,
- String applicationVersion, {
- String analyticsUrl
-}) async {
- Directory dataDirectory = await PathProvider.getTemporaryDirectory();
-
- String fileName = '.${applicationName.replaceAll(' ', '_')}';
- File file = new File(path.join(dataDirectory.path, fileName));
- await file.create();
- String contents = await file.readAsString();
- if (contents.isEmpty) contents = '{}';
- Map map = JSON.decode(contents);
-
- return new AnalyticsImpl(
- trackingId,
- new FlutterPersistentProperties(applicationName, file, map),
- new FlutterPostHandler(),
- applicationName: applicationName,
- applicationVersion: applicationVersion,
- analyticsUrl: analyticsUrl
- );
-}
-
-String _createUserAgent() {
- // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en)
- // Dart/1.8.0-edge.41170 (macos; macos; macos; null)
- String os = Platform.operatingSystem;
- String locale = Platform.environment['LANG'];
- return "Dart/${_dartVersion()} (${os}; ${os}; ${os}; ${locale})";
-}
-
-String _dartVersion() {
- String ver = Platform.version;
- int index = ver.indexOf(' ');
- if (index != -1) ver = ver.substring(0, index);
- return ver;
-}
-
-class FlutterPostHandler extends PostHandler {
- final String _userAgent;
- final HttpClient mockClient;
-
- FlutterPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent();
-
- Future sendPost(String url, Map<String, dynamic> parameters) {
- // Add custom parameters for OS and the Dart version.
- parameters['cd1'] = Platform.operatingSystem;
- parameters['cd2'] = 'dart ${_dartVersion()}';
-
- String data = postEncode(parameters);
-
- Map<String, String> headers = <String, String>{ 'User-Agent': _userAgent };
-
- return http.post(url, body: data, headers: headers);
- }
-}
-
-class FlutterPersistentProperties extends PersistentProperties {
- File _file;
- Map _map;
- FlutterPersistentProperties(String name, this._file, this._map) : super(name);
-
- dynamic operator[](String key) => _map[key];
-
- void operator[]=(String key, dynamic value) {
- if (value == null) {
- _map.remove(key);
- } else {
- _map[key] = value;
- }
-
- _file.writeAsString(JSON.encode(_map) + '\n');
- }
-}
diff --git a/pub/usage/lib/src/usage_impl_html.dart b/pub/usage/lib/src/usage_impl_html.dart
index 6a5922c..7752fe5 100644
--- a/pub/usage/lib/src/usage_impl_html.dart
+++ b/pub/usage/lib/src/usage_impl_html.dart
@@ -6,34 +6,21 @@
import 'dart:convert' show JSON;
import 'dart:html';
-import '../usage.dart';
import 'usage_impl.dart';
-Future<Analytics> createAnalytics(
- String trackingId,
- String applicationName,
- String applicationVersion, {
- String analyticsUrl
-}) {
- return new Future.value(new AnalyticsHtml(
- trackingId,
- applicationName,
- applicationVersion,
- analyticsUrl: analyticsUrl
- ));
-}
-
+/// An interface to a Google Analytics session, suitable for use in web apps.
+///
+/// [analyticsUrl] is an optional replacement for the default Google Analytics
+/// URL (`https://www.google-analytics.com/collect`).
class AnalyticsHtml extends AnalyticsImpl {
- AnalyticsHtml(String trackingId, String applicationName, String applicationVersion, {
- String analyticsUrl
- }) : super(
- trackingId,
- new HtmlPersistentProperties(applicationName),
- new HtmlPostHandler(),
- applicationName: applicationName,
- applicationVersion: applicationVersion,
- analyticsUrl: analyticsUrl
- ) {
+ AnalyticsHtml(
+ String trackingId, String applicationName, String applicationVersion,
+ {String analyticsUrl})
+ : super(trackingId, new HtmlPersistentProperties(applicationName),
+ new HtmlPostHandler(),
+ applicationName: applicationName,
+ applicationVersion: applicationVersion,
+ analyticsUrl: analyticsUrl) {
int screenWidth = window.screen.width;
int screenHeight = window.screen.height;
@@ -58,7 +45,7 @@
var request = mockRequestor == null ? HttpRequest.request : mockRequestor;
return request(url, method: 'POST', sendData: data).catchError((e) {
// Catch errors that can happen during a request, but that we can't do
- // anything about, e.g. a missing internet conenction.
+ // anything about, e.g. a missing internet connection.
});
}
}
@@ -72,9 +59,9 @@
_map = JSON.decode(str);
}
- dynamic operator[](String key) => _map[key];
+ dynamic operator [](String key) => _map[key];
- void operator[]=(String key, dynamic value) {
+ void operator []=(String key, dynamic value) {
if (value == null) {
_map.remove(key);
} else {
diff --git a/pub/usage/lib/src/usage_impl_io.dart b/pub/usage/lib/src/usage_impl_io.dart
index fff224a..fe5cc44 100644
--- a/pub/usage/lib/src/usage_impl_io.dart
+++ b/pub/usage/lib/src/usage_impl_io.dart
@@ -8,49 +8,55 @@
import 'package:path/path.dart' as path;
-import '../usage.dart';
import 'usage_impl.dart';
-Future<Analytics> createAnalytics(
- String trackingId,
- String applicationName,
- String applicationVersion, {
- String analyticsUrl
-}) {
- return new Future.value(new AnalyticsIO(
- trackingId,
- applicationName,
- applicationVersion,
- analyticsUrl: analyticsUrl
- ));
-}
-
+/// An interface to a Google Analytics session, suitable for use in command-line
+/// applications.
+///
+/// `trackingId`, `applicationName`, and `applicationVersion` values should be supplied.
+/// `analyticsUrl` is optional, and lets user's substitute their own analytics URL for
+/// the default.
+///
+/// `documentDirectory` is where the analytics settings are stored. It
+/// defaults to the user home directory. For regular `dart:io` apps this doesn't need to
+/// be supplied. For Flutter applications, you should pass in a value like
+/// `PathProvider.getApplicationDocumentsDirectory()`.
class AnalyticsIO extends AnalyticsImpl {
- AnalyticsIO(String trackingId, String applicationName, String applicationVersion, {
- String analyticsUrl
- }) : super(
- trackingId,
- new IOPersistentProperties(applicationName),
- new IOPostHandler(),
- applicationName: applicationName,
- applicationVersion: applicationVersion,
- analyticsUrl: analyticsUrl
- );
+ AnalyticsIO(
+ String trackingId, String applicationName, String applicationVersion,
+ {String analyticsUrl, Directory documentDirectory})
+ : super(
+ trackingId,
+ new IOPersistentProperties(applicationName,
+ documentDirPath: documentDirectory?.path),
+ new IOPostHandler(),
+ applicationName: applicationName,
+ applicationVersion: applicationVersion,
+ analyticsUrl: analyticsUrl) {
+ final String locale = getPlatformLocale();
+ if (locale != null) {
+ setSessionValue('ul', locale);
+ }
+ }
}
String _createUserAgent() {
- if (Platform.isMacOS) {
- return 'Mozilla/5.0 (Macintosh; Intel Mac OS X)';
+ final String locale = getPlatformLocale() ?? '';
+
+ if (Platform.isAndroid) {
+ return 'Mozilla/5.0 (Android; Mobile; ${locale})';
+ } else if (Platform.isIOS) {
+ return 'Mozilla/5.0 (iPhone; U; CPU iPhone OS like Mac OS X; ${locale})';
} else if (Platform.isMacOS) {
- return 'Mozilla/5.0 (Windows; Windows)';
+ return 'Mozilla/5.0 (Macintosh; Intel Mac OS X; Macintosh; ${locale})';
+ } else if (Platform.isWindows) {
+ return 'Mozilla/5.0 (Windows; Windows; Windows; ${locale})';
} else if (Platform.isLinux) {
- return 'Mozilla/5.0 (Linux; Linux)';
+ return 'Mozilla/5.0 (Linux; Linux; Linux; ${locale})';
} else {
- // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en)
- // Dart/1.8.0-edge.41170 (macos; macos; macos; null)
+ // Dart/1.8.0 (macos; macos; macos; en_US)
String os = Platform.operatingSystem;
- String locale = Platform.environment['LANG'];
- return "Dart/${_dartVersion()} (${os}; ${os}; ${os}; ${locale})";
+ return "Dart/${getDartVersion()} (${os}; ${os}; ${os}; ${locale})";
}
}
@@ -60,7 +66,7 @@
return value == null ? '.' : value;
}
-String _dartVersion() {
+String getDartVersion() {
String ver = Platform.version;
int index = ver.indexOf(' ');
if (index != -1) ver = ver.substring(0, index);
@@ -74,10 +80,6 @@
IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent();
Future sendPost(String url, Map<String, dynamic> parameters) async {
- // Add custom parameters for OS and the Dart version.
- parameters['cd1'] = Platform.operatingSystem;
- parameters['cd2'] = 'dart ${_dartVersion()}';
-
String data = postEncode(parameters);
HttpClient client = mockClient != null ? mockClient : new HttpClient();
@@ -87,9 +89,9 @@
req.write(data);
HttpClientResponse response = await req.close();
response.drain();
- } catch(exception) {
+ } catch (exception) {
// Catch errors that can happen during a request, but that we can't do
- // anything about, e.g. a missing internet conenction.
+ // anything about, e.g. a missing internet connection.
}
}
}
@@ -98,9 +100,10 @@
File _file;
Map _map;
- IOPersistentProperties(String name) : super(name) {
+ IOPersistentProperties(String name, {String documentDirPath}) : super(name) {
String fileName = '.${name.replaceAll(' ', '_')}';
- _file = new File(path.join(_userHomeDir(), fileName));
+ documentDirPath ??= _userHomeDir();
+ _file = new File(path.join(documentDirPath, fileName));
try {
if (!_file.existsSync()) _file.createSync();
@@ -112,9 +115,9 @@
}
}
- dynamic operator[](String key) => _map[key];
+ dynamic operator [](String key) => _map[key];
- void operator[]=(String key, dynamic value) {
+ void operator []=(String key, dynamic value) {
if (value == null && !_map.containsKey(key)) return;
if (_map[key] == value) return;
@@ -126,6 +129,24 @@
try {
_file.writeAsStringSync(JSON.encode(_map) + '\n');
- } catch (_) { }
+ } catch (_) {}
}
}
+
+/// Return the string for the platform's locale; return's `null` if the locale
+/// can't be determined.
+String getPlatformLocale() {
+ String locale = Platform.environment['LANG'];
+ if (locale == null) return null;
+
+ if (locale != null) {
+ // Convert `en_US.UTF-8` to `en_US`.
+ int index = locale.indexOf('.');
+ if (index != null) locale = locale.substring(0, index);
+
+ // Convert `en_US` to `en-us`.
+ locale = locale.replaceAll('_', '-').toLowerCase();
+ }
+
+ return locale;
+}
diff --git a/pub/usage/lib/src/uuid.dart b/pub/usage/lib/src/uuid.dart
deleted file mode 100644
index 66e99ac..0000000
--- a/pub/usage/lib/src/uuid.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2014, 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.
-
-/**
- * A UUID generator library.
- */
-library usage.uuid;
-
-import 'dart:math' show Random;
-
-/**
- * A UUID generator. This will generate unique IDs in the format:
- *
- * f47ac10b-58cc-4372-a567-0e02b2c3d479
- *
- * The generated uuids are 128 bit numbers encoded in a specific string format.
- *
- * For more information, see
- * http://en.wikipedia.org/wiki/Universally_unique_identifier.
- */
-class Uuid {
- Random _random = new Random();
-
- /**
- * Generate a version 4 (random) uuid. This is a uuid scheme that only uses
- * random numbers as the source of the generated uuid.
- */
- String generateV4() {
- // Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12.
- int special = 8 + _random.nextInt(4);
-
- return
- '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
- '${_bitsDigits(16, 4)}-'
- '4${_bitsDigits(12, 3)}-'
- '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
- '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
- }
-
- String _bitsDigits(int bitCount, int digitCount) =>
- _printDigits(_generateBits(bitCount), digitCount);
-
- int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);
-
- String _printDigits(int value, int count) =>
- value.toRadixString(16).padLeft(count, '0');
-}
diff --git a/pub/usage/lib/usage.dart b/pub/usage/lib/usage.dart
index 53f1812..13f98d6 100644
--- a/pub/usage/lib/usage.dart
+++ b/pub/usage/lib/usage.dart
@@ -3,15 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
/**
- * `usage` is a wrapper around Google Analytics for both command-line, web, and
- * Flutter apps.
+ * `usage` is a wrapper around Google Analytics for both command-line apps
+ * and web apps.
*
- * In order to use this library, call the [Analytics.create] static method.
- * You'll get either the command-line, web, or Flutter implementation based on
- * the current platform.
+ * In order to use this library as a web app, import the `analytics_html.dart`
+ * library and instantiate the [AnalyticsHtml] class.
*
- * When creating a new Analytics instance, you need to provide a Google
- * Analytics tracking ID, the application name, and the application version.
+ * In order to use this library as a command-line app, import the
+ * `analytics_io.dart` library and instantiate the [AnalyticsIO] class.
+ *
+ * For both classes, you need to provide a Google Analytics tracking ID, the
+ * application name, and the application version.
*
* Your application should provide an opt-in option for the user. If they
* opt-in, set the [optIn] field to `true`. This setting will persist across
@@ -24,40 +26,22 @@
import 'dart:async';
-import 'src/usage_impl_default.dart'
- if (dart.library.js) 'src/usage_impl_html.dart'
- if (dart.library.ui) 'src/usage_impl_flutter.dart'
- if (dart.library.io) 'src/usage_impl_io.dart'
- as impl;
-
// Matches file:/, non-ws, /, non-ws, .dart
final RegExp _pathRegex = new RegExp(r'file:/\S+/(\S+\.dart)');
+// Match multiple tabs or spaces.
+final RegExp _tabOrSpaceRegex = new RegExp(r'[\t ]+');
+
/**
- * An interface to a Google Analytics session. You'll get the correct one for
- * your platform by calling the [Analytics.create] static method.
- * [AnalyticsMock] can be used for testing or for some variants of an opt-in
- * workflow.
+ * An interface to a Google Analytics session. [AnalyticsHtml] and [AnalyticsIO]
+ * are concrete implementations of this interface. [AnalyticsMock] can be used
+ * for testing or for some variants of an opt-in workflow.
*
- * The analytics information is sent on a best-effort basis. Failures to send
- * the GA information will not result in errors from the asynchronous `send`
- * methods.
+ * The analytics information is sent on a best-effort basis. So, failures to
+ * send the GA information will not result in errors from the asynchronous
+ * `send` methods.
*/
abstract class Analytics {
- static Future<Analytics> create(
- String trackingId,
- String applicationName,
- String applicationVersion, {
- String analyticsUrl
- }) {
- return impl.createAnalytics(
- trackingId,
- applicationName,
- applicationVersion,
- analyticsUrl: analyticsUrl
- );
- }
-
/**
* Tracking ID / Property ID.
*/
@@ -109,8 +93,8 @@
* milliseconds). [category] specifies the category of the timing. [label]
* specifies the label of the timing.
*/
- Future sendTiming(String variableName, int time, {String category,
- String label});
+ Future sendTiming(String variableName, int time,
+ {String category, String label});
/**
* Start a timer. The time won't be calculated, and the analytics information
@@ -141,7 +125,7 @@
/**
* Fires events when the usage library sends any data over the network. This
- * will not fire if analytics has been disabled or if the throttling algorithim
+ * will not fire if analytics has been disabled or if the throttling algorithm
* has been engaged.
*
* This method is public to allow library clients to more easily test their
@@ -210,8 +194,8 @@
if (_endMillis != null) return new Future.value();
_endMillis = new DateTime.now().millisecondsSinceEpoch;
- return analytics.sendTiming(
- variableName, currentElapsedMillis, category: category, label: label);
+ return analytics.sendTiming(variableName, currentElapsedMillis,
+ category: category, label: label);
}
}
@@ -223,11 +207,11 @@
String get trackingId => 'UA-0';
final bool logCalls;
-
/**
* Events are never added to this controller for the mock implementation.
*/
- StreamController<Map<String, dynamic>> _sendController = new StreamController.broadcast();
+ StreamController<Map<String, dynamic>> _sendController =
+ new StreamController.broadcast();
/**
* Create a new [AnalyticsMock]. If [logCalls] is true, all calls will be
@@ -245,20 +229,31 @@
_log('screenView', {'viewName': viewName});
Future sendEvent(String category, String action, {String label, int value}) {
- return _log('event', {'category': category, 'action': action,
- 'label': label, 'value': value});
+ return _log('event', {
+ 'category': category,
+ 'action': action,
+ 'label': label,
+ 'value': value
+ });
}
Future sendSocial(String network, String action, String target) =>
_log('social', {'network': network, 'action': action, 'target': target});
- Future sendTiming(String variableName, int time, {String category, String label}) {
- return _log('timing', {'variableName': variableName, 'time': time,
- 'category': category, 'label': label});
+ Future sendTiming(String variableName, int time,
+ {String category, String label}) {
+ return _log('timing', {
+ 'variableName': variableName,
+ 'time': time,
+ 'category': category,
+ 'label': label
+ });
}
- AnalyticsTimer startTimer(String variableName, {String category, String label}) {
- return new AnalyticsTimer(this, variableName, category: category, label: label);
+ AnalyticsTimer startTimer(String variableName,
+ {String category, String label}) {
+ return new AnalyticsTimer(this, variableName,
+ category: category, label: label);
}
Future sendException(String description, {bool fatal}) =>
@@ -266,7 +261,7 @@
dynamic getSessionValue(String param) => null;
- void setSessionValue(String param, dynamic value) { }
+ void setSessionValue(String param, dynamic value) {}
Stream<Map<String, dynamic>> get onSend => _sendController.stream;
@@ -299,16 +294,13 @@
for (Match match in iter) {
String replacement = match.group(1);
- str = str.substring(0, match.start)
- + replacement + str.substring(match.end);
+ str =
+ str.substring(0, match.start) + replacement + str.substring(match.end);
}
if (shorten) {
// Shorten the stacktrace up a bit.
- str = str
- .replaceAll('(package:', '(')
- .replaceAll('(dart:', '(')
- .replaceAll(new RegExp(r'\s+'), ' ');
+ str = str.replaceAll(_tabOrSpaceRegex, ' ');
}
return str;
diff --git a/pub/usage/lib/usage_html.dart b/pub/usage/lib/usage_html.dart
new file mode 100644
index 0000000..569c1eb
--- /dev/null
+++ b/pub/usage/lib/usage_html.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2014, 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.
+
+/// In order to use this library import the `usage_html.dart` file and
+/// instantiate the [AnalyticsHtml] class.
+///
+/// You'll need to provide a Google Analytics tracking ID, the application name,
+/// and the application version.
+library usage_html;
+
+export 'src/usage_impl_html.dart' show AnalyticsHtml;
+export 'usage.dart';
diff --git a/pub/usage/lib/usage_io.dart b/pub/usage/lib/usage_io.dart
new file mode 100644
index 0000000..5e35e94
--- /dev/null
+++ b/pub/usage/lib/usage_io.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2014, 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.
+
+/// In order to use this library import the `usage_io.dart` file and
+/// instantiate the [AnalyticsIO] class.
+///
+/// You'll need to provide a Google Analytics tracking ID, the application name,
+/// and the application version.
+library usage_io;
+
+export 'src/usage_impl_io.dart' show AnalyticsIO;
+export 'usage.dart';
diff --git a/pub/usage/lib/uuid/uuid.dart b/pub/usage/lib/uuid/uuid.dart
new file mode 100644
index 0000000..eaafeb2
--- /dev/null
+++ b/pub/usage/lib/uuid/uuid.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2014, 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.
+
+/// A UUID generator library.
+library uuid;
+
+import 'dart:math' show Random;
+
+/// A UUID generator.
+///
+/// This will generate unique IDs in the format:
+///
+/// f47ac10b-58cc-4372-a567-0e02b2c3d479
+///
+/// The generated uuids are 128 bit numbers encoded in a specific string format.
+/// For more information, see
+/// [en.wikipedia.org/wiki/Universally_unique_identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier).
+class Uuid {
+ final Random _random = new Random();
+
+ /// Generate a version 4 (random) uuid. This is a uuid scheme that only uses
+ /// random numbers as the source of the generated uuid.
+ String generateV4() {
+ // Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12.
+ int special = 8 + _random.nextInt(4);
+
+ return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
+ '${_bitsDigits(16, 4)}-'
+ '4${_bitsDigits(12, 3)}-'
+ '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
+ '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
+ }
+
+ String _bitsDigits(int bitCount, int digitCount) =>
+ _printDigits(_generateBits(bitCount), digitCount);
+
+ int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);
+
+ String _printDigits(int value, int count) =>
+ value.toRadixString(16).padLeft(count, '0');
+}
diff --git a/pub/usage/pubspec.yaml b/pub/usage/pubspec.yaml
index 74705d9..34dd640 100644
--- a/pub/usage/pubspec.yaml
+++ b/pub/usage/pubspec.yaml
@@ -3,13 +3,13 @@
# BSD-style license that can be found in the LICENSE file.
name: usage
-version: 2.2.2
+version: 3.0.0
description: A Google Analytics wrapper for both command-line, web, and Flutter apps.
homepage: https://github.com/dart-lang/usage
author: Dart Team <misc@dartlang.org>
environment:
- sdk: '>=1.15.0 <2.0.0'
+ sdk: '>=1.19.0 <2.0.0'
dev_dependencies:
browser: ^0.10.0
diff --git a/pub/usage/readme.md b/pub/usage/readme.md
index 8aa0b95..458c581 100644
--- a/pub/usage/readme.md
+++ b/pub/usage/readme.md
@@ -3,20 +3,42 @@
`usage` is a wrapper around Google Analytics for both command-line, web, and
Flutter apps.
+[![pub package](https://img.shields.io/pub/v/usage.svg)](https://pub.dartlang.org/packages/usage)
[![Build Status](https://travis-ci.org/dart-lang/usage.svg)](https://travis-ci.org/dart-lang/usage)
[![Coverage Status](https://img.shields.io/coveralls/dart-lang/usage.svg)](https://coveralls.io/r/dart-lang/usage?branch=master)
-## Using this library
+## For web apps
-In order to use this library, call the `Analytics.create` static method.
-You'll get either the command-line, web, or Flutter implementation based on
-the current platform.
+To use this library as a web app, import the `usage_html.dart` library and
+instantiate the `AnalyticsHtml` class.
When you are creating a new property at [google analytics](https://www.google.com/analytics/)
-make sure to select not the website option, but the **mobile app** option.
+make sure to select the **mobile app** option, not the website option.
+
+## For Flutter apps
+
+Flutter applications can use the `AnalyticsIO` version of this library. They will need
+to specify the documents directory in the constructor in order to tell the library where
+to save the analytics preferences:
+
+```dart
+import 'package:flutter/services.dart';
+import 'package:usage/usage_io.dart';
+
+void main() {
+ final String UA = ...;
+
+ Analytics ga = new AnalyticsIO(UA, 'ga_test', '3.0',
+ documentsDirectory: PathProvider.getApplicationDocumentsDirectory());
+ ...
+}
+```
## For command-line apps
+To use this library as a command-line app, import the `usage_io.dart` library
+and instantiate the `AnalyticsIO` class.
+
Note, for CLI apps, the usage library will send analytics pings asynchronously.
This is useful it that it doesn't block the app generally. It does have one
side-effect, in that outstanding asynchronous requests will block termination
@@ -38,10 +60,10 @@
## Using the API
-Import the package:
+Import the package (in this example we use the `dart:io` version):
```dart
-import 'package:usage/usage.dart';
+import 'package:usage/usage_io.dart';
```
And call some analytics code:
@@ -49,7 +71,8 @@
```dart
final String UA = ...;
-Analytics ga = await Analytics.create(UA, 'ga_test', '1.0');
+Analytics ga = new AnalyticsIO(UA, 'ga_test', '3.0');
+ga.optIn = true;
ga.sendScreenView('home');
ga.sendException('foo exception');
diff --git a/pub/usage/tool/grind.dart b/pub/usage/tool/grind.dart
index 72f4393..f93261b 100644
--- a/pub/usage/tool/grind.dart
+++ b/pub/usage/tool/grind.dart
@@ -15,13 +15,15 @@
@Task()
void init() => _buildExampleDir.createSync(recursive: true);
-@Task() @Depends(init)
+@Task()
+@Depends(init)
void build() {
// Compile `test/web_test.dart` to the `build/test` dir; measure its size.
File srcFile = new File('example/example.dart');
- Dart2js.compile(
- srcFile, outDir: _buildExampleDir, minify: true, extraArgs: ['--conditional-directives']
- );
+ Dart2js.compile(srcFile,
+ outDir: _buildExampleDir,
+ minify: true,
+ extraArgs: ['--conditional-directives']);
File outFile = joinFile(_buildExampleDir, ['example.dart.js']);
log('${outFile.path} compiled to ${_printSize(outFile)}');
diff --git a/pub/usage/tool/travis.sh b/pub/usage/tool/travis.sh
index e6f3aca..a3f3082 100755
--- a/pub/usage/tool/travis.sh
+++ b/pub/usage/tool/travis.sh
@@ -15,11 +15,7 @@
test/all.dart
# Run the tests.
-dart --conditional_directives -c test/all.dart
-
-# Run the UI/web tests as well.
-#pub build test
-#pub run grinder:test build/test/web.html
+dart -c test/all.dart
# Measure the size of the compiled JS, for the dart:html version of the library.
dart tool/grind.dart build
diff --git a/pub/vector_math/BUILD.gn b/pub/vector_math/BUILD.gn
index 6cadce2..3136ecc 100644
--- a/pub/vector_math/BUILD.gn
+++ b/pub/vector_math/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for vector_math-2.0.3
+# This file is generated by importer.py for vector_math-2.0.4
import("//build/dart/dart_package.gni")
diff --git a/pub/vector_math/changelog.md b/pub/vector_math/changelog.md
index 88a48ed..5122c85 100644
--- a/pub/vector_math/changelog.md
+++ b/pub/vector_math/changelog.md
@@ -1,5 +1,13 @@
# Changelog - vector_math
+## v 2.0.4 - February 2017
+
+- Added Matrix4.isIdentity()
+- Added Matrix4.isZero()
+- Added Matrix3.isIdentity()
+- Added Matrix3.isZero()
+- Added Vector2.angleTo and Vector2.angleToSigned
+
## v 2.0.3 - May 2016
- Synchronize Float64 version
diff --git a/pub/vector_math/lib/src/vector_math/colors.dart b/pub/vector_math/lib/src/vector_math/colors.dart
index 2925a38..6b5a533 100644
--- a/pub/vector_math/lib/src/vector_math/colors.dart
+++ b/pub/vector_math/lib/src/vector_math/colors.dart
@@ -72,17 +72,35 @@
}
/// Convert a [input] color to a hex string without a leading '#'. To include
- /// the alpha channel, set [alpha] to true, it is false by default.
- static String toHexString(Vector4 input, {bool alpha: false}) {
- var color = (input.r * 255).floor() << 16 |
- (input.g * 255).floor() << 8 |
- (input.b * 255).floor();
+ /// the alpha channel, set [alpha] to true, it is false by default. If [short]
+ /// is true, the resulting hex string might also be a short version, like #ff0
+ /// (default false).
+ static String toHexString(Vector4 input,
+ {bool alpha: false, bool short: false}) {
+ final r = (input.r * 0xFF).floor() & 0xFF;
+ final g = (input.g * 0xFF).floor() & 0xFF;
+ final b = (input.b * 0xFF).floor() & 0xFF;
+ final a = (input.a * 0xFF).floor() & 0xFF;
- if (alpha) {
- color |= (input.a * 255).floor() << 24;
+ final isShort = short &&
+ ((r >> 4) == (r & 0xF)) &&
+ ((g >> 4) == (g & 0xF)) &&
+ ((b >> 4) == (b & 0xF)) &&
+ (!alpha || (a >> 4) == (a & 0xF));
+
+ if (isShort) {
+ final rgb = (r & 0xF).toRadixString(16) +
+ (g & 0xF).toRadixString(16) +
+ (b & 0xF).toRadixString(16);
+
+ return alpha ? (a & 0xF).toRadixString(16) + rgb : rgb;
+ } else {
+ final rgb = r.toRadixString(16).padLeft(2, '0') +
+ g.toRadixString(16).padLeft(2, '0') +
+ b.toRadixString(16).padLeft(2, '0');
+
+ return alpha ? a.toRadixString(16).padLeft(2, '0') + rgb : rgb;
}
-
- return color.toRadixString(16);
}
/// Blend the [foreground] color over [background] color and store the color
diff --git a/pub/vector_math/lib/src/vector_math/matrix3.dart b/pub/vector_math/lib/src/vector_math/matrix3.dart
index 0dae0a3..6f65541 100644
--- a/pub/vector_math/lib/src/vector_math/matrix3.dart
+++ b/pub/vector_math/lib/src/vector_math/matrix3.dart
@@ -899,4 +899,30 @@
double z = _m3storage[8];
return new Vector3(x, y, z);
}
+
+ /// Is [this] the identity matrix?
+ bool isIdentity() {
+ return _m3storage[0] == 1.0 // col 1
+ && _m3storage[1] == 0.0
+ && _m3storage[2] == 0.0
+ && _m3storage[3] == 0.0 // col 2
+ && _m3storage[4] == 1.0
+ && _m3storage[5] == 0.0
+ && _m3storage[6] == 0.0 // col 3
+ && _m3storage[7] == 0.0
+ && _m3storage[8] == 1.0;
+ }
+
+ /// Is [this] the zero matrix?
+ bool isZero() {
+ return _m3storage[0] == 0.0 // col 1
+ && _m3storage[1] == 0.0
+ && _m3storage[2] == 0.0
+ && _m3storage[3] == 0.0 // col 2
+ && _m3storage[4] == 0.0
+ && _m3storage[5] == 0.0
+ && _m3storage[6] == 0.0 // col 3
+ && _m3storage[7] == 0.0
+ && _m3storage[8] == 0.0;
+ }
}
diff --git a/pub/vector_math/lib/src/vector_math/matrix4.dart b/pub/vector_math/lib/src/vector_math/matrix4.dart
index 7fb688f..0e3e4b4 100644
--- a/pub/vector_math/lib/src/vector_math/matrix4.dart
+++ b/pub/vector_math/lib/src/vector_math/matrix4.dart
@@ -1980,4 +1980,44 @@
double z = _m4storage[10];
return new Vector3(x, y, z);
}
+
+ /// Is [this] the identity matrix?
+ bool isIdentity() {
+ return _m4storage[0] == 1.0 // col 1
+ && _m4storage[1] == 0.0
+ && _m4storage[2] == 0.0
+ && _m4storage[3] == 0.0
+ && _m4storage[4] == 0.0 // col 2
+ && _m4storage[5] == 1.0
+ && _m4storage[6] == 0.0
+ && _m4storage[7] == 0.0
+ && _m4storage[8] == 0.0 // col 3
+ && _m4storage[9] == 0.0
+ && _m4storage[10] == 1.0
+ && _m4storage[11] == 0.0
+ && _m4storage[12] == 0.0 // col 4
+ && _m4storage[13] == 0.0
+ && _m4storage[14] == 0.0
+ && _m4storage[15] == 1.0;
+ }
+
+ /// Is [this] the zero matrix?
+ bool isZero() {
+ return _m4storage[0] == 0.0 // col 1
+ && _m4storage[1] == 0.0
+ && _m4storage[2] == 0.0
+ && _m4storage[3] == 0.0
+ && _m4storage[4] == 0.0 // col 2
+ && _m4storage[5] == 0.0
+ && _m4storage[6] == 0.0
+ && _m4storage[7] == 0.0
+ && _m4storage[8] == 0.0 // col 3
+ && _m4storage[9] == 0.0
+ && _m4storage[10] == 0.0
+ && _m4storage[11] == 0.0
+ && _m4storage[12] == 0.0 // col 4
+ && _m4storage[13] == 0.0
+ && _m4storage[14] == 0.0
+ && _m4storage[15] == 0.0;
+ }
}
diff --git a/pub/vector_math/lib/src/vector_math/opengl.dart b/pub/vector_math/lib/src/vector_math/opengl.dart
index b913761..d46588d 100644
--- a/pub/vector_math/lib/src/vector_math/opengl.dart
+++ b/pub/vector_math/lib/src/vector_math/opengl.dart
@@ -113,10 +113,16 @@
/// (always positive).
void setPerspectiveMatrix(Matrix4 perspectiveMatrix, double fovYRadians,
double aspectRatio, double zNear, double zFar) {
- double height = Math.tan(fovYRadians * 0.5) * zNear;
- double width = height * aspectRatio;
- setFrustumMatrix(
- perspectiveMatrix, -width, width, -height, height, zNear, zFar);
+ final double height = Math.tan(fovYRadians * 0.5);
+ final double width = height * aspectRatio;
+ final double near_minus_far = zNear - zFar;
+
+ final Matrix4 view = perspectiveMatrix..setZero();
+ view.setEntry(0, 0, 1.0 / width);
+ view.setEntry(1, 1, 1.0 / height);
+ view.setEntry(2, 2, (zFar + zNear) / near_minus_far);
+ view.setEntry(3, 2, -1.0);
+ view.setEntry(2, 3, (2.0 * zNear * zFar) / near_minus_far);
}
/// Constructs a new OpenGL perspective projection matrix.
@@ -136,6 +142,41 @@
return r;
}
+/// Constructs an OpenGL infinite projection matrix in [infiniteMatrix].
+/// [fovYRadians] specifies the field of view angle, in radians, in the y
+/// direction.
+/// [aspectRatio] specifies the aspect ratio that determines the field of view
+/// in the x direction. The aspect ratio of x (width) to y (height).
+/// [zNear] specifies the distance from the viewer to the near plane
+/// (always positive).
+void setInfiniteMatrix(Matrix4 infiniteMatrix, double fovYRadians,
+ double aspectRatio, double zNear) {
+ final double height = Math.tan(fovYRadians * 0.5);
+ final double width = height * aspectRatio;
+
+ final Matrix4 view = infiniteMatrix..setZero();
+ view.setEntry(0, 0, 1.0 / width);
+ view.setEntry(1, 1, 1.0 / height);
+ view.setEntry(2, 2, -1.0);
+ view.setEntry(3, 2, -1.0);
+ view.setEntry(2, 3, -2.0 * zNear);
+}
+
+/// Constructs a new OpenGL infinite projection matrix.
+///
+/// [fovYRadians] specifies the field of view angle, in radians, in the y
+/// direction.
+/// [aspectRatio] specifies the aspect ratio that determines the field of view
+/// in the x direction. The aspect ratio of x (width) to y (height).
+/// [zNear] specifies the distance from the viewer to the near plane
+/// (always positive).
+Matrix4 makeInfiniteMatrix(
+ double fovYRadians, double aspectRatio, double zNear) {
+ Matrix4 r = new Matrix4.zero();
+ setInfiniteMatrix(r, fovYRadians, aspectRatio, zNear);
+ return r;
+}
+
/// Constructs an OpenGL perspective projection matrix in [perspectiveMatrix].
///
/// [left], [right] specify the coordinates for the left and right vertical
diff --git a/pub/vector_math/lib/src/vector_math/third_party/noise.dart b/pub/vector_math/lib/src/vector_math/third_party/noise.dart
index 517586c..0e695ba 100644
--- a/pub/vector_math/lib/src/vector_math/third_party/noise.dart
+++ b/pub/vector_math/lib/src/vector_math/third_party/noise.dart
@@ -28,53 +28,53 @@
class SimplexNoise {
static final _grad3 = <List<double>>[
- [1, 1, 0],
- [-1, 1, 0],
- [1, -1, 0],
- [-1, -1, 0],
- [1, 0, 1],
- [-1, 0, 1],
- [1, 0, -1],
- [-1, 0, -1],
- [0, 1, 1],
- [0, -1, 1],
- [0, 1, -1],
- [0, -1, -1]
+ [1.0, 1.0, 0.0],
+ [-1.0, 1.0, 0.0],
+ [1.0, -1.0, 0.0],
+ [-1.0, -1.0, 0.0],
+ [1.0, 0.0, 1.0],
+ [-1.0, 0.0, 1.0],
+ [1.0, 0.0, -1.0],
+ [-1.0, 0.0, -1.0],
+ [0.0, 1.0, 1.0],
+ [0.0, -1.0, 1.0],
+ [0.0, 1.0, -1.0],
+ [0.0, -1.0, -1.0]
];
static final _grad4 = <List<double>>[
- [0, 1, 1, 1],
- [0, 1, 1, -1],
- [0, 1, -1, 1],
- [0, 1, -1, -1],
- [0, -1, 1, 1],
- [0, -1, 1, -1],
- [0, -1, -1, 1],
- [0, -1, -1, -1],
- [1, 0, 1, 1],
- [1, 0, 1, -1],
- [1, 0, -1, 1],
- [1, 0, -1, -1],
- [-1, 0, 1, 1],
- [-1, 0, 1, -1],
- [-1, 0, -1, 1],
- [-1, 0, -1, -1],
- [1, 1, 0, 1],
- [1, 1, 0, -1],
- [1, -1, 0, 1],
- [1, -1, 0, -1],
- [-1, 1, 0, 1],
- [-1, 1, 0, -1],
- [-1, -1, 0, 1],
- [-1, -1, 0, -1],
- [1, 1, 1, 0],
- [1, 1, -1, 0],
- [1, -1, 1, 0],
- [1, -1, -1, 0],
- [-1, 1, 1, 0],
- [-1, 1, -1, 0],
- [-1, -1, 1, 0],
- [-1, -1, -1, 0]
+ [0.0, 1.0, 1.0, 1.0],
+ [0.0, 1.0, 1.0, -1.0],
+ [0.0, 1.0, -1.0, 1.0],
+ [0.0, 1.0, -1.0, -1.0],
+ [0.0, -1.0, 1.0, 1.0],
+ [0.0, -1.0, 1.0, -1.0],
+ [0.0, -1.0, -1.0, 1.0],
+ [0.0, -1.0, -1.0, -1.0],
+ [1.0, 0.0, 1.0, 1.0],
+ [1.0, 0.0, 1.0, -1.0],
+ [1.0, 0.0, -1.0, 1.0],
+ [1.0, 0.0, -1.0, -1.0],
+ [-1.0, 0.0, 1.0, 1.0],
+ [-1.0, 0.0, 1.0, -1.0],
+ [-1.0, 0.0, -1.0, 1.0],
+ [-1.0, 0.0, -1.0, -1.0],
+ [1.0, 1.0, 0.0, 1.0],
+ [1.0, 1.0, 0.0, -1.0],
+ [1.0, -1.0, 0.0, 1.0],
+ [1.0, -1.0, 0.0, -1.0],
+ [-1.0, 1.0, 0.0, 1.0],
+ [-1.0, 1.0, 0.0, -1.0],
+ [-1.0, -1.0, 0.0, 1.0],
+ [-1.0, -1.0, 0.0, -1.0],
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, -1.0, 0.0],
+ [1.0, -1.0, 1.0, 0.0],
+ [1.0, -1.0, -1.0, 0.0],
+ [-1.0, 1.0, 1.0, 0.0],
+ [-1.0, 1.0, -1.0, 0.0],
+ [-1.0, -1.0, 1.0, 0.0],
+ [-1.0, -1.0, -1.0, 0.0]
];
// To remove the need for index wrapping, double the permutation table length
@@ -101,7 +101,7 @@
if (r == null) {
r = new Math.Random();
}
- List p =
+ List<int> p =
new List<int>.generate(256, (i) => r.nextInt(256), growable: false);
_perm = new List<int>.generate(p.length * 2, (i) => p[i % p.length],
growable: false);
diff --git a/pub/vector_math/lib/src/vector_math/vector2.dart b/pub/vector_math/lib/src/vector_math/vector2.dart
index ef5dcba..d98ecec 100644
--- a/pub/vector_math/lib/src/vector_math/vector2.dart
+++ b/pub/vector_math/lib/src/vector_math/vector2.dart
@@ -54,6 +54,13 @@
Vector2.fromBuffer(ByteBuffer buffer, int offset)
: _v2storage = new Float32List.view(buffer, offset, 2);
+ /// Generate random vector in the range (0, 0) to (1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector2.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector2(rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_) {
_v2storage[0] = x_;
@@ -154,7 +161,7 @@
}
/// Normalize [this]. Returns length of vector before normalization.
- /// /// DEPRCATED: Use [normalize].
+ /// DEPRECATED: Use [normalize].
@deprecated
double normalizeLength() => normalize();
@@ -180,6 +187,33 @@
return dx * dx + dy * dy;
}
+ /// Returns the angle between [this] vector and [other] in radians.
+ double angleTo(Vector2 other) {
+ final otherStorage = other._v2storage;
+ if (_v2storage[0] == otherStorage[0] &&
+ _v2storage[1] == otherStorage[1]) {
+ return 0.0;
+ }
+
+ final d = dot(other);
+
+ return Math.acos(d.clamp(-1.0, 1.0));
+ }
+
+ /// Returns the signed angle between [this] and [other] in radians.
+ double angleToSigned(Vector2 other) {
+ final otherStorage = other._v2storage;
+ if (_v2storage[0] == otherStorage[0] &&
+ _v2storage[1] == otherStorage[1]) {
+ return 0.0;
+ }
+
+ final s = cross(other);
+ final c = dot(other);
+
+ return Math.atan2(s, c);
+ }
+
/// Inner product.
double dot(Vector2 other) {
final otherStorage = other._v2storage;
diff --git a/pub/vector_math/lib/src/vector_math/vector3.dart b/pub/vector_math/lib/src/vector_math/vector3.dart
index 6598290..3f5177d 100644
--- a/pub/vector_math/lib/src/vector_math/vector3.dart
+++ b/pub/vector_math/lib/src/vector_math/vector3.dart
@@ -58,6 +58,13 @@
Vector3.fromBuffer(ByteBuffer buffer, int offset)
: _v3storage = new Float32List.view(buffer, offset, 3);
+ /// Generate random vector in the range (0, 0, 0) to (1, 1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector3.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector3(rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_, double z_) {
_v3storage[0] = x_;
diff --git a/pub/vector_math/lib/src/vector_math/vector4.dart b/pub/vector_math/lib/src/vector_math/vector4.dart
index a63f4dd..72a2496 100644
--- a/pub/vector_math/lib/src/vector_math/vector4.dart
+++ b/pub/vector_math/lib/src/vector_math/vector4.dart
@@ -64,6 +64,14 @@
Vector4.fromBuffer(ByteBuffer buffer, int offset)
: _v4storage = new Float32List.view(buffer, offset, 4);
+ /// Generate random vector in the range (0, 0, 0, 0) to (1, 1, 1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector4.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector4(
+ rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_, double z_, double w_) {
_v4storage[3] = w_;
diff --git a/pub/vector_math/lib/src/vector_math_64/colors.dart b/pub/vector_math/lib/src/vector_math_64/colors.dart
index 9fa8d7a..d9917f6 100644
--- a/pub/vector_math/lib/src/vector_math_64/colors.dart
+++ b/pub/vector_math/lib/src/vector_math_64/colors.dart
@@ -72,17 +72,35 @@
}
/// Convert a [input] color to a hex string without a leading '#'. To include
- /// the alpha channel, set [alpha] to true, it is false by default.
- static String toHexString(Vector4 input, {bool alpha: false}) {
- var color = (input.r * 255).floor() << 16 |
- (input.g * 255).floor() << 8 |
- (input.b * 255).floor();
+ /// the alpha channel, set [alpha] to true, it is false by default. If [short]
+ /// is true, the resulting hex string might also be a short version, like #ff0
+ /// (default false).
+ static String toHexString(Vector4 input,
+ {bool alpha: false, bool short: false}) {
+ final r = (input.r * 0xFF).floor() & 0xFF;
+ final g = (input.g * 0xFF).floor() & 0xFF;
+ final b = (input.b * 0xFF).floor() & 0xFF;
+ final a = (input.a * 0xFF).floor() & 0xFF;
- if (alpha) {
- color |= (input.a * 255).floor() << 24;
+ final isShort = short &&
+ ((r >> 4) == (r & 0xF)) &&
+ ((g >> 4) == (g & 0xF)) &&
+ ((b >> 4) == (b & 0xF)) &&
+ (!alpha || (a >> 4) == (a & 0xF));
+
+ if (isShort) {
+ final rgb = (r & 0xF).toRadixString(16) +
+ (g & 0xF).toRadixString(16) +
+ (b & 0xF).toRadixString(16);
+
+ return alpha ? (a & 0xF).toRadixString(16) + rgb : rgb;
+ } else {
+ final rgb = r.toRadixString(16).padLeft(2, '0') +
+ g.toRadixString(16).padLeft(2, '0') +
+ b.toRadixString(16).padLeft(2, '0');
+
+ return alpha ? a.toRadixString(16).padLeft(2, '0') + rgb : rgb;
}
-
- return color.toRadixString(16);
}
/// Blend the [foreground] color over [background] color and store the color
diff --git a/pub/vector_math/lib/src/vector_math_64/matrix3.dart b/pub/vector_math/lib/src/vector_math_64/matrix3.dart
index c2890bd..2499d9a 100644
--- a/pub/vector_math/lib/src/vector_math_64/matrix3.dart
+++ b/pub/vector_math/lib/src/vector_math_64/matrix3.dart
@@ -899,4 +899,30 @@
double z = _m3storage[8];
return new Vector3(x, y, z);
}
+
+ /// Is [this] the identity matrix?
+ bool isIdentity() {
+ return _m3storage[0] == 1.0 // col 1
+ && _m3storage[1] == 0.0
+ && _m3storage[2] == 0.0
+ && _m3storage[3] == 0.0 // col 2
+ && _m3storage[4] == 1.0
+ && _m3storage[5] == 0.0
+ && _m3storage[6] == 0.0 // col 3
+ && _m3storage[7] == 0.0
+ && _m3storage[8] == 1.0;
+ }
+
+ /// Is [this] the zero matrix?
+ bool isZero() {
+ return _m3storage[0] == 0.0 // col 1
+ && _m3storage[1] == 0.0
+ && _m3storage[2] == 0.0
+ && _m3storage[3] == 0.0 // col 2
+ && _m3storage[4] == 0.0
+ && _m3storage[5] == 0.0
+ && _m3storage[6] == 0.0 // col 3
+ && _m3storage[7] == 0.0
+ && _m3storage[8] == 0.0;
+ }
}
diff --git a/pub/vector_math/lib/src/vector_math_64/matrix4.dart b/pub/vector_math/lib/src/vector_math_64/matrix4.dart
index b0bed20..96e260e 100644
--- a/pub/vector_math/lib/src/vector_math_64/matrix4.dart
+++ b/pub/vector_math/lib/src/vector_math_64/matrix4.dart
@@ -1980,4 +1980,44 @@
double z = _m4storage[10];
return new Vector3(x, y, z);
}
+
+ /// Is [this] the identity matrix?
+ bool isIdentity() {
+ return _m4storage[0] == 1.0 // col 1
+ && _m4storage[1] == 0.0
+ && _m4storage[2] == 0.0
+ && _m4storage[3] == 0.0
+ && _m4storage[4] == 0.0 // col 2
+ && _m4storage[5] == 1.0
+ && _m4storage[6] == 0.0
+ && _m4storage[7] == 0.0
+ && _m4storage[8] == 0.0 // col 3
+ && _m4storage[9] == 0.0
+ && _m4storage[10] == 1.0
+ && _m4storage[11] == 0.0
+ && _m4storage[12] == 0.0 // col 4
+ && _m4storage[13] == 0.0
+ && _m4storage[14] == 0.0
+ && _m4storage[15] == 1.0;
+ }
+
+ /// Is [this] the zero matrix?
+ bool isZero() {
+ return _m4storage[0] == 0.0 // col 1
+ && _m4storage[1] == 0.0
+ && _m4storage[2] == 0.0
+ && _m4storage[3] == 0.0
+ && _m4storage[4] == 0.0 // col 2
+ && _m4storage[5] == 0.0
+ && _m4storage[6] == 0.0
+ && _m4storage[7] == 0.0
+ && _m4storage[8] == 0.0 // col 3
+ && _m4storage[9] == 0.0
+ && _m4storage[10] == 0.0
+ && _m4storage[11] == 0.0
+ && _m4storage[12] == 0.0 // col 4
+ && _m4storage[13] == 0.0
+ && _m4storage[14] == 0.0
+ && _m4storage[15] == 0.0;
+ }
}
diff --git a/pub/vector_math/lib/src/vector_math_64/opengl.dart b/pub/vector_math/lib/src/vector_math_64/opengl.dart
index 1d2ba0e..b53dfaa 100644
--- a/pub/vector_math/lib/src/vector_math_64/opengl.dart
+++ b/pub/vector_math/lib/src/vector_math_64/opengl.dart
@@ -113,10 +113,16 @@
/// (always positive).
void setPerspectiveMatrix(Matrix4 perspectiveMatrix, double fovYRadians,
double aspectRatio, double zNear, double zFar) {
- double height = Math.tan(fovYRadians * 0.5) * zNear;
- double width = height * aspectRatio;
- setFrustumMatrix(
- perspectiveMatrix, -width, width, -height, height, zNear, zFar);
+ final double height = Math.tan(fovYRadians * 0.5);
+ final double width = height * aspectRatio;
+ final double near_minus_far = zNear - zFar;
+
+ final Matrix4 view = perspectiveMatrix..setZero();
+ view.setEntry(0, 0, 1.0 / width);
+ view.setEntry(1, 1, 1.0 / height);
+ view.setEntry(2, 2, (zFar + zNear) / near_minus_far);
+ view.setEntry(3, 2, -1.0);
+ view.setEntry(2, 3, (2.0 * zNear * zFar) / near_minus_far);
}
/// Constructs a new OpenGL perspective projection matrix.
@@ -136,6 +142,41 @@
return r;
}
+/// Constructs an OpenGL infinite projection matrix in [infiniteMatrix].
+/// [fovYRadians] specifies the field of view angle, in radians, in the y
+/// direction.
+/// [aspectRatio] specifies the aspect ratio that determines the field of view
+/// in the x direction. The aspect ratio of x (width) to y (height).
+/// [zNear] specifies the distance from the viewer to the near plane
+/// (always positive).
+void setInfiniteMatrix(Matrix4 infiniteMatrix, double fovYRadians,
+ double aspectRatio, double zNear) {
+ final double height = Math.tan(fovYRadians * 0.5);
+ final double width = height * aspectRatio;
+
+ final Matrix4 view = infiniteMatrix..setZero();
+ view.setEntry(0, 0, 1.0 / width);
+ view.setEntry(1, 1, 1.0 / height);
+ view.setEntry(2, 2, -1.0);
+ view.setEntry(3, 2, -1.0);
+ view.setEntry(2, 3, -2.0 * zNear);
+}
+
+/// Constructs a new OpenGL infinite projection matrix.
+///
+/// [fovYRadians] specifies the field of view angle, in radians, in the y
+/// direction.
+/// [aspectRatio] specifies the aspect ratio that determines the field of view
+/// in the x direction. The aspect ratio of x (width) to y (height).
+/// [zNear] specifies the distance from the viewer to the near plane
+/// (always positive).
+Matrix4 makeInfiniteMatrix(
+ double fovYRadians, double aspectRatio, double zNear) {
+ Matrix4 r = new Matrix4.zero();
+ setInfiniteMatrix(r, fovYRadians, aspectRatio, zNear);
+ return r;
+}
+
/// Constructs an OpenGL perspective projection matrix in [perspectiveMatrix].
///
/// [left], [right] specify the coordinates for the left and right vertical
diff --git a/pub/vector_math/lib/src/vector_math_64/third_party/noise.dart b/pub/vector_math/lib/src/vector_math_64/third_party/noise.dart
index 7d81c6a..bb1b81f 100644
--- a/pub/vector_math/lib/src/vector_math_64/third_party/noise.dart
+++ b/pub/vector_math/lib/src/vector_math_64/third_party/noise.dart
@@ -28,53 +28,53 @@
class SimplexNoise {
static final _grad3 = <List<double>>[
- [1, 1, 0],
- [-1, 1, 0],
- [1, -1, 0],
- [-1, -1, 0],
- [1, 0, 1],
- [-1, 0, 1],
- [1, 0, -1],
- [-1, 0, -1],
- [0, 1, 1],
- [0, -1, 1],
- [0, 1, -1],
- [0, -1, -1]
+ [1.0, 1.0, 0.0],
+ [-1.0, 1.0, 0.0],
+ [1.0, -1.0, 0.0],
+ [-1.0, -1.0, 0.0],
+ [1.0, 0.0, 1.0],
+ [-1.0, 0.0, 1.0],
+ [1.0, 0.0, -1.0],
+ [-1.0, 0.0, -1.0],
+ [0.0, 1.0, 1.0],
+ [0.0, -1.0, 1.0],
+ [0.0, 1.0, -1.0],
+ [0.0, -1.0, -1.0]
];
static final _grad4 = <List<double>>[
- [0, 1, 1, 1],
- [0, 1, 1, -1],
- [0, 1, -1, 1],
- [0, 1, -1, -1],
- [0, -1, 1, 1],
- [0, -1, 1, -1],
- [0, -1, -1, 1],
- [0, -1, -1, -1],
- [1, 0, 1, 1],
- [1, 0, 1, -1],
- [1, 0, -1, 1],
- [1, 0, -1, -1],
- [-1, 0, 1, 1],
- [-1, 0, 1, -1],
- [-1, 0, -1, 1],
- [-1, 0, -1, -1],
- [1, 1, 0, 1],
- [1, 1, 0, -1],
- [1, -1, 0, 1],
- [1, -1, 0, -1],
- [-1, 1, 0, 1],
- [-1, 1, 0, -1],
- [-1, -1, 0, 1],
- [-1, -1, 0, -1],
- [1, 1, 1, 0],
- [1, 1, -1, 0],
- [1, -1, 1, 0],
- [1, -1, -1, 0],
- [-1, 1, 1, 0],
- [-1, 1, -1, 0],
- [-1, -1, 1, 0],
- [-1, -1, -1, 0]
+ [0.0, 1.0, 1.0, 1.0],
+ [0.0, 1.0, 1.0, -1.0],
+ [0.0, 1.0, -1.0, 1.0],
+ [0.0, 1.0, -1.0, -1.0],
+ [0.0, -1.0, 1.0, 1.0],
+ [0.0, -1.0, 1.0, -1.0],
+ [0.0, -1.0, -1.0, 1.0],
+ [0.0, -1.0, -1.0, -1.0],
+ [1.0, 0.0, 1.0, 1.0],
+ [1.0, 0.0, 1.0, -1.0],
+ [1.0, 0.0, -1.0, 1.0],
+ [1.0, 0.0, -1.0, -1.0],
+ [-1.0, 0.0, 1.0, 1.0],
+ [-1.0, 0.0, 1.0, -1.0],
+ [-1.0, 0.0, -1.0, 1.0],
+ [-1.0, 0.0, -1.0, -1.0],
+ [1.0, 1.0, 0.0, 1.0],
+ [1.0, 1.0, 0.0, -1.0],
+ [1.0, -1.0, 0.0, 1.0],
+ [1.0, -1.0, 0.0, -1.0],
+ [-1.0, 1.0, 0.0, 1.0],
+ [-1.0, 1.0, 0.0, -1.0],
+ [-1.0, -1.0, 0.0, 1.0],
+ [-1.0, -1.0, 0.0, -1.0],
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, -1.0, 0.0],
+ [1.0, -1.0, 1.0, 0.0],
+ [1.0, -1.0, -1.0, 0.0],
+ [-1.0, 1.0, 1.0, 0.0],
+ [-1.0, 1.0, -1.0, 0.0],
+ [-1.0, -1.0, 1.0, 0.0],
+ [-1.0, -1.0, -1.0, 0.0]
];
// To remove the need for index wrapping, double the permutation table length
@@ -101,7 +101,7 @@
if (r == null) {
r = new Math.Random();
}
- List p =
+ List<int> p =
new List<int>.generate(256, (i) => r.nextInt(256), growable: false);
_perm = new List<int>.generate(p.length * 2, (i) => p[i % p.length],
growable: false);
diff --git a/pub/vector_math/lib/src/vector_math_64/vector2.dart b/pub/vector_math/lib/src/vector_math_64/vector2.dart
index 7559026..7e4e6e1 100644
--- a/pub/vector_math/lib/src/vector_math_64/vector2.dart
+++ b/pub/vector_math/lib/src/vector_math_64/vector2.dart
@@ -54,6 +54,13 @@
Vector2.fromBuffer(ByteBuffer buffer, int offset)
: _v2storage = new Float64List.view(buffer, offset, 2);
+ /// Generate random vector in the range (0, 0) to (1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector2.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector2(rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_) {
_v2storage[0] = x_;
@@ -154,7 +161,7 @@
}
/// Normalize [this]. Returns length of vector before normalization.
- /// /// DEPRCATED: Use [normalize].
+ /// DEPRECATED: Use [normalize].
@deprecated
double normalizeLength() => normalize();
@@ -180,6 +187,33 @@
return dx * dx + dy * dy;
}
+ /// Returns the angle between [this] vector and [other] in radians.
+ double angleTo(Vector2 other) {
+ final otherStorage = other._v2storage;
+ if (_v2storage[0] == otherStorage[0] &&
+ _v2storage[1] == otherStorage[1]) {
+ return 0.0;
+ }
+
+ final d = dot(other);
+
+ return Math.acos(d.clamp(-1.0, 1.0));
+ }
+
+ /// Returns the signed angle between [this] and [other] in radians.
+ double angleToSigned(Vector2 other) {
+ final otherStorage = other._v2storage;
+ if (_v2storage[0] == otherStorage[0] &&
+ _v2storage[1] == otherStorage[1]) {
+ return 0.0;
+ }
+
+ final s = cross(other);
+ final c = dot(other);
+
+ return Math.atan2(s, c);
+ }
+
/// Inner product.
double dot(Vector2 other) {
final otherStorage = other._v2storage;
diff --git a/pub/vector_math/lib/src/vector_math_64/vector3.dart b/pub/vector_math/lib/src/vector_math_64/vector3.dart
index 79cfa81..40dc376 100644
--- a/pub/vector_math/lib/src/vector_math_64/vector3.dart
+++ b/pub/vector_math/lib/src/vector_math_64/vector3.dart
@@ -58,6 +58,13 @@
Vector3.fromBuffer(ByteBuffer buffer, int offset)
: _v3storage = new Float64List.view(buffer, offset, 3);
+ /// Generate random vector in the range (0, 0, 0) to (1, 1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector3.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector3(rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_, double z_) {
_v3storage[0] = x_;
diff --git a/pub/vector_math/lib/src/vector_math_64/vector4.dart b/pub/vector_math/lib/src/vector_math_64/vector4.dart
index 8b234e4..e2d09df 100644
--- a/pub/vector_math/lib/src/vector_math_64/vector4.dart
+++ b/pub/vector_math/lib/src/vector_math_64/vector4.dart
@@ -64,6 +64,14 @@
Vector4.fromBuffer(ByteBuffer buffer, int offset)
: _v4storage = new Float64List.view(buffer, offset, 4);
+ /// Generate random vector in the range (0, 0, 0, 0) to (1, 1, 1, 1). You can
+ /// optionally pass your own random number generator.
+ factory Vector4.random([Math.Random rng]) {
+ rng = rng == null ? new Math.Random() : rng;
+ return new Vector4(
+ rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
+ }
+
/// Set the values of the vector.
void setValues(double x_, double y_, double z_, double w_) {
_v4storage[3] = w_;
diff --git a/pub/vector_math/pubspec.yaml b/pub/vector_math/pubspec.yaml
index e6b3811..c85ec11 100644
--- a/pub/vector_math/pubspec.yaml
+++ b/pub/vector_math/pubspec.yaml
@@ -1,5 +1,5 @@
name: vector_math
-version: 2.0.3
+version: 2.0.4
author: John McCutchan <john@johnmccutchan.com>
description: A Vector Math library for 2D and 3D applications.
homepage: https://github.com/google/vector_math.dart