Version 1.4.0-dev.3.0
svn merge -r 34916:35259 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@35275 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/AUTHORS b/AUTHORS
index dc0e928..c2622b3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -28,4 +28,6 @@
Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
Victor Berchet <victor.berchet@gmail.com>
Roel Spilker <r.spilker@gmail.com>
-Martin Charles <martincharles07@gmail.com>
\ No newline at end of file
+Martin Charles <martincharles07@gmail.com>
+Anders Holmgren <andersmholmgren@gmail.com>
+K. Alex Gann <k.alexgann@gmail.com>
\ No newline at end of file
diff --git a/pkg/analysis_server/bin/server.dart b/pkg/analysis_server/bin/server.dart
index ef37d27..84b7702 100644
--- a/pkg/analysis_server/bin/server.dart
+++ b/pkg/analysis_server/bin/server.dart
@@ -2,12 +2,12 @@
// 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:analysis_server/http_server.dart';
+import 'package:analysis_server/driver.dart';
/**
* Create and run an HTTP-based analysis server.
*/
void main(List<String> args) {
- HttpAnalysisServer server = new HttpAnalysisServer();
- server.start(args);
+ Driver driver = new Driver();
+ driver.start(args);
}
diff --git a/pkg/analysis_server/lib/driver.dart b/pkg/analysis_server/lib/driver.dart
new file mode 100644
index 0000000..911cbc6
--- /dev/null
+++ b/pkg/analysis_server/lib/driver.dart
@@ -0,0 +1,97 @@
+// 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.
+
+library driver;
+
+import 'dart:io';
+
+import 'package:analysis_server/http_server.dart';
+import 'package:analysis_server/src/socket_server.dart';
+import 'package:analysis_server/stdio_server.dart';
+import 'package:args/args.dart';
+
+/**
+ * The [Driver] class represents a single running instance of the analysis
+ * server application. It is responsible for parsing command line options
+ * and starting the HTTP and/or stdio servers.
+ */
+class Driver {
+ /**
+ * The name of the application that is used to start a server.
+ */
+ static const BINARY_NAME = 'server';
+
+ /**
+ * The name of the option used to print usage information.
+ */
+ static const String HELP_OPTION = "help";
+
+ /**
+ * The name of the option used to specify the port to which the server will
+ * connect.
+ */
+ static const String PORT_OPTION = "port";
+
+ SocketServer socketServer = new SocketServer();
+
+ HttpAnalysisServer httpServer;
+
+ StdioAnalysisServer stdioServer;
+
+ Driver() {
+ httpServer = new HttpAnalysisServer(socketServer);
+ stdioServer = new StdioAnalysisServer(socketServer);
+ }
+
+ /**
+ * Use the given command-line arguments to start this server.
+ */
+ void start(List<String> args) {
+ ArgParser parser = new ArgParser();
+ parser.addFlag(HELP_OPTION, help:
+ "print this help message without starting a server", defaultsTo: false,
+ negatable: false);
+ parser.addOption(PORT_OPTION, help:
+ "[port] the port on which the server will listen");
+
+ ArgResults results = parser.parse(args);
+ if (results[HELP_OPTION]) {
+ _printUsage(parser);
+ return;
+ }
+ int port;
+ bool serve_http = false;
+ if (results[PORT_OPTION] != null) {
+ serve_http = true;
+ try {
+ port = int.parse(results[PORT_OPTION]);
+ } on FormatException {
+ print('Invalid port number: ${results[PORT_OPTION]}');
+ print('');
+ _printUsage(parser);
+ exitCode = 1;
+ return;
+ }
+ }
+
+ if (serve_http) {
+ httpServer.serveHttp(port);
+ }
+ stdioServer.serveStdio().then((_) {
+ if (serve_http) {
+ httpServer.close();
+ }
+ });
+ }
+
+ /**
+ * Print information about how to use the server.
+ */
+ void _printUsage(ArgParser parser) {
+ print('Usage: $BINARY_NAME [flags]');
+ print('');
+ print('Supported flags are:');
+ print(parser.getUsage());
+ }
+}
diff --git a/pkg/analysis_server/lib/http_server.dart b/pkg/analysis_server/lib/http_server.dart
index fb88f79..af63b70 100644
--- a/pkg/analysis_server/lib/http_server.dart
+++ b/pkg/analysis_server/lib/http_server.dart
@@ -4,14 +4,12 @@
library http.server;
+import 'dart:async';
import 'dart:io';
-import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/channel.dart';
-import 'package:analysis_server/src/domain_context.dart';
-import 'package:analysis_server/src/domain_server.dart';
import 'package:analysis_server/src/get_handler.dart';
-import 'package:args/args.dart';
+import 'package:analysis_server/src/socket_server.dart';
/**
* Instances of the class [HttpServer] implement a simple HTTP server. The
@@ -20,26 +18,10 @@
*/
class HttpAnalysisServer {
/**
- * The name of the application that is used to start a server.
+ * An object that can handle either a WebSocket connection or a connection
+ * to the client over stdio.
*/
- static const BINARY_NAME = 'server';
-
- /**
- * The name of the option used to print usage information.
- */
- static const String HELP_OPTION = "help";
-
- /**
- * The name of the option used to specify the port to which the server will
- * connect.
- */
- static const String PORT_OPTION = "port";
-
- /**
- * The analysis server that was created when an UPGRADE request was received,
- * or `null` if no such request has yet been received.
- */
- AnalysisServer analysisServer;
+ SocketServer socketServer;
/**
* An object that can handle GET requests.
@@ -49,47 +31,12 @@
/**
* Initialize a newly created HTTP server.
*/
- HttpAnalysisServer();
+ HttpAnalysisServer(this.socketServer);
/**
- * Use the given command-line arguments to start this server.
+ * Future that is completed with the HTTP server once it is running.
*/
- void start(List<String> args) {
- ArgParser parser = new ArgParser();
- parser.addFlag(
- HELP_OPTION,
- help: "print this help message without starting a server",
- defaultsTo: false,
- negatable: false);
- parser.addOption(
- PORT_OPTION,
- help: "[port] the port on which the server will listen");
-
- ArgResults results = parser.parse(args);
- if (results[HELP_OPTION]) {
- _printUsage(parser);
- return;
- }
- if (results[PORT_OPTION] == null) {
- print('Missing required port number');
- print('');
- _printUsage(parser);
- exitCode = 1;
- return;
- }
-
- try {
- int port = int.parse(results[PORT_OPTION]);
- HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then(_handleServer);
- print('Listening on port $port');
- } on FormatException {
- print('Invalid port number: ${results[PORT_OPTION]}');
- print('');
- _printUsage(parser);
- exitCode = 1;
- return;
- }
- }
+ Future<HttpServer> _server;
/**
* Attach a listener to a newly created HTTP server.
@@ -98,10 +45,6 @@
httServer.listen((HttpRequest request) {
List<String> updateValues = request.headers[HttpHeaders.UPGRADE];
if (updateValues != null && updateValues.indexOf('websocket') >= 0) {
- if (analysisServer != null) {
- _returnServerAlreadyStarted(request);
- return;
- }
WebSocketTransformer.upgrade(request).then((WebSocket websocket) {
_handleWebSocket(websocket);
});
@@ -118,8 +61,7 @@
*/
void _handleGetRequest(HttpRequest request) {
if (getHandler == null) {
- getHandler = new GetHandler();
- getHandler.server = analysisServer;
+ getHandler = new GetHandler(socketServer);
}
getHandler.handleGetRequest(request);
}
@@ -129,44 +71,7 @@
* running an analysis server on a [WebSocket]-based communication channel.
*/
void _handleWebSocket(WebSocket socket) {
- analysisServer = new AnalysisServer(new WebSocketServerChannel(socket));
- _initializeHandlers(analysisServer);
- if (getHandler != null) {
- getHandler.server = analysisServer;
- }
- analysisServer.run();
- }
-
- /**
- * Initialize the handlers to be used by the given [server].
- */
- void _initializeHandlers(AnalysisServer server) {
- server.handlers = [
- new ServerDomainHandler(server),
- new ContextDomainHandler(server),
- ];
- }
-
- /**
- * Print information about how to use the server.
- */
- void _printUsage(ArgParser parser) {
- print('Usage: $BINARY_NAME [flags]');
- print('');
- print('Supported flags are:');
- print(parser.getUsage());
- }
-
- /**
- * Return an error in response to an UPGRADE request received after the server
- * has already been started by a previous UPGRADE request.
- */
- void _returnServerAlreadyStarted(HttpRequest request) {
- HttpResponse response = request.response;
- response.statusCode = HttpStatus.SERVICE_UNAVAILABLE;
- response.headers.add(HttpHeaders.CONTENT_TYPE, "text/plain");
- response.write('The server has already been started');
- response.close();
+ socketServer.createAnalysisServer(new WebSocketServerChannel(socket));
}
/**
@@ -180,4 +85,18 @@
response.write('Not found');
response.close();
}
+
+ /**
+ * Begin serving HTTP requests over the given port.
+ */
+ void serveHttp(int port) {
+ _server = HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port);
+ _server.then(_handleServer);
+ }
+
+ void close() {
+ _server.then((HttpServer server) {
+ server.close();
+ });
+ }
}
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 1c9004f..f63bd6e 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -32,6 +32,11 @@
static const String SOURCE_PARAM = 'source';
/**
+ * The event name of the connected notification.
+ */
+ static const String CONNECTED_NOTIFICATION = 'server.connected';
+
+ /**
* The channel from which requests are received and to which responses should
* be sent.
*/
@@ -66,6 +71,8 @@
AnalysisServer(this.channel) {
AnalysisEngine.instance.logger = new AnalysisLogger();
running = true;
+ Notification notification = new Notification(CONNECTED_NOTIFICATION);
+ channel.sendNotification(notification);
channel.listen(handleRequest, onDone: done, onError: error);
}
diff --git a/pkg/analysis_server/lib/src/channel.dart b/pkg/analysis_server/lib/src/channel.dart
index 6d60abd..f6b3230 100644
--- a/pkg/analysis_server/lib/src/channel.dart
+++ b/pkg/analysis_server/lib/src/channel.dart
@@ -170,6 +170,66 @@
}
/**
+ * Instances of the class [ByteStreamServerChannel] implement a
+ * [ClientCommunicationChannel] that uses a stream and a sink (typically,
+ * standard input and standard output) to communicate with servers.
+ */
+class ByteStreamServerChannel implements ServerCommunicationChannel {
+ final Stream input;
+ final IOSink output;
+
+ /**
+ * Completer that will be signalled when the input stream is closed.
+ */
+ final Completer _closed = new Completer();
+
+ ByteStreamServerChannel(this.input, this.output);
+
+ /**
+ * Future that will be completed when the input stream is closed.
+ */
+ Future get closed {
+ return _closed.future;
+ }
+
+ @override
+ void listen(void onRequest(Request request), {Function onError, void
+ onDone()}) {
+ input.transform((new Utf8Codec()).decoder).transform(new LineSplitter()
+ ).listen((String data) => _readRequest(data, onRequest), onError: onError,
+ onDone: () {
+ _closed.complete();
+ onDone();
+ });
+ }
+
+ @override
+ void sendNotification(Notification notification) {
+ output.writeln(JSON.encode(notification.toJson()));
+ }
+
+ @override
+ void sendResponse(Response response) {
+ output.writeln(JSON.encode(response.toJson()));
+ }
+
+ /**
+ * Read a request from the given [data] and use the given function to handle
+ * the request.
+ */
+ void _readRequest(Object data, void onRequest(Request request)) {
+ // Parse the string as a JSON descriptor and process the resulting
+ // structure as a request.
+ Request request = new Request.fromString(data);
+ if (request == null) {
+ sendResponse(new Response.invalidRequestFormat());
+ return;
+ }
+ onRequest(request);
+ }
+}
+
+/**
* Instances of the class [JsonStreamDecoder] convert JSON strings to JSON
* maps.
*/
diff --git a/pkg/analysis_server/lib/src/domain_context.dart b/pkg/analysis_server/lib/src/domain_context.dart
index 0fc8759..0126866 100644
--- a/pkg/analysis_server/lib/src/domain_context.dart
+++ b/pkg/analysis_server/lib/src/domain_context.dart
@@ -135,7 +135,7 @@
*/
Response applyChanges(Request request) {
AnalysisContext context = getAnalysisContext(request);
- Map<String, Object> changesData = request.getRequiredParameter(CHANGES_PARAM);
+ RequestDatum changesData = request.getRequiredParameter(CHANGES_PARAM);
ChangeSet changeSet = createChangeSet(
request,
context.sourceFactory,
@@ -150,7 +150,8 @@
* Convert the given JSON object into a [ChangeSet], using the given
* [sourceFactory] to convert the embedded strings into sources.
*/
- ChangeSet createChangeSet(Request request, SourceFactory sourceFactory, Map<String, Object> jsonData) {
+ ChangeSet createChangeSet(Request request, SourceFactory sourceFactory,
+ RequestDatum jsonData) {
ChangeSet changeSet = new ChangeSet();
convertSources(request, sourceFactory, jsonData[ADDED_PARAM], (Source source) {
changeSet.addedSource(source);
@@ -170,11 +171,8 @@
* [handler]. Otherwise, throw an exception indicating that the data in the
* request was not valid.
*/
- void convertSources(Request request, SourceFactory sourceFactory, Object sources, void handler(Source source)) {
- if (sources is! List<String>) {
- throw new RequestFailure(new Response(request.id, new RequestError(1, 'Invalid sources')));
- }
- convertToSources(sourceFactory, sources).forEach(handler);
+ void convertSources(Request request, SourceFactory sourceFactory, RequestDatum sources, void handler(Source source)) {
+ convertToSources(sourceFactory, sources.asStringList()).forEach(handler);
}
/**
@@ -204,21 +202,21 @@
* throw a [RequestFailure] exception if the analysis options are not valid.
*/
AnalysisOptions createAnalysisOptions(Request request) {
- Map<String, Object> optionsData = request.getRequiredParameter(OPTIONS_PARAM);
+ RequestDatum optionsData = request.getRequiredParameter(OPTIONS_PARAM);
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
- optionsData.forEach((String key, Object value) {
+ optionsData.forEachMap((String key, RequestDatum value) {
if (key == CACHE_SIZE_OPTION) {
- options.cacheSize = request.toInt(value);
+ options.cacheSize = value.asInt();
} else if (key == GENERATE_HINTS_OPTION) {
- options.hint = request.toBool(value);
+ options.hint = value.asBool();
} else if (key == GENERATE_DART2JS_OPTION) {
- options.dart2jsHint = request.toBool(value);
+ options.dart2jsHint = value.asBool();
} else if (key == PROVIDE_ERRORS_OPTION) {
-// options.provideErrors = toBool(request, value);
+// options.provideErrors = value.asBool();
} else if (key == PROVIDE_NAVIGATION_OPTION) {
-// options.provideNavigation = toBool(request, value);
+// options.provideNavigation = value.asBool();
} else if (key == PROVIDE_OUTLINE_OPTION) {
-// options.provideOutline = toBool(request, value);
+// options.provideOutline = value.asBool();
} else {
throw new RequestFailure(new Response.unknownAnalysisOption(request, key));
}
@@ -232,7 +230,7 @@
*/
Response setPrioritySources(Request request) {
AnalysisContext context = getAnalysisContext(request);
- List<String> sourcesData = request.getRequiredParameter(SOURCES_PARAM);
+ List<String> sourcesData = request.getRequiredParameter(SOURCES_PARAM).asStringList();
List<Source> sources = convertToSources(context.sourceFactory, sourcesData);
context.analysisPriorityOrder = sources;
@@ -258,7 +256,7 @@
* the specified context does not exist.
*/
AnalysisContext getAnalysisContext(Request request) {
- String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM);
+ String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
AnalysisContext context = server.contextMap[contextId];
if (context == null) {
throw new RequestFailure(new Response.contextDoesNotExist(request));
diff --git a/pkg/analysis_server/lib/src/domain_server.dart b/pkg/analysis_server/lib/src/domain_server.dart
index 40b0205..102a79c 100644
--- a/pkg/analysis_server/lib/src/domain_server.dart
+++ b/pkg/analysis_server/lib/src/domain_server.dart
@@ -53,11 +53,6 @@
static const String SDK_DIRECTORY_PARAM = 'sdkDirectory';
/**
- * The name of the contextId result value.
- */
- static const String CONTEXT_ID_RESULT = 'contextId';
-
- /**
* The name of the version result value.
*/
static const String VERSION_RESULT = 'version';
@@ -97,14 +92,12 @@
* Clients, therefore, are responsible for managing the lifetime of contexts.
*/
Response createContext(Request request) {
- String sdkDirectory = request.getRequiredParameter(SDK_DIRECTORY_PARAM);
- Map<String, String> packageMap = request.getParameter(PACKAGE_MAP_PARAM);
+ String sdkDirectory = request.getRequiredParameter(SDK_DIRECTORY_PARAM).asString();
+ Map<String, String> packageMap = request.getParameter(PACKAGE_MAP_PARAM, {}).asStringMap();
- String baseContextId = new DateTime.now().millisecondsSinceEpoch.toRadixString(16);
- String contextId = baseContextId;
- int index = 1;
- while (server.contextMap.containsKey(contextId)) {
- contextId = '$baseContextId-$index';
+ String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
+ if (server.contextMap.containsKey(contextId)) {
+ return new Response.contextAlreadyExists(request);
}
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
// TODO(brianwilkerson) Use the information from the request to set the
@@ -125,7 +118,6 @@
server.contextMap[contextId] = context;
Response response = new Response(request.id);
- response.setResult(CONTEXT_ID_RESULT, contextId);
return response;
}
@@ -134,7 +126,7 @@
* will result in an error being returned.
*/
Response deleteContext(Request request) {
- String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM);
+ String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
AnalysisContext removedContext = server.contextMap.remove(contextId);
if (removedContext == null) {
diff --git a/pkg/analysis_server/lib/src/generated/service_computers.dart b/pkg/analysis_server/lib/src/generated/service_computers.dart
new file mode 100644
index 0000000..9c57ba0
--- /dev/null
+++ b/pkg/analysis_server/lib/src/generated/service_computers.dart
@@ -0,0 +1,400 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library service.computers;
+
+import 'package:analyzer/src/generated/java_core.dart' show JavaStringBuilder, StringUtils;
+import 'package:analyzer/src/generated/source.dart' show Source;
+import 'package:analyzer/src/generated/scanner.dart' show Token;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart' show Element;
+import 'service_interfaces.dart';
+
+/**
+ * A concrete implementation of [SourceRegion].
+ */
+class SourceRegionImpl implements SourceRegion {
+ final int offset;
+
+ final int length;
+
+ SourceRegionImpl(this.offset, this.length);
+
+ @override
+ bool containsInclusive(int x) => offset <= x && x <= offset + length;
+
+ @override
+ String toString() {
+ JavaStringBuilder builder = new JavaStringBuilder();
+ builder.append("[offset=");
+ builder.append(offset);
+ builder.append(", length=");
+ builder.append(length);
+ builder.append("]");
+ return builder.toString();
+ }
+}
+
+/**
+ * A concrete implementation of [Outline].
+ */
+class OutlineImpl implements Outline {
+ final Outline parent;
+
+ final OutlineKind kind;
+
+ final String name;
+
+ final int offset;
+
+ final int length;
+
+ final String arguments;
+
+ final String returnType;
+
+ List<Outline> children = Outline.EMPTY_ARRAY;
+
+ OutlineImpl(this.parent, this.kind, this.name, this.offset, this.length, this.arguments, this.returnType);
+
+ @override
+ String toString() {
+ JavaStringBuilder builder = new JavaStringBuilder();
+ builder.append("[name=");
+ builder.append(name);
+ builder.append(", kind=");
+ builder.append(kind);
+ builder.append(", offset=");
+ builder.append(offset);
+ builder.append(", length=");
+ builder.append(length);
+ builder.append(", arguments=");
+ builder.append(arguments);
+ builder.append(", return=");
+ builder.append(returnType);
+ builder.append(", children=[");
+ builder.append(StringUtils.join(children, ", "));
+ builder.append("]]");
+ return builder.toString();
+ }
+}
+
+/**
+ * A computer for [NavigationRegion]s in a Dart [CompilationUnit].
+ */
+class DartUnitNavigationComputer {
+ final CompilationUnit _unit;
+
+ List<NavigationRegion> _regions = [];
+
+ DartUnitNavigationComputer(this._unit);
+
+ /**
+ * Returns the computed [NavigationRegion]s, not `null`.
+ */
+ List<NavigationRegion> compute() {
+ _unit.accept(new RecursiveAstVisitor_DartUnitNavigationComputer_compute(this));
+ return new List.from(_regions);
+ }
+
+ /**
+ * If the given [Element] is not `null`, then creates a corresponding
+ * [NavigationRegion].
+ */
+ void _addRegionForNode(AstNode node, Element element) {
+ int offset = node.offset;
+ int length = node.length;
+ _addRegion(offset, length, element);
+ }
+
+ /**
+ * If the given [Element] is not `null`, then creates a corresponding
+ * [NavigationRegion].
+ */
+ void _addRegion(int offset, int length, Element element) {
+ NavigationTarget target = _createTarget(element);
+ if (target == null) {
+ return;
+ }
+ _regions.add(new NavigationRegionImpl(offset, length, <NavigationTarget> [target]));
+ }
+
+ /**
+ * If the given [Element] is not `null`, then creates a corresponding
+ * [NavigationRegion].
+ */
+ void _addRegionForToken(Token token, Element element) {
+ int offset = token.offset;
+ int length = token.length;
+ _addRegion(offset, length, element);
+ }
+
+ /**
+ * Returns the [NavigationTarget] for the given [Element], maybe `null` if
+ * `null` was given.
+ */
+ NavigationTarget _createTarget(Element element) {
+ if (element == null) {
+ return null;
+ }
+ return new NavigationTargetImpl(element.source, _getElementId(element), element.nameOffset, element.displayName.length);
+ }
+
+ String _getElementId(Element element) => element.location.encoding;
+}
+
+class RecursiveAstVisitor_DartUnitNavigationComputer_compute extends RecursiveAstVisitor<Object> {
+ final DartUnitNavigationComputer DartUnitNavigationComputer_this;
+
+ RecursiveAstVisitor_DartUnitNavigationComputer_compute(this.DartUnitNavigationComputer_this) : super();
+
+ @override
+ Object visitAssignmentExpression(AssignmentExpression node) {
+ DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+ return super.visitAssignmentExpression(node);
+ }
+
+ @override
+ Object visitBinaryExpression(BinaryExpression node) {
+ DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+ return super.visitBinaryExpression(node);
+ }
+
+ @override
+ Object visitIndexExpression(IndexExpression node) {
+ DartUnitNavigationComputer_this._addRegionForToken(node.rightBracket, node.bestElement);
+ return super.visitIndexExpression(node);
+ }
+
+ @override
+ Object visitPostfixExpression(PostfixExpression node) {
+ DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+ return super.visitPostfixExpression(node);
+ }
+
+ @override
+ Object visitPrefixExpression(PrefixExpression node) {
+ DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+ return super.visitPrefixExpression(node);
+ }
+
+ @override
+ Object visitSimpleIdentifier(SimpleIdentifier node) {
+ DartUnitNavigationComputer_this._addRegionForNode(node, node.bestElement);
+ return super.visitSimpleIdentifier(node);
+ }
+}
+
+/**
+ * A concrete implementation of [NavigationRegion].
+ */
+class NavigationRegionImpl extends SourceRegionImpl implements NavigationRegion {
+ final List<NavigationTarget> targets;
+
+ NavigationRegionImpl(int offset, int length, this.targets) : super(offset, length);
+
+ @override
+ String toString() {
+ JavaStringBuilder builder = new JavaStringBuilder();
+ builder.append(super.toString());
+ builder.append(" -> [");
+ builder.append(StringUtils.join(targets, ", "));
+ builder.append("]");
+ return builder.toString();
+ }
+}
+
+/**
+ * A concrete implementation of [NavigationTarget].
+ */
+class NavigationTargetImpl implements NavigationTarget {
+ final Source source;
+
+ final String elementId;
+
+ final int offset;
+
+ final int length;
+
+ NavigationTargetImpl(this.source, this.elementId, this.offset, this.length);
+
+ @override
+ String toString() {
+ JavaStringBuilder builder = new JavaStringBuilder();
+ builder.append("[offset=");
+ builder.append(offset);
+ builder.append(", length=");
+ builder.append(length);
+ builder.append(", source=");
+ builder.append(source);
+ builder.append(", element=");
+ builder.append(elementId);
+ builder.append("]");
+ return builder.toString();
+ }
+}
+
+/**
+ * A computer for [Outline]s in a Dart [CompilationUnit].
+ */
+class DartUnitOutlineComputer {
+ final CompilationUnit _unit;
+
+ DartUnitOutlineComputer(this._unit);
+
+ /**
+ * Returns the computed [Outline]s, not `null`.
+ */
+ Outline compute() {
+ OutlineImpl unitOutline = _newUnitOutline();
+ List<Outline> unitChildren = [];
+ for (CompilationUnitMember unitMember in _unit.declarations) {
+ if (unitMember is ClassDeclaration) {
+ ClassDeclaration classDeclartion = unitMember;
+ OutlineImpl classOutline = _newClassOutline(unitOutline, unitChildren, classDeclartion);
+ List<Outline> classChildren = [];
+ for (ClassMember classMember in classDeclartion.members) {
+ if (classMember is ConstructorDeclaration) {
+ ConstructorDeclaration constructorDeclaration = classMember;
+ _newConstructorOutline(classOutline, classChildren, constructorDeclaration);
+ }
+ if (classMember is FieldDeclaration) {
+ FieldDeclaration fieldDeclaration = classMember;
+ VariableDeclarationList fields = fieldDeclaration.fields;
+ if (fields != null) {
+ TypeName fieldType = fields.type;
+ String fieldTypeName = fieldType != null ? fieldType.toSource() : "";
+ for (VariableDeclaration field in fields.variables) {
+ _newField(classOutline, classChildren, fieldTypeName, field);
+ }
+ }
+ }
+ if (classMember is MethodDeclaration) {
+ MethodDeclaration methodDeclaration = classMember;
+ _newMethodOutline(classOutline, classChildren, methodDeclaration);
+ }
+ }
+ classOutline.children = new List.from(classChildren);
+ }
+ if (unitMember is FunctionDeclaration) {
+ FunctionDeclaration functionDeclaration = unitMember;
+ _newFunctionOutline(unitOutline, unitChildren, functionDeclaration);
+ }
+ if (unitMember is ClassTypeAlias) {
+ ClassTypeAlias alias = unitMember;
+ _newClassTypeAlias(unitOutline, unitChildren, alias);
+ }
+ if (unitMember is FunctionTypeAlias) {
+ FunctionTypeAlias alias = unitMember;
+ _newFunctionTypeAliasOutline(unitOutline, unitChildren, alias);
+ }
+ }
+ unitOutline.children = new List.from(unitChildren);
+ return unitOutline;
+ }
+
+ void _addLocalFunctionOutlines(OutlineImpl parenet, FunctionBody body) {
+ List<Outline> localOutlines = [];
+ body.accept(new RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines(this, parenet, localOutlines));
+ parenet.children = new List.from(localOutlines);
+ }
+
+ OutlineImpl _newClassOutline(Outline unitOutline, List<Outline> unitChildren, ClassDeclaration classDeclartion) {
+ SimpleIdentifier nameNode = classDeclartion.name;
+ String name = nameNode.name;
+ OutlineImpl outline = new OutlineImpl(unitOutline, OutlineKind.CLASS, name, nameNode.offset, name.length, null, null);
+ unitChildren.add(outline);
+ return outline;
+ }
+
+ void _newClassTypeAlias(Outline unitOutline, List<Outline> unitChildren, ClassTypeAlias alias) {
+ SimpleIdentifier nameNode = alias.name;
+ unitChildren.add(new OutlineImpl(unitOutline, OutlineKind.CLASS_TYPE_ALIAS, nameNode.name, nameNode.offset, nameNode.length, null, null));
+ }
+
+ void _newConstructorOutline(OutlineImpl classOutline, List<Outline> children, ConstructorDeclaration constructorDeclaration) {
+ Identifier returnType = constructorDeclaration.returnType;
+ String name = returnType.name;
+ int offset = returnType.offset;
+ int length = returnType.length;
+ SimpleIdentifier constructorNameNode = constructorDeclaration.name;
+ if (constructorNameNode != null) {
+ name += ".${constructorNameNode.name}";
+ offset = constructorNameNode.offset;
+ length = constructorNameNode.length;
+ }
+ FormalParameterList parameters = constructorDeclaration.parameters;
+ OutlineImpl outline = new OutlineImpl(classOutline, OutlineKind.CONSTRUCTOR, name, offset, length, parameters != null ? parameters.toSource() : "", null);
+ children.add(outline);
+ _addLocalFunctionOutlines(outline, constructorDeclaration.body);
+ }
+
+ void _newField(OutlineImpl classOutline, List<Outline> children, String fieldTypeName, VariableDeclaration field) {
+ SimpleIdentifier nameNode = field.name;
+ children.add(new OutlineImpl(classOutline, OutlineKind.FIELD, nameNode.name, nameNode.offset, nameNode.length, null, fieldTypeName));
+ }
+
+ void _newFunctionOutline(Outline unitOutline, List<Outline> unitChildren, FunctionDeclaration functionDeclaration) {
+ TypeName returnType = functionDeclaration.returnType;
+ SimpleIdentifier nameNode = functionDeclaration.name;
+ FunctionExpression functionExpression = functionDeclaration.functionExpression;
+ FormalParameterList parameters = functionExpression.parameters;
+ OutlineKind kind;
+ if (functionDeclaration.isGetter) {
+ kind = OutlineKind.GETTER;
+ } else if (functionDeclaration.isSetter) {
+ kind = OutlineKind.SETTER;
+ } else {
+ kind = OutlineKind.FUNCTION;
+ }
+ OutlineImpl outline = new OutlineImpl(unitOutline, kind, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : "");
+ unitChildren.add(outline);
+ _addLocalFunctionOutlines(outline, functionExpression.body);
+ }
+
+ void _newFunctionTypeAliasOutline(Outline unitOutline, List<Outline> unitChildren, FunctionTypeAlias alias) {
+ TypeName returnType = alias.returnType;
+ SimpleIdentifier nameNode = alias.name;
+ FormalParameterList parameters = alias.parameters;
+ unitChildren.add(new OutlineImpl(unitOutline, OutlineKind.FUNCTION_TYPE_ALIAS, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : ""));
+ }
+
+ void _newMethodOutline(OutlineImpl classOutline, List<Outline> children, MethodDeclaration methodDeclaration) {
+ TypeName returnType = methodDeclaration.returnType;
+ SimpleIdentifier nameNode = methodDeclaration.name;
+ FormalParameterList parameters = methodDeclaration.parameters;
+ OutlineKind kind;
+ if (methodDeclaration.isGetter) {
+ kind = OutlineKind.GETTER;
+ } else if (methodDeclaration.isSetter) {
+ kind = OutlineKind.SETTER;
+ } else {
+ kind = OutlineKind.METHOD;
+ }
+ OutlineImpl outline = new OutlineImpl(classOutline, kind, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : "");
+ children.add(outline);
+ _addLocalFunctionOutlines(outline, methodDeclaration.body);
+ }
+
+ OutlineImpl _newUnitOutline() => new OutlineImpl(null, OutlineKind.COMPILATION_UNIT, null, 0, 0, null, null);
+}
+
+class RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines extends RecursiveAstVisitor<Object> {
+ final DartUnitOutlineComputer DartUnitOutlineComputer_this;
+
+ OutlineImpl parenet;
+
+ List<Outline> localOutlines;
+
+ RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines(this.DartUnitOutlineComputer_this, this.parenet, this.localOutlines) : super();
+
+ @override
+ Object visitFunctionDeclaration(FunctionDeclaration node) {
+ DartUnitOutlineComputer_this._newFunctionOutline(parenet, localOutlines, node);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/src/generated/service_interfaces.dart b/pkg/analysis_server/lib/src/generated/service_interfaces.dart
new file mode 100644
index 0000000..fc3007f
--- /dev/null
+++ b/pkg/analysis_server/lib/src/generated/service_interfaces.dart
@@ -0,0 +1,337 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library service.interfaces;
+
+import 'package:analyzer/src/generated/java_core.dart' show Enum;
+import 'package:analyzer/src/generated/source.dart' show Source;
+
+/**
+ * The interface `HighlightRegion` defines the behavior of objects representing a particular
+ * syntactic or semantic meaning associated with a source region.
+ */
+abstract class HighlightRegion implements SourceRegion {
+ /**
+ * Return the type of highlight associated with the region.
+ *
+ * @return the type of highlight associated with the region
+ */
+ HighlightType get type;
+}
+
+/**
+ * The interface `NavigationTarget` defines the behavior of objects that provide information
+ * about the target of a navigation region.
+ */
+abstract class NavigationTarget {
+ /**
+ * Return the id of the element to which this target will navigate.
+ *
+ * @return the id of the element to which this target will navigate
+ */
+ String get elementId;
+
+ /**
+ * Return the length of the region to which the target will navigate.
+ *
+ * @return the length of the region to which the target will navigate
+ */
+ int get length;
+
+ /**
+ * Return the offset to the region to which the target will navigate.
+ *
+ * @return the offset to the region to which the target will navigate
+ */
+ int get offset;
+
+ /**
+ * Return the source containing the element to which this target will navigate.
+ *
+ * @return the source containing the element to which this target will navigate
+ */
+ Source get source;
+}
+
+/**
+ * The enumeration `OutlineKind` defines the various kinds of [Outline] items.
+ */
+class OutlineKind extends Enum<OutlineKind> {
+ static const OutlineKind CLASS = const OutlineKind('CLASS', 0);
+
+ static const OutlineKind CLASS_TYPE_ALIAS = const OutlineKind('CLASS_TYPE_ALIAS', 1);
+
+ static const OutlineKind CONSTRUCTOR = const OutlineKind('CONSTRUCTOR', 2);
+
+ static const OutlineKind GETTER = const OutlineKind('GETTER', 3);
+
+ static const OutlineKind FIELD = const OutlineKind('FIELD', 4);
+
+ static const OutlineKind FUNCTION = const OutlineKind('FUNCTION', 5);
+
+ static const OutlineKind FUNCTION_TYPE_ALIAS = const OutlineKind('FUNCTION_TYPE_ALIAS', 6);
+
+ static const OutlineKind METHOD = const OutlineKind('METHOD', 7);
+
+ static const OutlineKind SETTER = const OutlineKind('SETTER', 8);
+
+ static const OutlineKind TOP_LEVEL_VARIABLE = const OutlineKind('TOP_LEVEL_VARIABLE', 9);
+
+ static const OutlineKind COMPILATION_UNIT = const OutlineKind('COMPILATION_UNIT', 10);
+
+ static const List<OutlineKind> values = const [
+ CLASS,
+ CLASS_TYPE_ALIAS,
+ CONSTRUCTOR,
+ GETTER,
+ FIELD,
+ FUNCTION,
+ FUNCTION_TYPE_ALIAS,
+ METHOD,
+ SETTER,
+ TOP_LEVEL_VARIABLE,
+ COMPILATION_UNIT];
+
+ const OutlineKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `SourceSet` defines the behavior of objects that represent a set of
+ * [Source]s.
+ */
+abstract class SourceSet {
+ /**
+ * An instance of [SourceSet] for [SourceSetKind#ALL].
+ */
+ static final SourceSet ALL = new _ImplicitSourceSet(SourceSetKind.ALL);
+
+ /**
+ * An instance of [SourceSet] for [SourceSetKind#NON_SDK].
+ */
+ static final SourceSet NON_SDK = new _ImplicitSourceSet(SourceSetKind.NON_SDK);
+
+ /**
+ * An instance of [SourceSet] for [SourceSetKind#EXPLICITLY_ADDED].
+ */
+ static final SourceSet EXPLICITLY_ADDED = new _ImplicitSourceSet(SourceSetKind.EXPLICITLY_ADDED);
+
+ /**
+ * Return the kind of the this source set.
+ */
+ SourceSetKind get kind;
+
+ /**
+ * Returns [Source]s that belong to this source set, if [SourceSetKind#LIST] is used;
+ * an empty array otherwise.
+ */
+ List<Source> get sources;
+}
+
+/**
+ * The enumeration `SourceSetKind` defines the kinds of [SourceSet]s.
+ */
+class SourceSetKind extends Enum<SourceSetKind> {
+ static const SourceSetKind ALL = const SourceSetKind('ALL', 0);
+
+ static const SourceSetKind NON_SDK = const SourceSetKind('NON_SDK', 1);
+
+ static const SourceSetKind EXPLICITLY_ADDED = const SourceSetKind('EXPLICITLY_ADDED', 2);
+
+ static const SourceSetKind LIST = const SourceSetKind('LIST', 3);
+
+ static const List<SourceSetKind> values = const [ALL, NON_SDK, EXPLICITLY_ADDED, LIST];
+
+ const SourceSetKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `SourceRegion` defines the behavior of objects representing a range of
+ * characters within a [Source].
+ */
+abstract class SourceRegion {
+ /**
+ * Check if <code>x</code> is in [offset, offset + length] interval.
+ */
+ bool containsInclusive(int x);
+
+ /**
+ * Return the length of the region.
+ *
+ * @return the length of the region
+ */
+ int get length;
+
+ /**
+ * Return the offset to the beginning of the region.
+ *
+ * @return the offset to the beginning of the region
+ */
+ int get offset;
+}
+
+/**
+ * The enumeration `NotificationKind` defines the kinds of notification clients may subscribe
+ * for.
+ */
+class NotificationKind extends Enum<NotificationKind> {
+ static const NotificationKind ERRORS = const NotificationKind('ERRORS', 0);
+
+ static const NotificationKind HIGHLIGHT = const NotificationKind('HIGHLIGHT', 1);
+
+ static const NotificationKind NAVIGATION = const NotificationKind('NAVIGATION', 2);
+
+ static const NotificationKind OUTLINE = const NotificationKind('OUTLINE', 3);
+
+ static const List<NotificationKind> values = const [ERRORS, HIGHLIGHT, NAVIGATION, OUTLINE];
+
+ const NotificationKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `NavigationRegion` defines the behavior of objects representing a list of
+ * elements with which a source region is associated.
+ */
+abstract class NavigationRegion implements SourceRegion {
+ /**
+ * An empty array of navigation regions.
+ */
+ static final List<NavigationRegion> EMPTY_ARRAY = new List<NavigationRegion>(0);
+
+ /**
+ * Return the identifiers of the elements associated with the region.
+ *
+ * @return the identifiers of the elements associated with the region
+ */
+ List<NavigationTarget> get targets;
+}
+
+/**
+ * An implementation of [SourceSet] for some [SourceSetKind].
+ */
+class _ImplicitSourceSet implements SourceSet {
+ final SourceSetKind kind;
+
+ _ImplicitSourceSet(this.kind);
+
+ @override
+ List<Source> get sources => Source.EMPTY_ARRAY;
+
+ @override
+ String toString() => kind.toString();
+}
+
+/**
+ * The enumeration `HighlightType` defines the kinds of highlighting that can be associated
+ * with a region of text.
+ */
+class HighlightType extends Enum<HighlightType> {
+ static const HighlightType COMMENT_BLOCK = const HighlightType('COMMENT_BLOCK', 0);
+
+ static const HighlightType COMMENT_DOCUMENTATION = const HighlightType('COMMENT_DOCUMENTATION', 1);
+
+ static const HighlightType COMMENT_END_OF_LINE = const HighlightType('COMMENT_END_OF_LINE', 2);
+
+ static const HighlightType KEYWORD = const HighlightType('KEYWORD', 3);
+
+ static const HighlightType LITERAL_BOOLEAN = const HighlightType('LITERAL_BOOLEAN', 4);
+
+ static const HighlightType LITERAL_DOUBLE = const HighlightType('LITERAL_DOUBLE', 5);
+
+ static const HighlightType LITERAL_INTEGER = const HighlightType('LITERAL_INTEGER', 6);
+
+ static const HighlightType LITERAL_LIST = const HighlightType('LITERAL_LIST', 7);
+
+ static const HighlightType LITERAL_MAP = const HighlightType('LITERAL_MAP', 8);
+
+ static const HighlightType LITERAL_STRING = const HighlightType('LITERAL_STRING', 9);
+
+ static const List<HighlightType> values = const [
+ COMMENT_BLOCK,
+ COMMENT_DOCUMENTATION,
+ COMMENT_END_OF_LINE,
+ KEYWORD,
+ LITERAL_BOOLEAN,
+ LITERAL_DOUBLE,
+ LITERAL_INTEGER,
+ LITERAL_LIST,
+ LITERAL_MAP,
+ LITERAL_STRING];
+
+ const HighlightType(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `Outline` defines the behavior of objects that represent an outline for a
+ * single source.
+ */
+abstract class Outline {
+ /**
+ * An empty array of outlines.
+ */
+ static final List<Outline> EMPTY_ARRAY = new List<Outline>(0);
+
+ /**
+ * Return the argument list for the element, or `null` if the element is not a method or
+ * function. If the element has zero arguments, the string `"()"` will be returned.
+ *
+ * @return the argument list for the element
+ */
+ String get arguments;
+
+ /**
+ * Return an array containing the children of the element. The array will be empty if the element
+ * has no children.
+ *
+ * @return an array containing the children of the element
+ */
+ List<Outline> get children;
+
+ /**
+ * Return the kind of the element.
+ *
+ * @return the kind of the element
+ */
+ OutlineKind get kind;
+
+ /**
+ * Return the length of the element's name.
+ *
+ * @return the length of the element's name
+ */
+ int get length;
+
+ /**
+ * Return the name of the element.
+ *
+ * @return the name of the element
+ */
+ String get name;
+
+ /**
+ * Return the offset to the beginning of the element's name.
+ *
+ * @return the offset to the beginning of the element's name
+ */
+ int get offset;
+
+ /**
+ * Return the outline that either physically or logically encloses this outline. This will be
+ * `null` if this outline is a unit outline.
+ *
+ * @return the outline that encloses this outline
+ */
+ Outline get parent;
+
+ /**
+ * Return the return type of the element, or `null` if the element is not a method or
+ * function. If the element does not have a declared return type then an empty string will be
+ * returned.
+ *
+ * @return the return type of the element
+ */
+ String get returnType;
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/src/get_handler.dart b/pkg/analysis_server/lib/src/get_handler.dart
index c93e9bb..be7d47b 100644
--- a/pkg/analysis_server/lib/src/get_handler.dart
+++ b/pkg/analysis_server/lib/src/get_handler.dart
@@ -8,7 +8,7 @@
import 'package:analyzer/src/generated/engine.dart';
-import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/socket_server.dart';
/**
* Instances of the class [GetHandler] handle GET requests.
@@ -20,15 +20,14 @@
static const String STATUS_PATH = '/status';
/**
- * The analysis server whose status is to be reported on, or `null` if the
- * server has not yet been created.
+ * The socket server whose status is to be reported on.
*/
- AnalysisServer server;
+ SocketServer _server;
/**
* Initialize a newly created handler for GET requests.
*/
- GetHandler();
+ GetHandler(SocketServer this._server);
/**
* Handle a GET request received by the HTTP server.
@@ -55,7 +54,7 @@
response.write('</head>');
response.write('<body>');
response.write('<h1>Analysis Server</h1>');
- if (server == null) {
+ if (_server.analysisServer == null) {
response.write('<p>Not running</p>');
} else {
response.write('<p>Running</p>');
@@ -66,7 +65,7 @@
response,
['Context', 'ERROR', 'FLUSHED', 'IN_PROCESS', 'INVALID', 'VALID'],
true);
- server.contextMap.forEach((String key, AnalysisContext context) {
+ _server.analysisServer.contextMap.forEach((String key, AnalysisContext context) {
AnalysisContentStatistics statistics =
(context as AnalysisContextImpl).statistics;
int errorCount = 0;
@@ -90,8 +89,8 @@
validCount]);
});
response.write('</table>');
- server.contextMap.forEach((String key, AnalysisContext context) {
- response.write('<h2><a name="context_$key">Analysis Context: $key}</a></h2>');
+ _server.analysisServer.contextMap.forEach((String key, AnalysisContext context) {
+ response.write('<h2><a name="context_$key">Analysis Context: $key</a></h2>');
AnalysisContentStatistics statistics = (context as AnalysisContextImpl).statistics;
response.write('<table>');
_writeRow(
diff --git a/pkg/analysis_server/lib/src/protocol.dart b/pkg/analysis_server/lib/src/protocol.dart
index 4b5a561..ef58a8a 100644
--- a/pkg/analysis_server/lib/src/protocol.dart
+++ b/pkg/analysis_server/lib/src/protocol.dart
@@ -94,22 +94,28 @@
}
/**
- * Return the value of the parameter with the given [name], or `null` if there
- * is no such parameter associated with this request.
+ * Return the value of the parameter with the given [name], or [defaultValue]
+ * if there is no such parameter associated with this request.
*/
- Object getParameter(String name) => params[name];
+ RequestDatum getParameter(String name, dynamic defaultValue) {
+ Object value = params[name];
+ if (value == null) {
+ return new RequestDatum(this, "default for $name", defaultValue);
+ }
+ return new RequestDatum(this, name, params[name]);
+ }
/**
* Return the value of the parameter with the given [name], or throw a
* [RequestFailure] exception with an appropriate error message if there is no
* such parameter associated with this request.
*/
- Object getRequiredParameter(String name) {
+ RequestDatum getRequiredParameter(String name) {
Object value = params[name];
if (value == null) {
throw new RequestFailure(new Response.missingRequiredParameter(this, name));
}
- return value;
+ return new RequestDatum(this, name, value);
}
/**
@@ -120,40 +126,6 @@
}
/**
- * Convert the given [value] to a boolean, or throw a [RequestFailure]
- * exception if the [value] could not be converted.
- *
- * The value is typically the result of invoking either [getParameter] or
- * [getRequiredParameter].
- */
- bool toBool(Object value) {
- if (value is bool) {
- return value;
- } else if (value is String) {
- return value == 'true';
- }
- throw new RequestFailure(new Response.expectedBoolean(this, value));
- }
-
- /**
- * Convert the given [value] to an integer, or throw a [RequestFailure]
- * exception if the [value] could not be converted.
- *
- * The value is typically the result of invoking either [getParameter] or
- * [getRequiredParameter].
- */
- int toInt(Object value) {
- if (value is int) {
- return value;
- } else if (value is String) {
- return int.parse(value, onError: (String value) {
- throw new RequestFailure(new Response.expectedInteger(this, value));
- });
- }
- throw new RequestFailure(new Response.expectedInteger(this, value));
- }
-
- /**
* Return a table representing the structure of the Json object that will be
* sent to the client to represent this response.
*/
@@ -169,6 +141,134 @@
}
/**
+ * Instances of the class [RequestDatum] wrap a piece of data from a
+ * request parameter, and contain accessor methods which automatically validate
+ * and convert the data into the appropriate form.
+ */
+class RequestDatum {
+ /**
+ * Request object that should be referred to in any errors that are
+ * generated.
+ */
+ final Request request;
+
+ /**
+ * String description of how [datum] was obtained from the request.
+ */
+ final String path;
+
+ /**
+ * Value to be decoded and validated.
+ */
+ final dynamic datum;
+
+ /**
+ * Create a RequestDatum for decoding and validating [datum], which refers to
+ * [request] in any errors it reports.
+ */
+ RequestDatum(this.request, this.path, this.datum);
+
+ /**
+ * Validate that the datum is a Map containing the given [key], and return
+ * a [RequestDatum] containing the corresponding value.
+ */
+ RequestDatum operator [](String key) {
+ if (datum is! Map<String, dynamic>) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be a map"));
+ }
+ if (!datum.containsKey(key)) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "contain key '$key'"));
+ }
+ return new RequestDatum(request, "$path.$key", datum[key]);
+ }
+
+ /**
+ * Validate that the datum is a Map whose keys are strings, and call [f] on
+ * each key/value pair in the map.
+ */
+ void forEachMap(void f(String key, RequestDatum value)) {
+ if (datum is! Map<String, dynamic>) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be a map"));
+ }
+ datum.forEach((String key, dynamic value) {
+ f(key, new RequestDatum(request, "$path.$key", value));
+ });
+ }
+
+ /**
+ * Validate that the datum is an integer (or a string that can be parsed
+ * as an integer), and return the int.
+ */
+ int asInt() {
+ if (datum is int) {
+ return datum;
+ } else if (datum is String) {
+ return int.parse(datum, onError: (String value) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be an integer"));
+ });
+ }
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be an integer"));
+ }
+
+ /**
+ * Validate that the datum is a boolean (or a string that can be parsed
+ * as a boolean), and return the bool.
+ *
+ * The value is typically the result of invoking either [getParameter] or
+ * [getRequiredParameter].
+ */
+ bool asBool() {
+ if (datum is bool) {
+ return datum;
+ } else if (datum == 'true') {
+ return datum == 'true';
+ } else if (datum == 'false') {
+ return datum == 'false';
+ }
+ throw new RequestFailure(new Response.invalidParameter(request, datum,
+ "be a boolean"));
+ }
+
+ /**
+ * Validate that the datum is a string, and return it.
+ */
+ String asString() {
+ if (datum is! String) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be a string"));
+ }
+ return datum;
+ }
+
+ /**
+ * Validate that the datum is a list of strings, and return it.
+ */
+ List<String> asStringList() {
+ if (datum is! List<String>) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be a list of strings"));
+ }
+ return datum;
+ }
+
+ /**
+ * Validate that the datum is a map from strings to strings, and return it.
+ */
+ Map<String, String> asStringMap() {
+ if (datum is! Map<String, String>) {
+ throw new RequestFailure(new Response.invalidParameter(request, path,
+ "be a string map"));
+ }
+ return datum;
+ }
+}
+
+/**
* Instances of the class [Response] represent a response to a request.
*/
class Response {
@@ -222,19 +322,14 @@
/**
* Initialize a newly created instance to represent an error condition caused
- * by a [request] that was expected to have a boolean-valued parameter but was
- * passed a non-boolean value.
+ * by a [request] that had invalid parameter. [path] is the path to the
+ * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the
+ * parameter "foo" contained a key "bar" whose value was the wrong type).
+ * [expectation] is a description of the type of data that was expected.
*/
- Response.expectedBoolean(Request request, Object value)
- : this(request.id, new RequestError(-2, 'Expected a boolean value, but found "$value"'));
-
- /**
- * Initialize a newly created instance to represent an error condition caused
- * by a [request] that was expected to have a integer-valued parameter but was
- * passed a non-integer value.
- */
- Response.expectedInteger(Request request, Object value)
- : this(request.id, new RequestError(-3, 'Expected an integer value, but found "$value"'));
+ Response.invalidParameter(Request request, String path, String expectation)
+ : this(request.id, new RequestError(-2,
+ "Expected parameter $path to $expectation"));
/**
* Initialize a newly created instance to represent an error condition caused
@@ -265,6 +360,9 @@
Response.unknownRequest(Request request)
: this(request.id, new RequestError(-7, 'Unknown request'));
+ Response.contextAlreadyExists(Request request)
+ : this(request.id, new RequestError(-8, 'Context already exists'));
+
/**
* Initialize a newly created instance based upon the given JSON data
*/
@@ -353,6 +451,12 @@
* server. An error occurred on the server while parsing the JSON text.
*/
static const int CODE_PARSE_ERROR = -32700;
+
+ /**
+ * An error code indicating that the analysis server has already been
+ * started (and hence won't accept new connections).
+ */
+ static const int CODE_SERVER_ALREADY_STARTED = -32701;
/**
* An error code indicating an invalid request. The JSON sent is not a valid
@@ -414,6 +518,13 @@
RequestError.parseError() : this(CODE_PARSE_ERROR, "Parse error");
/**
+ * Initialize a newly created [Error] to indicate that the analysis server
+ * has already been started (and hence won't accept new connections).
+ */
+ RequestError.serverAlreadyStarted()
+ : this(CODE_SERVER_ALREADY_STARTED, "Server already started");
+
+ /**
* Initialize a newly created [Error] to indicate an invalid request. The
* JSON sent is not a valid [Request] object.
*/
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
new file mode 100644
index 0000000..1d74d62
--- /dev/null
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -0,0 +1,54 @@
+// 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.
+
+library socket.server;
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/channel.dart';
+import 'package:analysis_server/src/domain_context.dart';
+import 'package:analysis_server/src/domain_server.dart';
+import 'package:analysis_server/src/protocol.dart';
+
+/**
+ * Instances of the class [SocketServer] implement the common parts of
+ * http-based and stdio-based analysis servers. The primary responsibility of
+ * the SocketServer is to manage the lifetime of the AnalysisServer and to
+ * encode and decode the JSON messages exchanged with the client.
+ */
+class SocketServer {
+ /**
+ * The analysis server that was created when a client established a
+ * connection, or `null` if no such connection has yet been established.
+ */
+ AnalysisServer analysisServer;
+
+ /**
+ * Create an analysis server which will communicate with the client using the
+ * given serverChannel.
+ */
+ void createAnalysisServer(ServerCommunicationChannel serverChannel) {
+ if (analysisServer != null) {
+ var error = new RequestError.serverAlreadyStarted();
+ serverChannel.sendResponse(new Response('', error));
+ serverChannel.listen((Request request) {
+ serverChannel.sendResponse(new Response(request.id, error));
+ });
+ return;
+ }
+ analysisServer = new AnalysisServer(serverChannel);
+ _initializeHandlers(analysisServer);
+ analysisServer.run();
+ }
+
+ /**
+ * Initialize the handlers to be used by the given [server].
+ */
+ void _initializeHandlers(AnalysisServer server) {
+ server.handlers = [
+ new ServerDomainHandler(server),
+ new ContextDomainHandler(server),
+ ];
+ }
+
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/stdio_server.dart b/pkg/analysis_server/lib/stdio_server.dart
new file mode 100644
index 0000000..a0cc7e3
--- /dev/null
+++ b/pkg/analysis_server/lib/stdio_server.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.
+
+library stdio.server;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:analysis_server/src/channel.dart';
+import 'package:analysis_server/src/socket_server.dart';
+
+/**
+ * Instances of the class [StdioServer] implement a simple server operating
+ * over standard input and output. The primary responsibility of this server
+ * is to split incoming messages on newlines and pass them along to the
+ * analysis server.
+ */
+class StdioAnalysisServer {
+ /**
+ * An object that can handle either a WebSocket connection or a connection
+ * to the client over stdio.
+ */
+ SocketServer socketServer;
+
+ /**
+ * Initialize a newly created stdio server.
+ */
+ StdioAnalysisServer(this.socketServer);
+
+ /**
+ * Begin serving requests over stdio.
+ *
+ * Return a future that will be completed when stdin closes.
+ */
+ Future serveStdio() {
+ ByteStreamServerChannel serverChannel = new ByteStreamServerChannel(stdin,
+ stdout);
+ socketServer.createAnalysisServer(serverChannel);
+ return serverChannel.closed;
+ }
+}
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 5af2c46..031fd6e 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -6,7 +6,7 @@
environment:
sdk: '>=1.0.0 <2.0.0'
dependencies:
- analyzer: 0.13.0-dev.6
+ analyzer: 0.13.6
args: any
logging: any
dev_dependencies:
diff --git a/pkg/analysis_server/test/analysis_server_test.dart b/pkg/analysis_server/test/analysis_server_test.dart
index d1cc597..eeaebdf 100644
--- a/pkg/analysis_server/test/analysis_server_test.dart
+++ b/pkg/analysis_server/test/analysis_server_test.dart
@@ -16,7 +16,7 @@
main() {
group('AnalysisServer', () {
setUp(AnalysisServerTest.setUp);
-// test('createContext', AnalysisServerTest.createContext);
+ test('createContext', AnalysisServerTest.createContext);
test('echo', AnalysisServerTest.echo);
test('shutdown', AnalysisServerTest.shutdown);
test('unknownRequest', AnalysisServerTest.unknownRequest);
@@ -36,13 +36,11 @@
server.handlers = [new ServerDomainHandler(server)];
var request = new Request('my27', ServerDomainHandler.CREATE_CONTEXT_METHOD);
request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ request.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
return channel.sendRequest(request)
- .timeout(new Duration(seconds: 1))
.then((Response response) {
expect(response.id, equals('my27'));
expect(response.error, isNull);
- var contextId = response.result[ServerDomainHandler.CONTEXT_ID_RESULT];
- expect(contextId is String, isTrue);
});
}
@@ -50,7 +48,6 @@
server.handlers = [new EchoHandler()];
var request = new Request('my22', 'echo');
return channel.sendRequest(request)
- .timeout(new Duration(seconds: 1))
.then((Response response) {
expect(response.id, equals('my22'));
expect(response.error, isNull);
@@ -62,7 +59,6 @@
var request = new Request('my28', ServerDomainHandler.SHUTDOWN_METHOD);
request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
return channel.sendRequest(request)
- .timeout(new Duration(seconds: 1))
.then((Response response) {
expect(response.id, equals('my28'));
expect(response.error, isNull);
@@ -73,7 +69,6 @@
server.handlers = [new EchoHandler()];
var request = new Request('my22', 'randomRequest');
return channel.sendRequest(request)
- .timeout(new Duration(seconds: 1))
.then((Response response) {
expect(response.id, equals('my22'));
expect(response.error, isNotNull);
diff --git a/pkg/analysis_server/test/channel_test.dart b/pkg/analysis_server/test/channel_test.dart
index 6d58887..1f23745 100644
--- a/pkg/analysis_server/test/channel_test.dart
+++ b/pkg/analysis_server/test/channel_test.dart
@@ -5,6 +5,8 @@
library test.channel;
import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
import 'package:analysis_server/src/channel.dart';
import 'package:analysis_server/src/protocol.dart';
@@ -24,6 +26,19 @@
test('requestResponse', WebSocketChannelTest.requestResponse);
test('response', WebSocketChannelTest.response);
});
+ group('ByteStreamServerChannel', () {
+ setUp(ByteStreamServerChannelTest.setUp);
+ test('closed', ByteStreamServerChannelTest.closed);
+ test('listen_wellFormedRequest',
+ ByteStreamServerChannelTest.listen_wellFormedRequest);
+ test('listen_invalidRequest',
+ ByteStreamServerChannelTest.listen_invalidRequest);
+ test('listen_invalidJson', ByteStreamServerChannelTest.listen_invalidJson);
+ test('listen_streamError', ByteStreamServerChannelTest.listen_streamError);
+ test('listen_streamDone', ByteStreamServerChannelTest.listen_streamDone);
+ test('sendNotification', ByteStreamServerChannelTest.sendNotification);
+ test('sendResponse', ByteStreamServerChannelTest.sendResponse);
+ });
}
class WebSocketChannelTest {
@@ -156,4 +171,131 @@
expect(responsesReceived, hasLength(responseCount));
expect(notificationsReceived, hasLength(notificationCount));
}
-}
\ No newline at end of file
+}
+
+class ByteStreamServerChannelTest {
+ static ByteStreamServerChannel channel;
+
+ /**
+ * Sink that may be used to deliver data to the channel, as though it's
+ * coming from the client.
+ */
+ static IOSink inputSink;
+
+ /**
+ * Stream of lines sent back to the client by the channel.
+ */
+ static Stream<String> outputLineStream;
+
+ /**
+ * Stream of requests received from the channel via [listen()].
+ */
+ static Stream<Request> requestStream;
+
+ /**
+ * Stream of errors received from the channel via [listen()].
+ */
+ static Stream errorStream;
+
+ /**
+ * Future which is completed when then [listen()] reports [onDone].
+ */
+ static Future doneFuture;
+
+ static void setUp() {
+ StreamController<List<int>> inputStream = new StreamController<List<int>>();
+ inputSink = new IOSink(inputStream);
+ StreamController<List<int>> outputStream = new StreamController<List<int>>(
+ );
+ outputLineStream = outputStream.stream.transform((new Utf8Codec()).decoder
+ ).transform(new LineSplitter());
+ IOSink outputSink = new IOSink(outputStream);
+ channel = new ByteStreamServerChannel(inputStream.stream, outputSink);
+ StreamController<Request> requestStreamController =
+ new StreamController<Request>();
+ requestStream = requestStreamController.stream;
+ StreamController errorStreamController = new StreamController();
+ errorStream = errorStreamController.stream;
+ Completer doneCompleter = new Completer();
+ doneFuture = doneCompleter.future;
+ channel.listen((Request request) {
+ requestStreamController.add(request);
+ }, onError: (error) {
+ errorStreamController.add(error);
+ }, onDone: () {
+ doneCompleter.complete();
+ });
+ }
+
+ static Future closed() {
+ return inputSink.close().then((_) => channel.closed.timeout(new Duration(
+ seconds: 1)));
+ }
+
+ static Future listen_wellFormedRequest() {
+ inputSink.writeln('{"id":"0","method":"server.version"}');
+ return inputSink.flush().then((_) => requestStream.first.timeout(
+ new Duration(seconds: 1))).then((Request request) {
+ expect(request.id, equals("0"));
+ expect(request.method, equals("server.version"));
+ });
+ }
+
+ static Future listen_invalidRequest() {
+ inputSink.writeln('{"id":"0"}');
+ return inputSink.flush().then((_) => outputLineStream.first.timeout(
+ new Duration(seconds: 1))).then((String response) {
+ var jsonResponse = new JsonCodec().decode(response);
+ expect(jsonResponse, isMap);
+ expect(jsonResponse, contains('error'));
+ expect(jsonResponse['error'], isNotNull);
+ });
+ }
+
+ static Future listen_invalidJson() {
+ inputSink.writeln('{"id":');
+ return inputSink.flush().then((_) => outputLineStream.first.timeout(
+ new Duration(seconds: 1))).then((String response) {
+ var jsonResponse = new JsonCodec().decode(response);
+ expect(jsonResponse, isMap);
+ expect(jsonResponse, contains('error'));
+ expect(jsonResponse['error'], isNotNull);
+ });
+ }
+
+ static Future listen_streamError() {
+ var error = new Error();
+ inputSink.addError(error);
+ return inputSink.flush().then((_) => errorStream.first.timeout(new Duration(
+ seconds: 1))).then((var receivedError) {
+ expect(receivedError, same(error));
+ });
+ }
+
+ static Future listen_streamDone() {
+ return inputSink.close().then((_) => doneFuture.timeout(new Duration(
+ seconds: 1)));
+ }
+
+ static Future sendNotification() {
+ channel.sendNotification(new Notification('foo'));
+ return outputLineStream.first.timeout(new Duration(seconds: 1)).then((String
+ notification) {
+ var jsonNotification = new JsonCodec().decode(notification);
+ expect(jsonNotification, isMap);
+ expect(jsonNotification, contains('event'));
+ expect(jsonNotification['event'], equals('foo'));
+ });
+ }
+
+ static Future sendResponse() {
+ channel.sendResponse(new Response('foo'));
+ return outputLineStream.first.timeout(new Duration(seconds: 1)).then((String
+ response) {
+ var jsonResponse = new JsonCodec().decode(response);
+ expect(jsonResponse, isMap);
+ expect(jsonResponse, contains('id'));
+ expect(jsonResponse['id'], equals('foo'));
+ });
+ }
+}
diff --git a/pkg/analysis_server/test/declarative_tests.dart b/pkg/analysis_server/test/declarative_tests.dart
new file mode 100644
index 0000000..f6a6e68
--- /dev/null
+++ b/pkg/analysis_server/test/declarative_tests.dart
@@ -0,0 +1,51 @@
+library declarative_tests;
+
+import 'dart:mirrors';
+
+import 'package:unittest/unittest.dart' show group, test;
+
+/// Use [runTest] annotation to indicate that method is a test method.
+/// Alternatively method name can have the `test` prefix.
+const runTest = const _RunTest();
+
+class _RunTest {
+ const _RunTest();
+}
+
+/// Creates a new named group of tests with the name of the given [Type], then
+/// adds new tests using [addTestMethods].
+addTestSuite(Type type) {
+ group(type.toString(), () {
+ addTestMethods(type);
+ });
+}
+
+/// Creates a new test case for the each static method with the name starting
+/// with `test` or having the [runTest] annotation.
+addTestMethods(Type type) {
+ var typeMirror = reflectClass(type);
+ typeMirror.staticMembers.forEach((methodSymbol, method) {
+ if (_isTestMethod(method)) {
+ var methodName = MirrorSystem.getName(methodSymbol);
+ test(methodName, () {
+ typeMirror.invoke(methodSymbol, []);
+ });
+ }
+ });
+}
+
+bool _isTestMethod(MethodMirror method) {
+ if (method.parameters.isNotEmpty) {
+ return false;
+ }
+ var methodSymbol = method.simpleName;
+ // name starts with "test"
+ var methodName = MirrorSystem.getName(methodSymbol);
+ if (methodName.startsWith('test')) {
+ return true;
+ }
+ // has @testMethod
+ return method.metadata.any((annotation) {
+ return identical(annotation.reflectee, runTest);
+ });
+}
diff --git a/pkg/analysis_server/test/domain_context_test.dart b/pkg/analysis_server/test/domain_context_test.dart
index c66856e..358aea0 100644
--- a/pkg/analysis_server/test/domain_context_test.dart
+++ b/pkg/analysis_server/test/domain_context_test.dart
@@ -26,6 +26,8 @@
}
class ContextDomainHandlerTest {
+ static int contextIdCounter = 0;
+
static void applyChanges() {
AnalysisServer server = new AnalysisServer(new MockServerChannel());
String contextId = _createContext(server);
@@ -34,6 +36,7 @@
Request request = new Request('0', ContextDomainHandler.APPLY_CHANGES_NAME);
request.setParameter(ContextDomainHandler.CONTEXT_ID_PARAM, contextId);
+ request.setParameter(ContextDomainHandler.SOURCES_PARAM, []);
request.setParameter(ContextDomainHandler.CHANGES_PARAM, {
ContextDomainHandler.ADDED_PARAM : ['ffile:/one.dart'],
ContextDomainHandler.MODIFIED_PARAM : ['ffile:/two.dart'],
@@ -51,11 +54,13 @@
Request request = new Request('0', ContextDomainHandler.APPLY_CHANGES_NAME);
ContextDomainHandler handler = new ContextDomainHandler(server);
SourceFactory sourceFactory = new SourceFactory([new FileUriResolver()]);
- ChangeSet changeSet = handler.createChangeSet(request, sourceFactory, {
- ContextDomainHandler.ADDED_PARAM : ['ffile:/one.dart'],
- ContextDomainHandler.MODIFIED_PARAM : [],
- ContextDomainHandler.REMOVED_PARAM : ['ffile:/two.dart', 'ffile:/three.dart']
- });
+ ChangeSet changeSet = handler.createChangeSet(request, sourceFactory,
+ new RequestDatum(request, ContextDomainHandler.CHANGES_PARAM, {
+ ContextDomainHandler.ADDED_PARAM: ['ffile:/one.dart'],
+ ContextDomainHandler.MODIFIED_PARAM: [],
+ ContextDomainHandler.REMOVED_PARAM: ['ffile:/two.dart',
+ 'ffile:/three.dart']
+ }));
expect(changeSet.addedSources, hasLength(equals(1)));
expect(changeSet.changedSources, hasLength(equals(0)));
expect(changeSet.removedSources, hasLength(equals(2)));
@@ -111,14 +116,15 @@
}
static String _createContext(AnalysisServer server) {
+ String contextId = "context${contextIdCounter++}";
ServerDomainHandler handler = new ServerDomainHandler(server);
Request request = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ request.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
Response response = handler.handleRequest(request);
if (response.error != null) {
fail('Unexpected error: ${response.error.toJson()}');
}
- expect(response.error, isNull);
- return response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+ return contextId;
}
}
diff --git a/pkg/analysis_server/test/domain_server_test.dart b/pkg/analysis_server/test/domain_server_test.dart
index 84e290c..cb63f61 100644
--- a/pkg/analysis_server/test/domain_server_test.dart
+++ b/pkg/analysis_server/test/domain_server_test.dart
@@ -13,10 +13,10 @@
main() {
group('ServerDomainHandler', () {
-// test('createContext', ServerDomainHandlerTest.createContext);
-// test('deleteContext_alreadyDeleted', ServerDomainHandlerTest.deleteContext_alreadyDeleted);
+ test('createContext', ServerDomainHandlerTest.createContext);
+ test('deleteContext_alreadyDeleted', ServerDomainHandlerTest.deleteContext_alreadyDeleted);
test('deleteContext_doesNotExist', ServerDomainHandlerTest.deleteContext_doesNotExist);
-// test('deleteContext_existing', ServerDomainHandlerTest.deleteContext_existing);
+ test('deleteContext_existing', ServerDomainHandlerTest.deleteContext_existing);
test('shutdown', ServerDomainHandlerTest.shutdown);
test('version', ServerDomainHandlerTest.version);
});
@@ -29,28 +29,42 @@
Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
Response response = handler.handleRequest(createRequest);
- String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
- expect(contextId, isNotNull);
+ expect(response.id, equals('0'));
+ expect(response.error, isNull);
+ expect(response.result, isEmpty);
+ }
+
+ static void createContext_alreadyExists() {
+ AnalysisServer server = new AnalysisServer(new MockServerChannel());
+ ServerDomainHandler handler = new ServerDomainHandler(server);
+
+ Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
+ createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
+ Response response = handler.handleRequest(createRequest);
+ expect(response.error, isNull);
+ response = handler.handleRequest(createRequest);
+ expect(response.error, isNotNull);
}
static void deleteContext_alreadyDeleted() {
AnalysisServer server = new AnalysisServer(new MockServerChannel());
ServerDomainHandler handler = new ServerDomainHandler(server);
+ String contextId = 'ctx';
Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
- createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
- Response response = handler.handleRequest(createRequest);
- String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+ createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
+ handler.handleRequest(createRequest);
Request deleteRequest = new Request('0', ServerDomainHandler.DELETE_CONTEXT_METHOD);
deleteRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
- response = handler.handleRequest(deleteRequest);
- response = handler.handleRequest(deleteRequest);
- expect(response.toJson(), equals({
- Response.ID: '0',
- Response.ERROR: 'Context does not exist'
- }));
+ handler.handleRequest(deleteRequest);
+ Response response = handler.handleRequest(deleteRequest);
+ expect(response.id, equals('0'));
+ expect(response.error, isNotNull);
}
static void deleteContext_doesNotExist() {
@@ -68,14 +82,15 @@
AnalysisServer server = new AnalysisServer(new MockServerChannel());
ServerDomainHandler handler = new ServerDomainHandler(server);
+ String contextId = 'ctx';
Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
- createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
- Response response = handler.createContext(createRequest);
- String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+ createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+ createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
+ handler.createContext(createRequest);
Request deleteRequest = new Request('0', ServerDomainHandler.DELETE_CONTEXT_METHOD);
deleteRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
- response = handler.handleRequest(deleteRequest);
+ Response response = handler.handleRequest(deleteRequest);
expect(response.toJson(), equals({
Response.ID: '0',
Response.ERROR: null
diff --git a/pkg/analysis_server/test/mocks.dart b/pkg/analysis_server/test/mocks.dart
index f6e6452..f1c23a9 100644
--- a/pkg/analysis_server/test/mocks.dart
+++ b/pkg/analysis_server/test/mocks.dart
@@ -31,6 +31,20 @@
}
/**
+ * Returns a [Future] that completes after pumping the event queue [times]
+ * times. By default, this should pump the event queue enough times to allow
+ * any code to run, as long as it's not waiting on some external event.
+ */
+Future pumpEventQueue([int times = 20]) {
+ if (times == 0) return new Future.value();
+ // We use a delayed future to allow microtask events to finish. The
+ // Future.value or Future() constructors use scheduleMicrotask themselves and
+ // would therefore not wait for microtask callbacks that are scheduled after
+ // invoking this method.
+ return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
+}
+
+/**
* A mock [WebSocket] for testing.
*/
class MockSocket<T> implements WebSocket {
@@ -69,6 +83,17 @@
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
+class NoResponseException implements Exception {
+ /// The request that was not responded to.
+ final Request request;
+
+ NoResponseException(this.request);
+
+ String toString() {
+ return "NoResponseException after request ${request.toJson()}";
+ }
+}
+
/**
* A mock [ServerCommunicationChannel] for testing [AnalysisServer].
*/
@@ -100,7 +125,10 @@
var id = request.id;
// Wrap send request in future to simulate websocket
new Future(() => requestController.add(request));
- return responseController.stream.firstWhere((response) => response.id == id);
+ pumpEventQueue().then((_) => responseController.addError(
+ new NoResponseException(request)));
+ return responseController.stream.firstWhere((response) => response.id == id
+ );
}
@override
diff --git a/pkg/analysis_server/test/protocol_test.dart b/pkg/analysis_server/test/protocol_test.dart
index 6597f11..0869923 100644
--- a/pkg/analysis_server/test/protocol_test.dart
+++ b/pkg/analysis_server/test/protocol_test.dart
@@ -9,52 +9,18 @@
import 'package:analysis_server/src/protocol.dart';
import 'package:unittest/unittest.dart';
+import 'declarative_tests.dart';
+
main() {
- group('Notification', () {
- test('getParameter_defined', NotificationTest.getParameter_defined);
- test('getParameter_undefined', NotificationTest.getParameter_undefined);
- test('fromJson', NotificationTest.fromJson);
- test('fromJson_withParams', NotificationTest.fromJson_withParams);
- });
- group('Request', () {
- test('getParameter_defined', RequestTest.getParameter_defined);
- test('getParameter_undefined', RequestTest.getParameter_undefined);
- test('getRequiredParameter_defined', RequestTest.getRequiredParameter_defined);
- test('getRequiredParameter_undefined', RequestTest.getRequiredParameter_undefined);
- test('fromJson', RequestTest.fromJson);
- test('fromJson_invalidId', RequestTest.fromJson_invalidId);
- test('fromJson_invalidMethod', RequestTest.fromJson_invalidMethod);
- test('fromJson_invalidParams', RequestTest.fromJson_invalidParams);
- test('fromJson_withParams', RequestTest.fromJson_withParams);
- test('toBool', RequestTest.toBool);
- test('toInt', RequestTest.toInt);
- test('toJson', RequestTest.toJson);
- test('toJson_withParams', RequestTest.toJson_withParams);
- });
- group('RequestError', () {
- test('create', RequestErrorTest.create);
- test('create_methodNotFound', RequestErrorTest.create_methodNotFound);
- test('create_invalidParameters', RequestErrorTest.create_invalidParameters);
- test('create_invalidRequest', RequestErrorTest.create_invalidRequest);
- test('create_internalError', RequestErrorTest.create_internalError);
- test('create_parseError', RequestErrorTest.create_parseError);
- test('fromJson', RequestErrorTest.fromJson);
- test('toJson', RequestErrorTest.toJson);
- });
- group('Response', () {
- test('create_contextDoesNotExist', ResponseTest.create_contextDoesNotExist);
- test('create_invalidRequestFormat', ResponseTest.create_invalidRequestFormat);
- test('create_missingRequiredParameter', ResponseTest.create_missingRequiredParameter);
- test('create_unknownAnalysisOption', ResponseTest.create_unknownAnalysisOption);
- test('create_unknownRequest', ResponseTest.create_unknownRequest);
- test('setResult', ResponseTest.setResult);
- test('fromJson', ResponseTest.fromJson);
- test('fromJson_withError', ResponseTest.fromJson_withError);
- test('fromJson_withResult', ResponseTest.fromJson_withResult);
- });
+ addTestSuite(NotificationTest);
+ addTestSuite(RequestTest);
+ addTestSuite(RequestErrorTest);
+ addTestSuite(RequestDatumTest);
+ addTestSuite(ResponseTest);
}
class NotificationTest {
+ @runTest
static void getParameter_defined() {
Notification notification = new Notification('foo');
notification.setParameter('x', 'y');
@@ -67,6 +33,7 @@
}));
}
+ @runTest
static void getParameter_undefined() {
Notification notification = new Notification('foo');
expect(notification.event, equals('foo'));
@@ -77,6 +44,7 @@
}));
}
+ @runTest
static void fromJson() {
Notification original = new Notification('foo');
Notification notification = new Notification.fromJson(original.toJson());
@@ -85,6 +53,7 @@
expect(notification.getParameter('x'), isNull);
}
+ @runTest
static void fromJson_withParams() {
Notification original = new Notification('foo');
original.setParameter('x', 'y');
@@ -96,34 +65,40 @@
}
class RequestTest {
+ @runTest
static void getParameter_defined() {
String name = 'name';
String value = 'value';
Request request = new Request('0', '');
request.setParameter(name, value);
- expect(request.getParameter(name), equals(value));
+ expect(request.getParameter(name, null).datum, equals(value));
}
+ @runTest
static void getParameter_undefined() {
String name = 'name';
+ String defaultValue = 'default value';
Request request = new Request('0', '');
- expect(request.getParameter(name), isNull);
+ expect(request.getParameter(name, defaultValue).datum, equals(defaultValue));
}
+ @runTest
static void getRequiredParameter_defined() {
String name = 'name';
String value = 'value';
Request request = new Request('0', '');
request.setParameter(name, value);
- expect(request.getRequiredParameter(name), equals(value));
+ expect(request.getRequiredParameter(name).datum, equals(value));
}
+ @runTest
static void getRequiredParameter_undefined() {
String name = 'name';
Request request = new Request('0', '');
expect(() => request.getRequiredParameter(name), _throwsRequestFailure);
}
+ @runTest
static void fromJson() {
Request original = new Request('one', 'aMethod');
String json = JSON.encode(original.toJson());
@@ -132,24 +107,28 @@
expect(request.method, equals('aMethod'));
}
+ @runTest
static void fromJson_invalidId() {
String json = '{"id":{"one":"two"},"method":"aMethod","params":{"foo":"bar"}}';
Request request = new Request.fromString(json);
expect(request, isNull);
}
+ @runTest
static void fromJson_invalidMethod() {
String json = '{"id":"one","method":{"boo":"aMethod"},"params":{"foo":"bar"}}';
Request request = new Request.fromString(json);
expect(request, isNull);
}
+ @runTest
static void fromJson_invalidParams() {
String json = '{"id":"one","method":"aMethod","params":"foobar"}';
Request request = new Request.fromString(json);
expect(request, isNull);
}
+ @runTest
static void fromJson_withParams() {
Request original = new Request('one', 'aMethod');
original.setParameter('foo', 'bar');
@@ -157,27 +136,10 @@
Request request = new Request.fromString(json);
expect(request.id, equals('one'));
expect(request.method, equals('aMethod'));
- expect(request.getParameter('foo'), equals('bar'));
+ expect(request.getParameter('foo', null).asString(), equals('bar'));
}
- static void toBool() {
- Request request = new Request('0', '');
- expect(request.toBool(true), isTrue);
- expect(request.toBool(false), isFalse);
- expect(request.toBool('true'), isTrue);
- expect(request.toBool('false'), isFalse);
- expect(request.toBool('abc'), isFalse);
- expect(() => request.toBool(42), _throwsRequestFailure);
- }
-
- static void toInt() {
- Request request = new Request('0', '');
- expect(request.toInt(1), equals(1));
- expect(request.toInt('2'), equals(2));
- expect(() => request.toInt('xxx'), _throwsRequestFailure);
- expect(() => request.toInt(request), _throwsRequestFailure);
- }
-
+ @runTest
static void toJson() {
Request request = new Request('one', 'aMethod');
expect(request.toJson(), equals({
@@ -186,6 +148,7 @@
}));
}
+ @runTest
static void toJson_withParams() {
Request request = new Request('one', 'aMethod');
request.setParameter('foo', 'bar');
@@ -198,6 +161,7 @@
}
class RequestErrorTest {
+ @runTest
static void create() {
RequestError error = new RequestError(42, 'msg');
expect(error.code, 42);
@@ -208,36 +172,49 @@
}));
}
+ @runTest
static void create_parseError() {
RequestError error = new RequestError.parseError();
expect(error.code, RequestError.CODE_PARSE_ERROR);
expect(error.message, "Parse error");
}
+ @runTest
static void create_methodNotFound() {
RequestError error = new RequestError.methodNotFound();
expect(error.code, RequestError.CODE_METHOD_NOT_FOUND);
expect(error.message, "Method not found");
}
+ @runTest
static void create_invalidParameters() {
RequestError error = new RequestError.invalidParameters();
expect(error.code, RequestError.CODE_INVALID_PARAMS);
expect(error.message, "Invalid parameters");
}
+ @runTest
static void create_invalidRequest() {
RequestError error = new RequestError.invalidRequest();
expect(error.code, RequestError.CODE_INVALID_REQUEST);
expect(error.message, "Invalid request");
}
+ @runTest
static void create_internalError() {
RequestError error = new RequestError.internalError();
expect(error.code, RequestError.CODE_INTERNAL_ERROR);
expect(error.message, "Internal error");
}
+ @runTest
+ static void create_serverAlreadyStarted() {
+ RequestError error = new RequestError.serverAlreadyStarted();
+ expect(error.code, RequestError.CODE_SERVER_ALREADY_STARTED);
+ expect(error.message, "Server already started");
+ }
+
+ @runTest
static void fromJson() {
var json = {
RequestError.CODE: RequestError.CODE_PARSE_ERROR,
@@ -251,6 +228,7 @@
expect(error.getData('ints'), [1, 2, 3]);
}
+ @runTest
static void toJson() {
RequestError error = new RequestError(0, 'msg');
error.setData('answer', 42);
@@ -263,7 +241,159 @@
}
}
+class InvalidParameterResponseMatcher extends Matcher {
+ static const int ERROR_CODE = -2;
+
+ @override
+ Description describe(Description description) =>
+ description.add("an 'invalid parameter' response (code $ERROR_CODE)");
+
+ @override
+ bool matches(item, Map matchState) {
+ if (item is! Response) {
+ return false;
+ }
+ Response response = item;
+ if (response.error is! RequestError) {
+ return false;
+ }
+ RequestError requestError = response.error;
+ if (requestError.code != ERROR_CODE) {
+ return false;
+ }
+ return true;
+ }
+}
+
+class RequestDatumTest {
+ static Request request;
+
+ static Matcher _throwsInvalidParameter = throwsA(
+ new InvalidParameterResponseMatcher());
+ static Matcher isRequestDatum = new isInstanceOf<RequestDatum>("RequestDatum"
+ );
+
+ static void setUp() {
+ request = new Request('myId', 'myMethod');
+ }
+
+ static RequestDatum makeDatum(dynamic datum) {
+ return new RequestDatum(request, 'myPath', datum);
+ }
+
+ static void indexOperator_nonMap() {
+ expect(() => makeDatum(1)['foo'], _throwsInvalidParameter);
+ }
+
+ static void indexOperator_missingKey() {
+ expect(() => makeDatum({
+ 'foo': 'bar'
+ })['baz'], _throwsInvalidParameter);
+ }
+
+ static void indexOperator_hasKey() {
+ var indexResult = makeDatum({
+ 'foo': 'bar'
+ })['foo'];
+ expect(indexResult, isRequestDatum);
+ expect(indexResult.datum, equals('bar'));
+ expect(indexResult.path, equals('myPath.bar'));
+ }
+
+ static void forEachMap_nonMap() {
+ expect(() => makeDatum(1).forEachMap((key, value) {
+ fail('Non-map should not be iterated');
+ }), _throwsInvalidParameter);
+ }
+
+ static void forEachMap_emptyMap() {
+ makeDatum({}).forEachMap((key, value) {
+ fail('Empty map should not be iterated');
+ });
+ }
+
+ static void forEachMap_oneElementMap() {
+ int callCount = 0;
+ makeDatum({
+ 'key': 'value'
+ }).forEachMap((key, value) {
+ callCount++;
+ expect(key, equals('key'));
+ expect(value, isRequestDatum);
+ expect(value.datum, equals('value'));
+ });
+ expect(callCount, equals(1));
+ }
+
+ static void forEachMap_twoElementMap() {
+ int callCount = 0;
+ Map<String, String> map = {
+ 'key1': 'value1',
+ 'key2': 'value2'
+ };
+ Map iterationResult = {};
+ makeDatum(map).forEachMap((key, value) {
+ callCount++;
+ iterationResult[key] = value;
+ });
+ expect(callCount, equals(2));
+ expect(iterationResult, equals(map));
+ }
+
+ static void asBool() {
+ expect(makeDatum(true).asBool(), isTrue);
+ expect(makeDatum(false).asBool(), isFalse);
+ expect(makeDatum('true').asBool(), isTrue);
+ expect(makeDatum('false').asBool(), isFalse);
+ expect(() => makeDatum('abc').asBool(), _throwsInvalidParameter);
+ }
+
+ static void asInt() {
+ expect(makeDatum(1).asInt(), equals(1));
+ expect(makeDatum('2').asInt(), equals(2));
+ expect(() => makeDatum('xxx').asInt(), _throwsInvalidParameter);
+ expect(() => makeDatum(true).asInt(), _throwsInvalidParameter);
+ }
+
+ static void asString() {
+ expect(makeDatum('foo').asString(), equals('foo'));
+ expect(() => makeDatum(3).asString(), _throwsInvalidParameter);
+ }
+
+ static void asStringList() {
+ expect(makeDatum(['foo', 'bar']).asStringList(), equals(['foo', 'bar']));
+ expect(makeDatum([]).asStringList(), equals([]));
+ expect(() => makeDatum(['foo', 1]).asStringList(), _throwsInvalidParameter);
+ expect(() => makeDatum({}).asStringList(), _throwsInvalidParameter);
+ }
+
+ static void asStringMap() {
+ expect(makeDatum({
+ 'key1': 'value1',
+ 'key2': 'value2'
+ }).asStringMap(), equals({
+ 'key1': 'value1',
+ 'key2': 'value2'
+ }));
+ expect(makeDatum({}).asStringMap(), equals({}));
+ expect(() => makeDatum({
+ 'key1': 'value1',
+ 'key2': 2
+ }).asStringMap(), _throwsInvalidParameter);
+ expect(() => makeDatum({
+ 'key1': 1,
+ 'key2': 2
+ }).asStringMap(), _throwsInvalidParameter);
+ expect(() => makeDatum({
+ 1: 'value1',
+ 2: 'value2'
+ }).asStringMap(), _throwsInvalidParameter);
+ expect(() => makeDatum([]).asStringMap(), _throwsInvalidParameter);
+ }
+}
+
class ResponseTest {
+ @runTest
static void create_contextDoesNotExist() {
Response response = new Response.contextDoesNotExist(new Request('0', ''));
expect(response.id, equals('0'));
@@ -274,6 +404,7 @@
}));
}
+ @runTest
static void create_invalidRequestFormat() {
Response response = new Response.invalidRequestFormat();
expect(response.id, equals(''));
@@ -284,6 +415,7 @@
}));
}
+ @runTest
static void create_missingRequiredParameter() {
Response response = new Response.missingRequiredParameter(new Request('0', ''), 'x');
expect(response.id, equals('0'));
@@ -294,6 +426,7 @@
}));
}
+ @runTest
static void create_unknownAnalysisOption() {
Response response = new Response.unknownAnalysisOption(new Request('0', ''), 'x');
expect(response.id, equals('0'));
@@ -304,6 +437,7 @@
}));
}
+ @runTest
static void create_unknownRequest() {
Response response = new Response.unknownRequest(new Request('0', ''));
expect(response.id, equals('0'));
@@ -314,6 +448,7 @@
}));
}
+ @runTest
static void setResult() {
String resultName = 'name';
String resultValue = 'value';
@@ -329,12 +464,14 @@
}));
}
+ @runTest
static void fromJson() {
Response original = new Response('myId');
Response response = new Response.fromJson(original.toJson());
expect(response.id, equals('myId'));
}
+ @runTest
static void fromJson_withError() {
Response original = new Response.invalidRequestFormat();
Response response = new Response.fromJson(original.toJson());
@@ -345,6 +482,7 @@
expect(error.message, equals('Invalid request'));
}
+ @runTest
static void fromJson_withResult() {
Response original = new Response('myId');
original.setResult('foo', 'bar');
diff --git a/pkg/analysis_server/test/socket_server_test.dart b/pkg/analysis_server/test/socket_server_test.dart
new file mode 100644
index 0000000..fbc1512
--- /dev/null
+++ b/pkg/analysis_server/test/socket_server_test.dart
@@ -0,0 +1,67 @@
+// 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.
+
+library test.socket.server;
+
+import 'dart:async';
+
+import 'mocks.dart';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_server.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/socket_server.dart';
+import 'package:unittest/unittest.dart';
+
+main() {
+ group('SocketServer', () {
+ test('createAnalysisServer_successful',
+ SocketServerTest.createAnalysisServer_successful);
+ test('createAnalysisServer_alreadyStarted',
+ SocketServerTest.createAnalysisServer_alreadyStarted);
+ });
+}
+
+class SocketServerTest {
+ static Future createAnalysisServer_successful() {
+ SocketServer server = new SocketServer();
+ MockServerChannel channel = new MockServerChannel();
+ server.createAnalysisServer(channel);
+ channel.expectMsgCount(notificationCount: 1);
+ expect(channel.notificationsReceived[0].event, equals(
+ AnalysisServer.CONNECTED_NOTIFICATION));
+ expect(channel.notificationsReceived[0].params, isEmpty);
+ return channel.sendRequest(new Request('0',
+ ServerDomainHandler.SHUTDOWN_METHOD)).then((Response response) {
+ expect(response.id, equals('0'));
+ expect(response.error, isNull);
+ channel.expectMsgCount(responseCount: 1, notificationCount: 1);
+ });
+ }
+
+ static void createAnalysisServer_alreadyStarted() {
+ SocketServer server = new SocketServer();
+ MockServerChannel channel1 = new MockServerChannel();
+ MockServerChannel channel2 = new MockServerChannel();
+ server.createAnalysisServer(channel1);
+ expect(channel1.notificationsReceived[0].event, equals(
+ AnalysisServer.CONNECTED_NOTIFICATION));
+ expect(channel1.notificationsReceived[0].params, isEmpty);
+ server.createAnalysisServer(channel2);
+ channel1.expectMsgCount(notificationCount: 1);
+ channel2.expectMsgCount(responseCount: 1);
+ expect(channel2.responsesReceived[0].id, equals(''));
+ expect(channel2.responsesReceived[0].error, isNotNull);
+ expect(channel2.responsesReceived[0].error.code, equals(
+ RequestError.CODE_SERVER_ALREADY_STARTED));
+ channel2.sendRequest(new Request('0', ServerDomainHandler.SHUTDOWN_METHOD)
+ ).then((Response response) {
+ expect(response.id, equals('0'));
+ expect(response.error, isNotNull);
+ expect(response.error.code, equals(
+ RequestError.CODE_SERVER_ALREADY_STARTED));
+ channel2.expectMsgCount(responseCount: 2);
+ });
+ }
+}
diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart
index e9a8ad7..e230a51 100644
--- a/pkg/analysis_server/test/test_all.dart
+++ b/pkg/analysis_server/test/test_all.dart
@@ -9,6 +9,7 @@
import 'domain_context_test.dart' as domain_context_test;
import 'domain_server_test.dart' as domain_server_test;
import 'protocol_test.dart' as protocol_test;
+import 'socket_server_test.dart' as socket_server_test;
/// Utility for manually running all tests
main() {
@@ -18,5 +19,6 @@
domain_context_test.main();
domain_server_test.main();
protocol_test.main();
+ socket_server_test.main();
});
}
\ No newline at end of file
diff --git a/pkg/analyzer/lib/src/generated/ast.dart b/pkg/analyzer/lib/src/generated/ast.dart
index 8a91113..0b9fc8f 100644
--- a/pkg/analyzer/lib/src/generated/ast.dart
+++ b/pkg/analyzer/lib/src/generated/ast.dart
@@ -12724,11 +12724,7 @@
}
}
}
- Element element = node.bestElement;
- if (element == null) {
- element = node.staticElement;
- }
- return element;
+ return node.bestElement;
}
@override
@@ -15922,7 +15918,7 @@
SwitchStatement visitSwitchStatement(SwitchStatement node) => new SwitchStatement(node.keyword, node.leftParenthesis, _cloneNode(node.expression), node.rightParenthesis, node.leftBracket, _cloneNodeList(node.members), node.rightBracket);
@override
- AstNode visitSymbolLiteral(SymbolLiteral node) => new SymbolLiteral(node.poundSign, node.components);
+ SymbolLiteral visitSymbolLiteral(SymbolLiteral node) => new SymbolLiteral(node.poundSign, node.components);
@override
ThisExpression visitThisExpression(ThisExpression node) => new ThisExpression(node.keyword);
@@ -15992,7 +15988,7 @@
* @param second the second node being compared
* @return `true` if the two AST nodes are equal
*/
- static bool equalUnits(CompilationUnit first, CompilationUnit second) {
+ static bool equalNodes(AstNode first, AstNode second) {
AstComparator comparator = new AstComparator();
return comparator._isEqualNodes(first, second);
}
@@ -16682,7 +16678,7 @@
return false;
}
for (int i = 0; i < length; i++) {
- if (_isEqualTokens(first[i], second[i])) {
+ if (!_isEqualTokens(first[i], second[i])) {
return false;
}
}
@@ -16701,6 +16697,8 @@
return second == null;
} else if (second == null) {
return false;
+ } else if (identical(first, second)) {
+ return true;
}
return first.offset == second.offset && first.length == second.length && first.lexeme == second.lexeme;
}
diff --git a/pkg/analyzer/lib/src/generated/constant.dart b/pkg/analyzer/lib/src/generated/constant.dart
index 91815ec..967a31c 100644
--- a/pkg/analyzer/lib/src/generated/constant.dart
+++ b/pkg/analyzer/lib/src/generated/constant.dart
@@ -312,11 +312,11 @@
*/
void computeValues() {
_declarationMap = _constantFinder.variableMap;
- for (MapIterator<VariableElement, VariableDeclaration> iter = SingleMapIterator.forMap(_declarationMap); iter.moveNext();) {
- VariableElement element = iter.key;
+ for (MapEntry<VariableElement, VariableDeclaration> entry in getMapEntrySet(_declarationMap)) {
+ VariableElement element = entry.getKey();
ReferenceFinder referenceFinder = new ReferenceFinder(element, _referenceGraph);
_referenceGraph.addNode(element);
- iter.value.initializer.accept(referenceFinder);
+ entry.getValue().initializer.accept(referenceFinder);
}
while (!_referenceGraph.isEmpty) {
VariableElement element = _referenceGraph.removeSink();
@@ -2929,7 +2929,7 @@
return false;
}
GenericState state = object as GenericState;
- Set<String> otherFields = new Set<String>();
+ Set<String> otherFields = new Set<String>.from(state._fieldMap.keys.toSet());
for (String fieldName in _fieldMap.keys.toSet()) {
if (_fieldMap[fieldName] != state._fieldMap[fieldName]) {
return false;
@@ -3938,9 +3938,9 @@
} else if (count == 0) {
return true;
}
- for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
- DartObjectImpl key = iter.key;
- DartObjectImpl value = iter.value;
+ for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+ DartObjectImpl key = entry.getKey();
+ DartObjectImpl value = entry.getValue();
DartObjectImpl otherValue = otherElements[key];
if (value != otherValue) {
return false;
@@ -3955,9 +3955,9 @@
@override
Map<Object, Object> get value {
Map<Object, Object> result = new Map<Object, Object>();
- for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
- DartObjectImpl key = iter.key;
- DartObjectImpl value = iter.value;
+ for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+ DartObjectImpl key = entry.getKey();
+ DartObjectImpl value = entry.getValue();
if (!key.hasExactValue || !value.hasExactValue) {
return null;
}
@@ -3968,8 +3968,8 @@
@override
bool get hasExactValue {
- for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
- if (!iter.key.hasExactValue || !iter.value.hasExactValue) {
+ for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+ if (!entry.getKey().hasExactValue || !entry.getValue().hasExactValue) {
return false;
}
}
diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart
index a10931f..68eb93f 100644
--- a/pkg/analyzer/lib/src/generated/element.dart
+++ b/pkg/analyzer/lib/src/generated/element.dart
@@ -3867,7 +3867,7 @@
/**
* The name of the top-level variable used to mark a class as implementing a proxy object.
*/
- static String _PROXY_VARIABLE_NAME = "proxy";
+ static String PROXY_VARIABLE_NAME = "proxy";
/**
* Initialize a newly created annotation.
@@ -3913,7 +3913,7 @@
if (element != null) {
LibraryElement library = element.library;
if (library != null && library.isDartCore) {
- if (element is PropertyAccessorElement && element.name == _PROXY_VARIABLE_NAME) {
+ if (element is PropertyAccessorElement && element.name == PROXY_VARIABLE_NAME) {
return true;
}
}
@@ -5901,6 +5901,15 @@
accept(ElementVisitor visitor) => visitor.visitMethodElement(this);
@override
+ String get displayName {
+ String displayName = super.displayName;
+ if ("unary-" == displayName) {
+ return "-";
+ }
+ return displayName;
+ }
+
+ @override
ClassElement get enclosingElement => super.enclosingElement as ClassElement;
@override
@@ -8495,10 +8504,12 @@
if (secondTypes.length != firstTypes.length) {
return false;
}
- MapIterator<String, DartType> firstIterator = SingleMapIterator.forMap(firstTypes);
- MapIterator<String, DartType> secondIterator = SingleMapIterator.forMap(secondTypes);
- while (firstIterator.moveNext() && secondIterator.moveNext()) {
- if (firstIterator.key != secondIterator.key || !(firstIterator.value as TypeImpl).internalEquals(secondIterator.value, visitedElementPairs)) {
+ JavaIterator<MapEntry<String, DartType>> firstIterator = new JavaIterator(getMapEntrySet(firstTypes));
+ JavaIterator<MapEntry<String, DartType>> secondIterator = new JavaIterator(getMapEntrySet(secondTypes));
+ while (firstIterator.hasNext) {
+ MapEntry<String, DartType> firstEntry = firstIterator.next();
+ MapEntry<String, DartType> secondEntry = secondIterator.next();
+ if (firstEntry.getKey() != secondEntry.getKey() || !(firstEntry.getValue() as TypeImpl).internalEquals(secondEntry.getValue(), visitedElementPairs)) {
return false;
}
}
@@ -8574,15 +8585,15 @@
needsComma = false;
}
builder.append("{");
- for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(namedParameterTypes); iter.moveNext();) {
+ for (MapEntry<String, DartType> entry in getMapEntrySet(namedParameterTypes)) {
if (needsComma) {
builder.append(", ");
} else {
needsComma = true;
}
- builder.append(iter.key);
+ builder.append(entry.getKey());
builder.append(": ");
- builder.append(iter.value.displayName);
+ builder.append(entry.getValue().displayName);
}
builder.append("}");
needsComma = true;
@@ -8753,13 +8764,14 @@
}
// Loop through each element in S verifying that T has a matching parameter name and that the
// corresponding type is more specific then the type in S.
- MapIterator<String, DartType> iteratorS = SingleMapIterator.forMap(namedTypesS);
- while (iteratorS.moveNext()) {
- DartType typeT = namedTypesT[iteratorS.key];
+ JavaIterator<MapEntry<String, DartType>> iteratorS = new JavaIterator(getMapEntrySet(namedTypesS));
+ while (iteratorS.hasNext) {
+ MapEntry<String, DartType> entryS = iteratorS.next();
+ DartType typeT = namedTypesT[entryS.getKey()];
if (typeT == null) {
return false;
}
- if (!(typeT as TypeImpl).isMoreSpecificThan2(iteratorS.value, withDynamic, visitedTypePairs)) {
+ if (!(typeT as TypeImpl).isMoreSpecificThan2(entryS.getValue(), withDynamic, visitedTypePairs)) {
return false;
}
}
@@ -8872,15 +8884,15 @@
needsComma = false;
}
builder.append("{");
- for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(namedParameterTypes); iter.moveNext();) {
+ for (MapEntry<String, DartType> entry in getMapEntrySet(namedParameterTypes)) {
if (needsComma) {
builder.append(", ");
} else {
needsComma = true;
}
- builder.append(iter.key);
+ builder.append(entry.getKey());
builder.append(": ");
- (iter.value as TypeImpl).appendTo(builder);
+ (entry.getValue() as TypeImpl).appendTo(builder);
}
builder.append("}");
needsComma = true;
@@ -8968,13 +8980,14 @@
}
// Loop through each element in S verifying that T has a matching parameter name and that the
// corresponding type is assignable to the type in S.
- MapIterator<String, DartType> iteratorS = SingleMapIterator.forMap(namedTypesS);
- while (iteratorS.moveNext()) {
- DartType typeT = namedTypesT[iteratorS.key];
+ JavaIterator<MapEntry<String, DartType>> iteratorS = new JavaIterator(getMapEntrySet(namedTypesS));
+ while (iteratorS.hasNext) {
+ MapEntry<String, DartType> entryS = iteratorS.next();
+ DartType typeT = namedTypesT[entryS.getKey()];
if (typeT == null) {
return false;
}
- if (!(typeT as TypeImpl).isAssignableTo2(iteratorS.value, visitedTypePairs)) {
+ if (!(typeT as TypeImpl).isAssignableTo2(entryS.getValue(), visitedTypePairs)) {
return false;
}
}
@@ -9130,15 +9143,14 @@
*/
static Set<InterfaceType> _computeSuperinterfaceSet(InterfaceType type, Set<InterfaceType> set) {
Element element = type.element;
- if (element != null && element is ClassElement) {
- ClassElement classElement = element;
- List<InterfaceType> superinterfaces = classElement.interfaces;
+ if (element != null) {
+ List<InterfaceType> superinterfaces = type.interfaces;
for (InterfaceType superinterface in superinterfaces) {
if (set.add(superinterface)) {
_computeSuperinterfaceSet(superinterface, set);
}
}
- InterfaceType supertype = classElement.supertype;
+ InterfaceType supertype = type.superclass;
if (supertype != null) {
if (set.add(supertype)) {
_computeSuperinterfaceSet(supertype, set);
@@ -9150,27 +9162,15 @@
/**
* Return the intersection of the given sets of types, where intersection is based on the equality
- * of the elements of the types rather than on the equality of the types themselves. In cases
- * where two non-equal types have equal elements, which only happens when the class is
- * parameterized, the type that is added to the intersection is the base type with type arguments
- * that are the least upper bound of the type arguments of the two types.
+ * of the types themselves.
*
* @param first the first set of types to be intersected
* @param second the second set of types to be intersected
* @return the intersection of the given sets of types
*/
static List<InterfaceType> _intersection(Set<InterfaceType> first, Set<InterfaceType> second) {
- Map<ClassElement, InterfaceType> firstMap = new Map<ClassElement, InterfaceType>();
- for (InterfaceType firstType in first) {
- firstMap[firstType.element] = firstType;
- }
- Set<InterfaceType> result = new Set<InterfaceType>();
- for (InterfaceType secondType in second) {
- InterfaceType firstType = firstMap[secondType.element];
- if (firstType != null) {
- result.add(_leastUpperBound(firstType, secondType));
- }
- }
+ Set<InterfaceType> result = new Set<InterfaceType>.from(first);
+ result.retainAll(second);
return new List.from(result);
}
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 18d00f7..3f91c74 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -87,6 +87,11 @@
Logger _logger = Logger.NULL;
/**
+ * The partition manager being used to manage the shared partitions.
+ */
+ final PartitionManager partitionManager = new PartitionManager();
+
+ /**
* Create a new context in which analysis can be performed.
*
* @return the analysis context that was created
@@ -96,9 +101,9 @@
// If instrumentation is ignoring data, return an uninstrumented analysis context.
//
if (Instrumentation.isNullLogger) {
- return new DelegatingAnalysisContextImpl();
+ return new AnalysisContextImpl();
}
- return new InstrumentedAnalysisContextImpl.con1(new DelegatingAnalysisContextImpl());
+ return new InstrumentedAnalysisContextImpl.con1(new AnalysisContextImpl());
}
/**
@@ -959,24 +964,42 @@
}
/**
- * Instances of the class `ChangeSet` indicate what sources have been added, changed, or
- * removed.
+ * Instances of the class `ChangeSet` indicate which sources have been added, changed,
+ * removed, or deleted. In the case of a changed source, there are multiple ways of indicating the
+ * nature of the change.
+ *
+ * No source should be added to the change set more than once, either with the same or a different
+ * kind of change. It does not make sense, for example, for a source to be both added and removed,
+ * and it is redundant for a source to be marked as changed in its entirety and changed in some
+ * specific range.
*/
class ChangeSet {
/**
* A list containing the sources that have been added.
*/
- List<Source> _added = new List<Source>();
+ final List<Source> addedSources = new List<Source>();
/**
* A list containing the sources that have been changed.
*/
- List<Source> _changed = new List<Source>();
+ final List<Source> changedSources = new List<Source>();
+
+ /**
+ * A table mapping the sources whose content has been changed to the current content of those
+ * sources.
+ */
+ Map<Source, String> _changedContent = new Map<Source, String>();
+
+ /**
+ * A table mapping the sources whose content has been changed within a single range to the current
+ * content of those sources and information about the affected range.
+ */
+ final Map<Source, ChangeSet_ContentChange> changedRanges = new Map<Source, ChangeSet_ContentChange>();
/**
* A list containing the sources that have been removed.
*/
- List<Source> _removed = new List<Source>();
+ final List<Source> removedSources = new List<Source>();
/**
* A list containing the source containers specifying additional sources that have been removed.
@@ -984,52 +1007,78 @@
final List<SourceContainer> removedContainers = new List<SourceContainer>();
/**
- * Record that the specified source has been added and that it's content is the default contents
- * of the source.
+ * A list containing the sources that have been deleted.
+ */
+ final List<Source> deletedSources = new List<Source>();
+
+ /**
+ * Record that the specified source has been added and that its content is the default contents of
+ * the source.
*
* @param source the source that was added
*/
void addedSource(Source source) {
- _added.add(source);
+ addedSources.add(source);
}
/**
- * Record that the specified source has been changed and that it's content is the default contents
- * of the source.
+ * Record that the specified source has been changed and that its content is the given contents.
+ *
+ * @param source the source that was changed
+ * @param contents the new contents of the source, or `null` if the default contents of the
+ * source are to be used
+ */
+ void changedContent(Source source, String contents) {
+ _changedContent[source] = contents;
+ }
+
+ /**
+ * Record that the specified source has been changed and that its content is the given contents.
+ *
+ * @param source the source that was changed
+ * @param contents the new contents of the source
+ * @param offset the offset into the current contents
+ * @param oldLength the number of characters in the original contents that were replaced
+ * @param newLength the number of characters in the replacement text
+ */
+ void changedRange(Source source, String contents, int offset, int oldLength, int newLength) {
+ changedRanges[source] = new ChangeSet_ContentChange(contents, offset, oldLength, newLength);
+ }
+
+ /**
+ * Record that the specified source has been changed. If the content of the source was previously
+ * overridden, use [changedContent] instead.
*
* @param source the source that was changed
*/
void changedSource(Source source) {
- _changed.add(source);
+ changedSources.add(source);
}
/**
- * Return a collection of the sources that have been added.
+ * Record that the specified source has been deleted.
*
- * @return a collection of the sources that have been added
+ * @param source the source that was deleted
*/
- List<Source> get addedSources => _added;
+ void deletedSource(Source source) {
+ deletedSources.add(source);
+ }
/**
- * Return a collection of sources that have been changed.
+ * Return a table mapping the sources whose content has been changed to the current content of
+ * those sources.
*
- * @return a collection of sources that have been changed
+ * @return a table mapping the sources whose content has been changed to the current content of
+ * those sources
*/
- List<Source> get changedSources => _changed;
-
- /**
- * Return a list containing the sources that were removed.
- *
- * @return a list containing the sources that were removed
- */
- List<Source> get removedSources => _removed;
+ Map<Source, String> get changedContents => _changedContent;
/**
* Return `true` if this change set does not contain any changes.
*
* @return `true` if this change set does not contain any changes
*/
- bool get isEmpty => _added.isEmpty && _changed.isEmpty && _removed.isEmpty && removedContainers.isEmpty;
+ bool get isEmpty => addedSources.isEmpty && changedSources.isEmpty && _changedContent.isEmpty && changedRanges.isEmpty && removedSources.isEmpty && removedContainers.isEmpty && deletedSources.isEmpty;
/**
* Record that the specified source container has been removed.
@@ -1049,19 +1098,22 @@
*/
void removedSource(Source source) {
if (source != null) {
- _removed.add(source);
+ removedSources.add(source);
}
}
@override
String toString() {
JavaStringBuilder builder = new JavaStringBuilder();
- bool needsSeparator = _appendSources(builder, _added, false, "added");
- needsSeparator = _appendSources(builder, _changed, needsSeparator, "changed");
- _appendSources(builder, _removed, needsSeparator, "removed");
+ bool needsSeparator = _appendSources(builder, addedSources, false, "addedSources");
+ needsSeparator = _appendSources(builder, changedSources, needsSeparator, "changedSources");
+ needsSeparator = _appendSources2(builder, _changedContent, needsSeparator, "changedContent");
+ needsSeparator = _appendSources2(builder, changedRanges, needsSeparator, "changedRanges");
+ needsSeparator = _appendSources(builder, deletedSources, needsSeparator, "deletedSources");
+ needsSeparator = _appendSources(builder, removedSources, needsSeparator, "removedSources");
int count = removedContainers.length;
if (count > 0) {
- if (_removed.isEmpty) {
+ if (removedSources.isEmpty) {
if (needsSeparator) {
builder.append("; ");
}
@@ -1103,6 +1155,68 @@
}
return true;
}
+
+ /**
+ * Append the given sources to the given builder, prefixed with the given label and possibly a
+ * separator.
+ *
+ * @param builder the builder to which the sources are to be appended
+ * @param sources the sources to be appended
+ * @param needsSeparator `true` if a separator is needed before the label
+ * @param label the label used to prefix the sources
+ * @return `true` if future lists of sources will need a separator
+ */
+ bool _appendSources2(JavaStringBuilder builder, Map<Source, dynamic> sources, bool needsSeparator, String label) {
+ if (sources.isEmpty) {
+ return needsSeparator;
+ }
+ if (needsSeparator) {
+ builder.append("; ");
+ }
+ builder.append(label);
+ String prefix = " ";
+ for (Source source in sources.keys.toSet()) {
+ builder.append(prefix);
+ builder.append(source.fullName);
+ prefix = ", ";
+ }
+ return true;
+ }
+}
+
+/**
+ * Instances of the class `ContentChange` represent a change to the content of a source.
+ */
+class ChangeSet_ContentChange {
+ /**
+ * The new contents of the source.
+ */
+ final String contents;
+
+ /**
+ * The offset into the current contents.
+ */
+ final int offset;
+
+ /**
+ * The number of characters in the original contents that were replaced
+ */
+ final int oldLength;
+
+ /**
+ * The number of characters in the replacement text.
+ */
+ final int newLength;
+
+ /**
+ * Initialize a newly created change object to represent a change to the content of a source.
+ *
+ * @param contents the new contents of the source
+ * @param offset the offset into the current contents
+ * @param oldLength the number of characters in the original contents that were replaced
+ * @param newLength the number of characters in the replacement text
+ */
+ ChangeSet_ContentChange(this.contents, this.offset, this.oldLength, this.newLength);
}
/**
@@ -1139,6 +1253,148 @@
*/
class AnalysisCache {
/**
+ * An array containing the partitions of which this cache is comprised.
+ */
+ final List<CachePartition> _partitions;
+
+ /**
+ * Initialize a newly created cache to have the given partitions. The partitions will be searched
+ * in the order in which they appear in the array, so the most specific partition (usually an
+ * [SdkCachePartition]) should be first and the most general (usually a
+ * [UniversalCachePartition]) last.
+ *
+ * @param partitions the partitions for the newly created cache
+ */
+ AnalysisCache(this._partitions);
+
+ /**
+ * Record that the AST associated with the given source was just read from the cache.
+ *
+ * @param source the source whose AST was accessed
+ */
+ void accessedAst(Source source) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ _partitions[i].accessedAst(source);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return the entry associated with the given source.
+ *
+ * @param source the source whose entry is to be returned
+ * @return the entry associated with the given source
+ */
+ SourceEntry get(Source source) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ return _partitions[i].get(source);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return an iterator returning all of the map entries mapping sources to cache entries.
+ *
+ * @return an iterator returning all of the map entries mapping sources to cache entries
+ */
+ MapIterator<Source, SourceEntry> iterator() {
+ int count = _partitions.length;
+ List<Map<Source, SourceEntry>> maps = new List<Map>(count);
+ for (int i = 0; i < count; i++) {
+ maps[i] = _partitions[i].map;
+ }
+ return new MultipleMapIterator<Source, SourceEntry>(maps);
+ }
+
+ /**
+ * Associate the given entry with the given source.
+ *
+ * @param source the source with which the entry is to be associated
+ * @param entry the entry to be associated with the source
+ */
+ void put(Source source, SourceEntry entry) {
+ (entry as SourceEntryImpl).fixExceptionState();
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ _partitions[i].put(source, entry);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Remove all information related to the given source from this cache.
+ *
+ * @param source the source to be removed
+ */
+ void remove(Source source) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ _partitions[i].remove(source);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Record that the AST associated with the given source was just removed from the cache.
+ *
+ * @param source the source whose AST was removed
+ */
+ void removedAst(Source source) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ _partitions[i].removedAst(source);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return the number of sources that are mapped to cache entries.
+ *
+ * @return the number of sources that are mapped to cache entries
+ */
+ int size() {
+ int size = 0;
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ size += _partitions[i].size();
+ }
+ return size;
+ }
+
+ /**
+ * Record that the AST associated with the given source was just stored to the cache.
+ *
+ * @param source the source whose AST was stored
+ */
+ void storedAst(Source source) {
+ int count = _partitions.length;
+ for (int i = 0; i < count; i++) {
+ if (_partitions[i].contains(source)) {
+ _partitions[i].storedAst(source);
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * Instances of the class `CachePartition` implement a single partition in an LRU cache of
+ * information related to analysis.
+ */
+abstract class CachePartition {
+ /**
* A table mapping the sources known to the context to the information known about the source.
*/
Map<Source, SourceEntry> _sourceMap = new Map<Source, SourceEntry>();
@@ -1169,7 +1425,7 @@
* @param retentionPolicy the policy used to determine which pieces of data to remove from the
* cache
*/
- AnalysisCache(int maxCacheSize, this._retentionPolicy) {
+ CachePartition(int maxCacheSize, this._retentionPolicy) {
this._maxCacheSize = maxCacheSize;
_recentlyUsed = new List<Source>();
}
@@ -1193,6 +1449,14 @@
}
/**
+ * Return `true` if the given source is contained in this partition.
+ *
+ * @param source the source being tested
+ * @return `true` if the source is contained in this partition
+ */
+ bool contains(Source source);
+
+ /**
* Return the entry associated with the given source.
*
* @param source the source whose entry is to be returned
@@ -1201,6 +1465,18 @@
SourceEntry get(Source source) => _sourceMap[source];
/**
+ * Return a table mapping the sources known to the context to the information known about the
+ * source.
+ *
+ * <b>Note:</b> This method is only visible for use by [AnalysisCache] and should not be
+ * used for any other purpose.
+ *
+ * @return a table mapping the sources known to the context to the information known about the
+ * source
+ */
+ Map<Source, SourceEntry> get map => _sourceMap;
+
+ /**
* Return an iterator returning all of the map entries mapping sources to cache entries.
*
* @return an iterator returning all of the map entries mapping sources to cache entries
@@ -1303,11 +1579,6 @@
* flushed from the cache. The source that will be returned will be the source that has been
* unreferenced for the longest period of time but that is not a priority for analysis.
*
- * It is possible for there to be no AST that can be flushed, in which case `null` will be
- * returned. This happens, for example, if the context is reserving the AST's needed to resolve a
- * cycle of libraries and the number of AST's being reserved is larger than the current cache
- * size.
- *
* @return the source that was removed
*/
Source _removeAstToFlush() {
@@ -1322,6 +1593,7 @@
}
}
if (sourceToRemove < 0) {
+ AnalysisEngine.instance.logger.logError2("Internal error: Could not flush data from the cache", new JavaException());
return null;
}
return _recentlyUsed.removeAt(sourceToRemove);
@@ -2823,6 +3095,38 @@
}
/**
+ * Instances of the class `DefaultRetentionPolicy` implement a retention policy that will keep
+ * AST's in the cache if there is analysis information that needs to be computed for a source, where
+ * the computation is dependent on having the AST.
+ */
+class DefaultRetentionPolicy implements CacheRetentionPolicy {
+ /**
+ * An instance of this class that can be shared.
+ */
+ static DefaultRetentionPolicy POLICY = new DefaultRetentionPolicy();
+
+ @override
+ RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) {
+ if (sourceEntry is DartEntry) {
+ DartEntry dartEntry = sourceEntry;
+ if (astIsNeeded(dartEntry)) {
+ return RetentionPriority.MEDIUM;
+ }
+ }
+ return RetentionPriority.LOW;
+ }
+
+ /**
+ * Return `true` if there is analysis information in the given entry that needs to be
+ * computed, where the computation is dependent on having the AST.
+ *
+ * @param dartEntry the entry being tested
+ * @return `true` if there is analysis information that needs to be computed from the AST
+ */
+ bool astIsNeeded(DartEntry dartEntry) => dartEntry.hasInvalidData(DartEntry.HINTS) || dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) || dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
+}
+
+/**
* The interface `HtmlEntry` defines the behavior of objects that maintain the information
* cached by an analysis context about an individual HTML file.
*/
@@ -3426,6 +3730,37 @@
}
/**
+ * Instances of the class `PartitionManager` manage the partitions that can be shared between
+ * analysis contexts.
+ */
+class PartitionManager {
+ /**
+ * A table mapping SDK's to the partitions used for those SDK's.
+ */
+ Map<DartSdk, SdkCachePartition> _sdkPartitions = new Map<DartSdk, SdkCachePartition>();
+
+ /**
+ * The default cache size for a Dart SDK partition.
+ */
+ static int _DEFAULT_SDK_CACHE_SIZE = 256;
+
+ /**
+ * Return the partition being used for the given SDK, creating the partition if necessary.
+ *
+ * @param sdk the SDK for which a partition is being requested
+ * @return the partition being used for the given SDK
+ */
+ SdkCachePartition forSdk(DartSdk sdk) {
+ SdkCachePartition partition = _sdkPartitions[sdk];
+ if (partition == null) {
+ partition = new SdkCachePartition(_DEFAULT_SDK_CACHE_SIZE);
+ _sdkPartitions[sdk] = partition;
+ }
+ return partition;
+ }
+}
+
+/**
* The enumerated type `RetentionPriority` represents the priority of data in the cache in
* terms of the desirability of retaining some specified data about a specified source.
*/
@@ -3455,6 +3790,23 @@
}
/**
+ * Instances of the class `SdkCachePartition` implement a cache partition that contains all of
+ * the sources in the SDK.
+ */
+class SdkCachePartition extends CachePartition {
+ /**
+ * Initialize a newly created partition.
+ *
+ * @param maxCacheSize the maximum number of sources for which AST structures should be kept in
+ * the cache
+ */
+ SdkCachePartition(int maxCacheSize) : super(maxCacheSize, DefaultRetentionPolicy.POLICY);
+
+ @override
+ bool contains(Source source) => source.isInSystemLibrary;
+}
+
+/**
* The interface `SourceEntry` defines the behavior of objects that maintain the information
* cached by an analysis context about an individual source, no matter what kind of source it is.
*
@@ -3633,7 +3985,7 @@
*/
void invalidateAllInformation() {
_content = null;
- _contentState = CacheState.INVALID;
+ _contentState = _checkContentState(CacheState.INVALID);
_lineInfo = null;
_lineInfoState = CacheState.INVALID;
}
@@ -3676,7 +4028,7 @@
void setState(DataDescriptor descriptor, CacheState state) {
if (identical(descriptor, SourceEntry.CONTENT)) {
_content = updatedValue(state, _content, null);
- _contentState = state;
+ _contentState = _checkContentState(state);
} else if (identical(descriptor, SourceEntry.LINE_INFO)) {
_lineInfo = updatedValue(state, _lineInfo, null);
_lineInfoState = state;
@@ -3694,7 +4046,7 @@
void setValue(DataDescriptor descriptor, Object value) {
if (identical(descriptor, SourceEntry.CONTENT)) {
_content = value as String;
- _contentState = CacheState.VALID;
+ _contentState = _checkContentState(CacheState.VALID);
} else if (identical(descriptor, SourceEntry.LINE_INFO)) {
_lineInfo = value as LineInfo;
_lineInfoState = CacheState.VALID;
@@ -3796,6 +4148,43 @@
builder.append("; lineInfo = ");
builder.append(_lineInfoState);
}
+
+ /**
+ * If the state is changing from ERROR to anything else, capture the information. This is an
+ * attempt to discover the underlying cause of a long-standing bug.
+ *
+ * @param newState the new state of the content
+ * @return the new state of the content
+ */
+ CacheState _checkContentState(CacheState newState) {
+ if (_contentState == CacheState.ERROR) {
+ InstrumentationBuilder builder = Instrumentation.builder2("SourceEntryImpl-checkContentState");
+ builder.data3("message", "contentState changing from ${_contentState} to ${newState}");
+ //builder.data("source", source.getFullName());
+ builder.record(new AnalysisException());
+ builder.log();
+ }
+ return newState;
+ }
+}
+
+/**
+ * Instances of the class `UniversalCachePartition` implement a cache partition that contains
+ * all sources not contained in other partitions.
+ */
+class UniversalCachePartition extends CachePartition {
+ /**
+ * Initialize a newly created partition.
+ *
+ * @param maxCacheSize the maximum number of sources for which AST structures should be kept in
+ * the cache
+ * @param retentionPolicy the policy used to determine which pieces of data to remove from the
+ * cache
+ */
+ UniversalCachePartition(int maxCacheSize, CacheRetentionPolicy retentionPolicy) : super(maxCacheSize, retentionPolicy);
+
+ @override
+ bool contains(Source source) => true;
}
/**
@@ -3954,6 +4343,11 @@
Source _coreLibrarySource;
/**
+ * The partition that contains analysis results that are not shared with other contexts.
+ */
+ CachePartition _privatePartition;
+
+ /**
* A table mapping the sources known to the context to the information known about the source.
*/
AnalysisCache _cache;
@@ -3989,7 +4383,7 @@
* results are for the same version (modification time) of the source as our current cache
* content.
*/
- Object _cacheLock = new Object();
+ static Object _cacheLock = new Object();
/**
* The object used to record the results of performing an analysis task.
@@ -4017,7 +4411,8 @@
*/
AnalysisContextImpl() : super() {
_resultRecorder = new AnalysisContextImpl_AnalysisTaskResultRecorder(this);
- _cache = new AnalysisCache(AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, new AnalysisContextImpl_ContextRetentionPolicy(this));
+ _privatePartition = new UniversalCachePartition(AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, new AnalysisContextImpl_ContextRetentionPolicy(this));
+ _cache = _createCacheFromSourceFactory(null);
}
@override
@@ -4052,6 +4447,16 @@
for (Source source in changeSet.changedSources) {
_sourceChanged(source);
}
+ for (MapEntry<Source, String> entry in getMapEntrySet(changeSet.changedContents)) {
+ setContents(entry.getKey(), entry.getValue());
+ }
+ for (MapEntry<Source, ChangeSet_ContentChange> entry in getMapEntrySet(changeSet.changedRanges)) {
+ ChangeSet_ContentChange change = entry.getValue();
+ setChangedContents(entry.getKey(), change.contents, change.offset, change.oldLength, change.newLength);
+ }
+ for (Source source in changeSet.deletedSources) {
+ _sourceDeleted(source);
+ }
for (Source source in removedSources) {
_sourceRemoved(source);
}
@@ -4751,9 +5156,9 @@
@override
void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
Source htmlSource = _sourceFactory.forUri(DartSdk.DART_HTML);
- for (MapIterator<Source, LibraryElement> iter = SingleMapIterator.forMap(elementMap); iter.moveNext();) {
- Source librarySource = iter.key;
- LibraryElement library = iter.value;
+ for (MapEntry<Source, LibraryElement> entry in getMapEntrySet(elementMap)) {
+ Source librarySource = entry.getKey();
+ LibraryElement library = entry.getValue();
//
// Cache the element in the library's info.
//
@@ -4761,6 +5166,15 @@
if (dartEntry != null) {
DartEntryImpl dartCopy = dartEntry.writableCopy;
_recordElementData(dartCopy, library, library.source, htmlSource);
+ dartCopy.setValue(DartEntry.SCAN_ERRORS, AnalysisError.NO_ERRORS);
+ dartCopy.setValue(DartEntry.PARSE_ERRORS, AnalysisError.NO_ERRORS);
+ dartCopy.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED);
+ dartCopy.setValueInLibrary(DartEntry.BUILD_ELEMENT_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+ dartCopy.setValueInLibrary(DartEntry.RESOLUTION_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+ dartCopy.setStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource, CacheState.FLUSHED);
+ dartCopy.setValueInLibrary(DartEntry.VERIFICATION_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+ dartCopy.setValue(DartEntry.ANGULAR_ERRORS, AnalysisError.NO_ERRORS);
+ dartCopy.setValueInLibrary(DartEntry.HINTS, librarySource, AnalysisError.NO_ERRORS);
_cache.put(librarySource, dartCopy);
}
}
@@ -4789,7 +5203,8 @@
int cacheSize = options.cacheSize;
if (this._options.cacheSize != cacheSize) {
this._options.cacheSize = cacheSize;
- _cache.maxCacheSize = cacheSize;
+ //cache.setMaxCacheSize(cacheSize);
+ _privatePartition.maxCacheSize = cacheSize;
//
// Cap the size of the priority list to being less than the cache size. Failure to do so can
// result in an infinite loop in performAnalysisTask() because re-caching one AST structure
@@ -4810,7 +5225,7 @@
this._options.preserveComments = options.preserveComments;
_generateSdkErrors = options.generateSdkErrors;
if (needsRecompute) {
- _invalidateAllResolutionInformation();
+ _invalidateAllLocalResolutionInformation();
}
}
@@ -4896,7 +5311,8 @@
factory.context = this;
_sourceFactory = factory;
_coreLibrarySource = _sourceFactory.forUri(DartSdk.DART_CORE);
- _invalidateAllResolutionInformation();
+ _cache = _createCacheFromSourceFactory(factory);
+ _invalidateAllLocalResolutionInformation();
}
/**
@@ -5419,7 +5835,11 @@
// change, this loop will eventually terminate.
//
LibraryElement library = computeLibraryElement(librarySource);
- dartEntry = new GenerateDartErrorsTask(this, unitSource, dartEntry.modificationTime, resolveCompilationUnit(unitSource, library), library).perform(_resultRecorder) as DartEntry;
+ CompilationUnit unit = resolveCompilationUnit(unitSource, library);
+ if (unit == null) {
+ throw new AnalysisException.con1("Could not resolve compilation unit ${unitSource.fullName} in ${librarySource.fullName}");
+ }
+ dartEntry = new GenerateDartErrorsTask(this, unitSource, dartEntry.modificationTime, unit, library).perform(_resultRecorder) as DartEntry;
state = dartEntry.getStateInLibrary(descriptor, librarySource);
}
return dartEntry;
@@ -5544,6 +5964,25 @@
}
/**
+ * Create an analysis cache based on the given source factory.
+ *
+ * @param factory the source factory containing the information needed to create the cache
+ * @return the cache that was created
+ */
+ AnalysisCache _createCacheFromSourceFactory(SourceFactory factory) {
+ if (factory == null) {
+ return new AnalysisCache(<CachePartition> [_privatePartition]);
+ }
+ DartSdk sdk = factory.dartSdk;
+ if (sdk == null) {
+ return new AnalysisCache(<CachePartition> [_privatePartition]);
+ }
+ return new AnalysisCache(<CachePartition> [
+ AnalysisEngine.instance.partitionManager.forSdk(sdk),
+ _privatePartition]);
+ }
+
+ /**
* Create a [GenerateDartErrorsTask] for the given source, marking the verification errors
* as being in-process. The compilation unit and the library can be the same if the compilation
* unit is the defining compilation unit of the library.
@@ -5559,6 +5998,13 @@
return _createResolveDartLibraryTask(librarySource, libraryEntry);
}
CompilationUnit unit = unitEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource);
+ if (unit == null) {
+ AnalysisEngine.instance.logger.logInformation2("Entry has VALID state for RESOLVED_UNIT but null value for ${unitSource.fullName} in ${librarySource.fullName}", new AnalysisException());
+ DartEntryImpl dartCopy = unitEntry.writableCopy;
+ dartCopy.recordResolutionError();
+ _cache.put(unitSource, dartCopy);
+ return new AnalysisContextImpl_TaskData(null, false);
+ }
LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
DartEntryImpl dartCopy = unitEntry.writableCopy;
dartCopy.setStateInLibrary(DartEntry.VERIFICATION_ERRORS, librarySource, CacheState.IN_PROCESS);
@@ -6592,9 +7038,9 @@
*
* <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock].
*/
- void _invalidateAllResolutionInformation() {
+ void _invalidateAllLocalResolutionInformation() {
Map<Source, List<Source>> oldPartMap = new Map<Source, List<Source>>();
- MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+ MapIterator<Source, SourceEntry> iterator = _privatePartition.iterator();
while (iterator.moveNext()) {
Source source = iterator.key;
SourceEntry sourceEntry = iterator.value;
@@ -7027,9 +7473,9 @@
_cache.put(librarySource, dartCopy);
throw thrownException;
}
- for (MapIterator<Source, TimestampedData<List<AnalysisError>>> iter = SingleMapIterator.forMap(hintMap); iter.moveNext();) {
- Source unitSource = iter.key;
- TimestampedData<List<AnalysisError>> results = iter.value;
+ for (MapEntry<Source, TimestampedData<List<AnalysisError>>> entry in getMapEntrySet(hintMap)) {
+ Source unitSource = entry.getKey();
+ TimestampedData<List<AnalysisError>> results = entry.getValue();
SourceEntry sourceEntry = _cache.get(unitSource);
if (sourceEntry is! DartEntry) {
// This shouldn't be possible because we should never have performed the task if the source
@@ -7943,9 +8389,9 @@
* @param oldPartMap the table containing the parts associated with each library
*/
void _removeFromPartsUsingMap(Map<Source, List<Source>> oldPartMap) {
- for (MapIterator<Source, List<Source>> iter = SingleMapIterator.forMap(oldPartMap); iter.moveNext();) {
- Source librarySource = iter.key;
- List<Source> oldParts = iter.value;
+ for (MapEntry<Source, List<Source>> entry in getMapEntrySet(oldPartMap)) {
+ Source librarySource = entry.getKey();
+ List<Source> oldParts = entry.getValue();
for (int i = 0; i < oldParts.length; i++) {
Source partSource = oldParts[i];
if (partSource != librarySource) {
@@ -8059,6 +8505,37 @@
*
* @param source the source that has been deleted
*/
+ void _sourceDeleted(Source source) {
+ SourceEntry sourceEntry = _cache.get(source);
+ if (sourceEntry is HtmlEntry) {
+ HtmlEntryImpl htmlCopy = sourceEntry.writableCopy;
+ _invalidateAngularResolution(htmlCopy);
+ htmlCopy.recordContentError();
+ _cache.put(source, htmlCopy);
+ } else if (sourceEntry is DartEntry) {
+ Set<Source> libraries = new Set<Source>();
+ for (Source librarySource in getLibrariesContaining(source)) {
+ libraries.add(librarySource);
+ for (Source dependentLibrary in getLibrariesDependingOn(librarySource)) {
+ libraries.add(dependentLibrary);
+ }
+ }
+ for (Source librarySource in libraries) {
+ _invalidateLibraryResolution(librarySource);
+ }
+ DartEntryImpl dartCopy = sourceEntry.writableCopy;
+ dartCopy.recordContentError();
+ _cache.put(source, dartCopy);
+ }
+ _workManager.remove(source);
+ _removeFromPriorityOrder(source);
+ }
+
+ /**
+ * <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock].
+ *
+ * @param source the source that has been removed
+ */
void _sourceRemoved(Source source) {
SourceEntry sourceEntry = _cache.get(source);
if (sourceEntry is HtmlEntry) {
@@ -8684,7 +9161,8 @@
* @param dartEntry the entry associated with the source
*/
void _ensureResolvableCompilationUnit(Source source, DartEntry dartEntry) {
- if (!dartEntry.hasResolvableCompilationUnit) {
+ // The entry will be null if the source represents a non-Dart file.
+ if (dartEntry != null && !dartEntry.hasResolvableCompilationUnit) {
if (_taskData == null) {
_taskData = AnalysisContextImpl_this._createParseDartTask(source, dartEntry);
}
@@ -8964,319 +9442,6 @@
}
/**
- * Instances of the class `DelegatingAnalysisContextImpl` extend [AnalysisContextImpl
- ] to delegate sources to the appropriate analysis context. For instance, if the
- * source is in a system library then the analysis context from the [DartSdk] is used.
- */
-class DelegatingAnalysisContextImpl extends AnalysisContextImpl {
- /**
- * This references the [InternalAnalysisContext] held onto by the [DartSdk] which is
- * used (instead of this [AnalysisContext]) for SDK sources. This field is set when
- * #setSourceFactory(SourceFactory) is called, and references the analysis context in the
- * [DartUriResolver] in the [SourceFactory], this analysis context assumes that there
- * will be such a resolver.
- */
- InternalAnalysisContext _sdkAnalysisContext;
-
- @override
- void addSourceInfo(Source source, SourceEntry info) {
- if (source.isInSystemLibrary) {
- _sdkAnalysisContext.addSourceInfo(source, info);
- } else {
- super.addSourceInfo(source, info);
- }
- }
-
- @override
- List<AnalysisError> computeErrors(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeErrors(source);
- } else {
- return super.computeErrors(source);
- }
- }
-
- @override
- List<Source> computeExportedLibraries(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeExportedLibraries(source);
- } else {
- return super.computeExportedLibraries(source);
- }
- }
-
- @override
- HtmlElement computeHtmlElement(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeHtmlElement(source);
- } else {
- return super.computeHtmlElement(source);
- }
- }
-
- @override
- List<Source> computeImportedLibraries(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeImportedLibraries(source);
- } else {
- return super.computeImportedLibraries(source);
- }
- }
-
- @override
- SourceKind computeKindOf(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeKindOf(source);
- } else {
- return super.computeKindOf(source);
- }
- }
-
- @override
- LibraryElement computeLibraryElement(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeLibraryElement(source);
- } else {
- return super.computeLibraryElement(source);
- }
- }
-
- @override
- LineInfo computeLineInfo(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeLineInfo(source);
- } else {
- return super.computeLineInfo(source);
- }
- }
-
- @override
- ResolvableCompilationUnit computeResolvableCompilationUnit(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.computeResolvableCompilationUnit(source);
- } else {
- return super.computeResolvableCompilationUnit(source);
- }
- }
-
- @override
- AnalysisErrorInfo getErrors(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getErrors(source);
- } else {
- return super.getErrors(source);
- }
- }
-
- @override
- HtmlElement getHtmlElement(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getHtmlElement(source);
- } else {
- return super.getHtmlElement(source);
- }
- }
-
- @override
- List<Source> getHtmlFilesReferencing(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getHtmlFilesReferencing(source);
- } else {
- return super.getHtmlFilesReferencing(source);
- }
- }
-
- @override
- SourceKind getKindOf(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getKindOf(source);
- } else {
- return super.getKindOf(source);
- }
- }
-
- @override
- List<Source> getLibrariesContaining(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getLibrariesContaining(source);
- } else {
- return super.getLibrariesContaining(source);
- }
- }
-
- @override
- List<Source> getLibrariesDependingOn(Source librarySource) {
- if (librarySource.isInSystemLibrary) {
- return _sdkAnalysisContext.getLibrariesDependingOn(librarySource);
- } else {
- return super.getLibrariesDependingOn(librarySource);
- }
- }
-
- @override
- LibraryElement getLibraryElement(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getLibraryElement(source);
- } else {
- return super.getLibraryElement(source);
- }
- }
-
- @override
- List<Source> get librarySources => ArrayUtils.addAll(super.librarySources, _sdkAnalysisContext.librarySources);
-
- @override
- LineInfo getLineInfo(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getLineInfo(source);
- } else {
- return super.getLineInfo(source);
- }
- }
-
- @override
- Namespace getPublicNamespace(LibraryElement library) {
- Source source = library.source;
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.getPublicNamespace(library);
- } else {
- return super.getPublicNamespace(library);
- }
- }
-
- @override
- CompilationUnit getResolvedCompilationUnit(Source unitSource, LibraryElement library) {
- if (unitSource.isInSystemLibrary) {
- return _sdkAnalysisContext.getResolvedCompilationUnit(unitSource, library);
- } else {
- return super.getResolvedCompilationUnit(unitSource, library);
- }
- }
-
- @override
- CompilationUnit getResolvedCompilationUnit2(Source unitSource, Source librarySource) {
- if (unitSource.isInSystemLibrary) {
- return _sdkAnalysisContext.getResolvedCompilationUnit2(unitSource, librarySource);
- } else {
- return super.getResolvedCompilationUnit2(unitSource, librarySource);
- }
- }
-
- @override
- bool isClientLibrary(Source librarySource) {
- if (librarySource.isInSystemLibrary) {
- return _sdkAnalysisContext.isClientLibrary(librarySource);
- } else {
- return super.isClientLibrary(librarySource);
- }
- }
-
- @override
- bool isServerLibrary(Source librarySource) {
- if (librarySource.isInSystemLibrary) {
- return _sdkAnalysisContext.isServerLibrary(librarySource);
- } else {
- return super.isServerLibrary(librarySource);
- }
- }
-
- @override
- CompilationUnit parseCompilationUnit(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.parseCompilationUnit(source);
- } else {
- return super.parseCompilationUnit(source);
- }
- }
-
- @override
- ht.HtmlUnit parseHtmlUnit(Source source) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.parseHtmlUnit(source);
- } else {
- return super.parseHtmlUnit(source);
- }
- }
-
- @override
- void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
- if (elementMap.isEmpty) {
- return;
- }
- // TODO(jwren) we are making the assumption here that the elementMap will have sources from only
- // one library, while this is true with our use of the Analysis Engine, it is not required by
- // the API, revisit to fix cases where the elementMap can have sources both in the sdk and other
- // libraries
- Source source = new JavaIterator(elementMap.keys.toSet()).next();
- if (source.isInSystemLibrary) {
- _sdkAnalysisContext.recordLibraryElements(elementMap);
- } else {
- super.recordLibraryElements(elementMap);
- }
- }
-
- @override
- CompilationUnit resolveCompilationUnit(Source source, LibraryElement library) {
- if (source.isInSystemLibrary) {
- return _sdkAnalysisContext.resolveCompilationUnit(source, library);
- } else {
- return super.resolveCompilationUnit(source, library);
- }
- }
-
- @override
- CompilationUnit resolveCompilationUnit2(Source unitSource, Source librarySource) {
- if (unitSource.isInSystemLibrary) {
- return _sdkAnalysisContext.resolveCompilationUnit2(unitSource, librarySource);
- } else {
- return super.resolveCompilationUnit2(unitSource, librarySource);
- }
- }
-
- @override
- ht.HtmlUnit resolveHtmlUnit(Source unitSource) {
- if (unitSource.isInSystemLibrary) {
- return _sdkAnalysisContext.resolveHtmlUnit(unitSource);
- } else {
- return super.resolveHtmlUnit(unitSource);
- }
- }
-
- @override
- void setChangedContents(Source source, String contents, int offset, int oldLength, int newLength) {
- if (source.isInSystemLibrary) {
- _sdkAnalysisContext.setChangedContents(source, contents, offset, oldLength, newLength);
- } else {
- super.setChangedContents(source, contents, offset, oldLength, newLength);
- }
- }
-
- @override
- void setContents(Source source, String contents) {
- if (source.isInSystemLibrary) {
- _sdkAnalysisContext.setContents(source, contents);
- } else {
- super.setContents(source, contents);
- }
- }
-
- @override
- void set sourceFactory(SourceFactory factory) {
- super.sourceFactory = factory;
- DartSdk sdk = factory.dartSdk;
- if (sdk != null) {
- _sdkAnalysisContext = sdk.context as InternalAnalysisContext;
- if (_sdkAnalysisContext is DelegatingAnalysisContextImpl) {
- _sdkAnalysisContext = null;
- throw new IllegalStateException("The context provided by an SDK cannot itself be a delegating analysis context");
- }
- } else {
- throw new IllegalStateException("SourceFactorys provided to DelegatingAnalysisContextImpls must have a DartSdk associated with the provided SourceFactory.");
- }
- }
-}
-
-/**
* Instances of the class `IncrementalAnalysisCache` hold information used to perform
* incremental analysis.
*
@@ -9382,7 +9547,7 @@
*/
static IncrementalAnalysisCache verifyStructure(IncrementalAnalysisCache cache, Source source, CompilationUnit unit) {
if (cache != null && unit != null && cache.source == source) {
- if (!AstComparator.equalUnits(cache.resolvedUnit, unit)) {
+ if (!AstComparator.equalNodes(cache.resolvedUnit, unit)) {
return null;
}
}
@@ -9487,7 +9652,7 @@
* Create a new [InstrumentedAnalysisContextImpl] which wraps a new
* [AnalysisContextImpl] as the basis context.
*/
- InstrumentedAnalysisContextImpl() : this.con1(new DelegatingAnalysisContextImpl());
+ InstrumentedAnalysisContextImpl() : this.con1(new AnalysisContextImpl());
/**
* Create a new [InstrumentedAnalysisContextImpl] with a specified basis context, aka the
@@ -10360,14 +10525,14 @@
* @return an array of errors (not `null`, contains no `null`s)
*/
List<AnalysisError> get errors {
- Iterable<Set<AnalysisError>> errorsSets = _errors.values;
- int numEntries = errorsSets.length;
+ Iterable<MapEntry<Source, Set<AnalysisError>>> entrySet = getMapEntrySet(_errors);
+ int numEntries = entrySet.length;
if (numEntries == 0) {
return AnalysisError.NO_ERRORS;
}
List<AnalysisError> resultList = new List<AnalysisError>();
- for (Set<AnalysisError> errorsSet in errorsSets) {
- resultList.addAll(errorsSet);
+ for (MapEntry<Source, Set<AnalysisError>> entry in entrySet) {
+ resultList.addAll(entry.getValue());
}
return new List.from(resultList);
}
@@ -11274,8 +11439,13 @@
Token filterToken = tokens[i];
Token barToken = filterToken;
filterToken = filterToken.next;
- // TODO(scheglov) report missing identifier
- SimpleIdentifier name = _parseDartExpressionInToken(filterToken) as SimpleIdentifier;
+ // parse name
+ Expression nameExpression = _parseDartExpressionInToken(filterToken);
+ if (nameExpression is! SimpleIdentifier) {
+ _reportErrorForNode(AngularCode.INVALID_FILTER_NAME, nameExpression, []);
+ continue;
+ }
+ SimpleIdentifier name = nameExpression as SimpleIdentifier;
filterToken = name.endToken.next;
// parse arguments
List<AngularFilterArgument> arguments = [];
@@ -11570,10 +11740,6 @@
}
_libraryElement.imports = new List.from(imports);
}
- // push conditional errors
- for (ProxyConditionalAnalysisError conditionalCode in _resolver.proxyConditionalAnalysisErrors) {
- _resolver.reportError(conditionalCode.analysisError);
- }
}
void _resolveXmlExpression(AngularXmlExpression angularXmlExpression) {
@@ -14279,12 +14445,6 @@
InheritanceManager inheritanceManager = new InheritanceManager(_libraryElement);
ResolverVisitor resolverVisitor = new ResolverVisitor.con2(_libraryElement, source, typeProvider, inheritanceManager, errorListener);
unit.accept(resolverVisitor);
- // TODO (jwren) Move this logic/ loop into the ResolverVisitor and then make the reportError protected again.
- for (ProxyConditionalAnalysisError conditionalCode in resolverVisitor.proxyConditionalAnalysisErrors) {
- if (conditionalCode.shouldIncludeErrorCode()) {
- resolverVisitor.reportError(conditionalCode.analysisError);
- }
- }
//
// Perform additional error checking.
//
diff --git a/pkg/analyzer/lib/src/generated/error.dart b/pkg/analyzer/lib/src/generated/error.dart
index e944834..e71e593 100644
--- a/pkg/analyzer/lib/src/generated/error.dart
+++ b/pkg/analyzer/lib/src/generated/error.dart
@@ -192,34 +192,37 @@
class AngularCode extends Enum<AngularCode> implements ErrorCode {
static const AngularCode CANNOT_PARSE_SELECTOR = const AngularCode('CANNOT_PARSE_SELECTOR', 0, "The selector '%s' cannot be parsed");
- static const AngularCode INVALID_PROPERTY_KIND = const AngularCode('INVALID_PROPERTY_KIND', 1, "Unknown property binding kind '%s', use one of the '@', '=>', '=>!' or '<=>'");
+ static const AngularCode INVALID_FILTER_NAME = const AngularCode('INVALID_FILTER_NAME', 1, "Filter name must be a simple identifier");
- static const AngularCode INVALID_PROPERTY_FIELD = const AngularCode('INVALID_PROPERTY_FIELD', 2, "Unknown property field '%s'");
+ static const AngularCode INVALID_PROPERTY_KIND = const AngularCode('INVALID_PROPERTY_KIND', 2, "Unknown property binding kind '%s', use one of the '@', '=>', '=>!' or '<=>'");
- static const AngularCode INVALID_PROPERTY_MAP = const AngularCode('INVALID_PROPERTY_MAP', 3, "Argument 'map' must be a constant map literal");
+ static const AngularCode INVALID_PROPERTY_FIELD = const AngularCode('INVALID_PROPERTY_FIELD', 3, "Unknown property field '%s'");
- static const AngularCode INVALID_PROPERTY_NAME = const AngularCode('INVALID_PROPERTY_NAME', 4, "Property name must be a string literal");
+ static const AngularCode INVALID_PROPERTY_MAP = const AngularCode('INVALID_PROPERTY_MAP', 4, "Argument 'map' must be a constant map literal");
- static const AngularCode INVALID_PROPERTY_SPEC = const AngularCode('INVALID_PROPERTY_SPEC', 5, "Property binding specification must be a string literal");
+ static const AngularCode INVALID_PROPERTY_NAME = const AngularCode('INVALID_PROPERTY_NAME', 5, "Property name must be a string literal");
- static const AngularCode INVALID_REPEAT_SYNTAX = const AngularCode('INVALID_REPEAT_SYNTAX', 6, "Expected statement in form '_item_ in _collection_ [tracked by _id_]'");
+ static const AngularCode INVALID_PROPERTY_SPEC = const AngularCode('INVALID_PROPERTY_SPEC', 6, "Property binding specification must be a string literal");
- static const AngularCode INVALID_REPEAT_ITEM_SYNTAX = const AngularCode('INVALID_REPEAT_ITEM_SYNTAX', 7, "Item must by identifier or in '(_key_, _value_)' pair.");
+ static const AngularCode INVALID_REPEAT_SYNTAX = const AngularCode('INVALID_REPEAT_SYNTAX', 7, "Expected statement in form '_item_ in _collection_ [tracked by _id_]'");
- static const AngularCode INVALID_URI = const AngularCode('INVALID_URI', 8, "Invalid URI syntax: '%s'");
+ static const AngularCode INVALID_REPEAT_ITEM_SYNTAX = const AngularCode('INVALID_REPEAT_ITEM_SYNTAX', 8, "Item must by identifier or in '(_key_, _value_)' pair.");
- static const AngularCode MISSING_FILTER_COLON = const AngularCode('MISSING_FILTER_COLON', 9, "Missing ':' before filter argument");
+ static const AngularCode INVALID_URI = const AngularCode('INVALID_URI', 9, "Invalid URI syntax: '%s'");
- static const AngularCode MISSING_NAME = const AngularCode('MISSING_NAME', 10, "Argument 'name' must be provided");
+ static const AngularCode MISSING_FILTER_COLON = const AngularCode('MISSING_FILTER_COLON', 10, "Missing ':' before filter argument");
- static const AngularCode MISSING_PUBLISH_AS = const AngularCode('MISSING_PUBLISH_AS', 11, "Argument 'publishAs' must be provided");
+ static const AngularCode MISSING_NAME = const AngularCode('MISSING_NAME', 11, "Argument 'name' must be provided");
- static const AngularCode MISSING_SELECTOR = const AngularCode('MISSING_SELECTOR', 12, "Argument 'selector' must be provided");
+ static const AngularCode MISSING_PUBLISH_AS = const AngularCode('MISSING_PUBLISH_AS', 12, "Argument 'publishAs' must be provided");
- static const AngularCode URI_DOES_NOT_EXIST = const AngularCode('URI_DOES_NOT_EXIST', 13, "Target of URI does not exist: '%s'");
+ static const AngularCode MISSING_SELECTOR = const AngularCode('MISSING_SELECTOR', 13, "Argument 'selector' must be provided");
+
+ static const AngularCode URI_DOES_NOT_EXIST = const AngularCode('URI_DOES_NOT_EXIST', 14, "Target of URI does not exist: '%s'");
static const List<AngularCode> values = const [
CANNOT_PARSE_SELECTOR,
+ INVALID_FILTER_NAME,
INVALID_PROPERTY_KIND,
INVALID_PROPERTY_FIELD,
INVALID_PROPERTY_MAP,
@@ -1534,7 +1537,7 @@
* @param expressionSource the expression source code that is the unexpected type
* @param expectedType the name of the expected type
*/
- static const CompileTimeErrorCode INCONSISTENT_CASE_EXPRESSION_TYPES = const CompileTimeErrorCode.con1('INCONSISTENT_CASE_EXPRESSION_TYPES', 65, "Case expressions must have the same types, '%s' is not a %s'");
+ static const CompileTimeErrorCode INCONSISTENT_CASE_EXPRESSION_TYPES = const CompileTimeErrorCode.con1('INCONSISTENT_CASE_EXPRESSION_TYPES', 65, "Case expressions must have the same types, '%s' is not a '%s'");
/**
* 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It is a compile-time
@@ -2104,16 +2107,6 @@
static const CompileTimeErrorCode UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT = const CompileTimeErrorCode.con1('UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT', 134, "The class '%s' does not have a default generative constructor");
/**
- * 12.14.3 Unqualified Invocation: If there exists a lexically visible declaration named
- * <i>id</i>, let <i>f<sub>id</sub></i> be the innermost such declaration. Then: [skip].
- * Otherwise, <i>i</i> is equivalent to <b>this</b>.<i>id</i>(<i>a<sub>1</sub></i>; ...
- * <i>x<sub>n+k</sub></i> : <i>a<sub>n+k</sub></i>).
- *
- * @param methodName the name of the method that is undefined
- */
- static const CompileTimeErrorCode UNDEFINED_FUNCTION = const CompileTimeErrorCode.con1('UNDEFINED_FUNCTION', 135, "The function '%s' is not defined");
-
- /**
* 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
* must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> ...
* <i>p<sub>n+k</sub></i>} or a static warning occurs.
@@ -2123,7 +2116,7 @@
*
* @param name the name of the requested named parameter
*/
- static const CompileTimeErrorCode UNDEFINED_NAMED_PARAMETER = const CompileTimeErrorCode.con1('UNDEFINED_NAMED_PARAMETER', 136, "The named parameter '%s' is not defined");
+ static const CompileTimeErrorCode UNDEFINED_NAMED_PARAMETER = const CompileTimeErrorCode.con1('UNDEFINED_NAMED_PARAMETER', 135, "The named parameter '%s' is not defined");
/**
* 14.2 Exports: It is a compile-time error if the compilation unit found at the specified URI is
@@ -2138,7 +2131,7 @@
* @param uri the URI pointing to a non-existent file
* @see #INVALID_URI
*/
- static const CompileTimeErrorCode URI_DOES_NOT_EXIST = const CompileTimeErrorCode.con1('URI_DOES_NOT_EXIST', 137, "Target of URI does not exist: '%s'");
+ static const CompileTimeErrorCode URI_DOES_NOT_EXIST = const CompileTimeErrorCode.con1('URI_DOES_NOT_EXIST', 136, "Target of URI does not exist: '%s'");
/**
* 14.1 Imports: It is a compile-time error if <i>x</i> is not a compile-time constant, or if
@@ -2150,7 +2143,7 @@
* 14.5 URIs: It is a compile-time error if the string literal <i>x</i> that describes a URI is
* not a compile-time constant, or if <i>x</i> involves string interpolation.
*/
- static const CompileTimeErrorCode URI_WITH_INTERPOLATION = const CompileTimeErrorCode.con1('URI_WITH_INTERPOLATION', 138, "URIs cannot use string interpolation");
+ static const CompileTimeErrorCode URI_WITH_INTERPOLATION = const CompileTimeErrorCode.con1('URI_WITH_INTERPOLATION', 137, "URIs cannot use string interpolation");
/**
* 7.1.1 Operators: It is a compile-time error if the arity of the user-declared operator []= is
@@ -2163,7 +2156,7 @@
* @param expectedNumberOfParameters the number of parameters expected
* @param actualNumberOfParameters the number of parameters found in the operator declaration
*/
- static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR', 139, "Operator '%s' should declare exactly %d parameter(s), but %d found");
+ static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR', 138, "Operator '%s' should declare exactly %d parameter(s), but %d found");
/**
* 7.1.1 Operators: It is a compile time error if the arity of the user-declared operator - is not
@@ -2171,13 +2164,13 @@
*
* @param actualNumberOfParameters the number of parameters found in the operator declaration
*/
- static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS', 140, "Operator '-' should declare 0 or 1 parameter, but %d found");
+ static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS', 139, "Operator '-' should declare 0 or 1 parameter, but %d found");
/**
* 7.3 Setters: It is a compile-time error if a setter's formal parameter list does not include
* exactly one required formal parameter <i>p</i>.
*/
- static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER', 141, "Setters should declare exactly one required parameter");
+ static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER', 140, "Setters should declare exactly one required parameter");
static const List<CompileTimeErrorCode> values = const [
AMBIGUOUS_EXPORT,
@@ -2315,7 +2308,6 @@
UNDEFINED_CLASS,
UNDEFINED_CONSTRUCTOR_IN_INITIALIZER,
UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT,
- UNDEFINED_FUNCTION,
UNDEFINED_NAMED_PARAMETER,
URI_DOES_NOT_EXIST,
URI_WITH_INTERPOLATION,
@@ -2670,14 +2662,13 @@
static const StaticWarningCode IMPORT_DUPLICATED_LIBRARY_NAME = const StaticWarningCode.con1('IMPORT_DUPLICATED_LIBRARY_NAME', 25, "The imported libraries '%s' and '%s' should not have the same name '%s'");
/**
- * 8.1.1 Inheritance and Overriding: However, if there are multiple members <i>m<sub>1</sub>,
- * … m<sub>k</sub></i> with the same name <i>n</i> that would be inherited (because
- * identically named members existed in several superinterfaces) then at most one member is
- * inherited.
+ * 8.1.1 Inheritance and Overriding: However, if the above rules would cause multiple members
+ * <i>m<sub>1</sub>, …, m<sub>k</sub></i> with the same name <i>n</i> that would be
+ * inherited (because identically named members existed in several superinterfaces) then at most
+ * one member is inherited.
*
- * If some but not all of the <i>m<sub>i</sub>, 1 <= i <= k</i>, are getters, or if some but
- * not all of the <i>m<sub>i</sub></i> are setters, none of the <i>m<sub>i</sub></i> are
- * inherited, and a static warning is issued.
+ * If some but not all of the <i>m<sub>i</sub>, 1 <= i <= k</i> are getters none of the
+ * <i>m<sub>i</sub></i> are inherited, and a static warning is issued.
*/
static const StaticWarningCode INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD = const StaticWarningCode.con1('INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD', 26, "'%s' is inherited as a getter and also a method");
@@ -2807,7 +2798,7 @@
static const StaticWarningCode INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE = const StaticWarningCode.con1('INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE', 38, "The parameter type '%s' is not assignable to '%s' as required by the setter it is overriding from '%s'");
/**
- * 12.6 Lists: A run-time list literal <<i>E</i>> [<i>e<sub>1</sub></i> ...
+ * 12.6 Lists: A run-time list literal <<i>E</i>> [<i>e<sub>1</sub></i> …
* <i>e<sub>n</sub></i>] is evaluated as follows:
* * The operator []= is invoked on <i>a</i> with first argument <i>i</i> and second argument
* <i>o<sub>i+1</sub></i><i>, 1 <= i <= n</i>
@@ -2822,7 +2813,8 @@
/**
* 12.7 Map: A run-time map literal <<i>K</i>, <i>V</i>> [<i>k<sub>1</sub></i> :
- * <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as follows:
+ * <i>e<sub>1</sub></i> … <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as
+ * follows:
* * The operator []= is invoked on <i>m</i> with first argument <i>k<sub>i</sub></i> and second
* argument <i>e<sub>i</sub></i><i>, 1 <= i <= n</i>
*
@@ -2836,7 +2828,8 @@
/**
* 12.7 Map: A run-time map literal <<i>K</i>, <i>V</i>> [<i>k<sub>1</sub></i> :
- * <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as follows:
+ * <i>e<sub>1</sub></i> … <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as
+ * follows:
* * The operator []= is invoked on <i>m</i> with first argument <i>k<sub>i</sub></i> and second
* argument <i>e<sub>i</sub></i><i>, 1 <= i <= n</i>
*
@@ -2899,7 +2892,7 @@
* x<sub>n+1</sub>: a<sub>n+1</sub>, …, x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a
* static warning if <i>T.id</i> is not the name of a constructor declared by the type <i>T</i>.
* If <i>e</i> of the form <i>new T(a<sub>1</sub>, …, a<sub>n</sub>, x<sub>n+1</sub>:
- * a<sub>n+1</sub>, … x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
+ * a<sub>n+1</sub>, …, x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
* type <i>T</i> does not declare a constructor with the same name as the declaration of <i>T</i>.
*/
static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR = const StaticWarningCode.con1('NEW_WITH_UNDEFINED_CONSTRUCTOR', 48, "The class '%s' does not have a constructor '%s'");
@@ -2910,7 +2903,7 @@
* x<sub>n+1</sub>: a<sub>n+1</sub>, …, x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a
* static warning if <i>T.id</i> is not the name of a constructor declared by the type <i>T</i>.
* If <i>e</i> of the form <i>new T(a<sub>1</sub>, …, a<sub>n</sub>, x<sub>n+1</sub>:
- * a<sub>n+1</sub>, … x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
+ * a<sub>n+1</sub>, …, x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
* type <i>T</i> does not declare a constructor with the same name as the declaration of <i>T</i>.
*/
static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT = const StaticWarningCode.con1('NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT', 49, "The class '%s' does not have a default constructor");
@@ -3165,8 +3158,8 @@
static const StaticWarningCode UNDEFINED_IDENTIFIER = const StaticWarningCode.con1('UNDEFINED_IDENTIFIER', 73, "Undefined name '%s'");
/**
- * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
- * must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> ...
+ * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
+ * must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> …
* <i>p<sub>n+k</sub></i>} or a static warning occurs.
*
* @param name the name of the requested named parameter
@@ -3429,24 +3422,26 @@
static const StaticTypeWarningCode INACCESSIBLE_SETTER = const StaticTypeWarningCode.con1('INACCESSIBLE_SETTER', 2, "");
/**
- * 8.1.1 Inheritance and Overriding: However, if there are multiple members <i>m<sub>1</sub>,
- * … m<sub>k</sub></i> with the same name <i>n</i> that would be inherited (because
- * identically named members existed in several superinterfaces) then at most one member is
- * inherited.
+ * 8.1.1 Inheritance and Overriding: However, if the above rules would cause multiple members
+ * <i>m<sub>1</sub>, …, m<sub>k</sub></i> with the same name <i>n</i> that would be
+ * inherited (because identically named members existed in several superinterfaces) then at most
+ * one member is inherited.
*
* If the static types <i>T<sub>1</sub>, …, T<sub>k</sub></i> of the members
* <i>m<sub>1</sub>, …, m<sub>k</sub></i> are not identical, then there must be a member
- * <i>m<sub>x</sub></i> such that <i>T<sub>x</sub> < T<sub>i</sub>, 1 <= x <= k</i> for
- * all <i>i, 1 <= i < k</i>, or a static type warning occurs. The member that is inherited
+ * <i>m<sub>x</sub></i> such that <i>T<sub>x</sub> <: T<sub>i</sub>, 1 <= x <= k</i> for
+ * all <i>i, 1 <= i <= k</i>, or a static type warning occurs. The member that is inherited
* is <i>m<sub>x</sub></i>, if it exists; otherwise:
- * <ol>
- * * If all of <i>m<sub>1</sub>, … m<sub>k</sub></i> have the same number <i>r</i> of
- * required parameters and the same set of named parameters <i>s</i>, then let <i>h = max(
- * numberOfOptionalPositionals( m<sub>i</sub> ) ), 1 <= i <= k</i>. <i>I</i> has a method
- * named <i>n</i>, with <i>r</i> required parameters of type dynamic, <i>h</i> optional positional
- * parameters of type dynamic, named parameters <i>s</i> of type dynamic and return type dynamic.
+ * * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
+ * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
+ * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
+ * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then let
+ * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+ * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
+ * If <i>r <= h</i> then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters
+ * of type <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
+ * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
* * Otherwise none of the members <i>m<sub>1</sub>, …, m<sub>k</sub></i> is inherited.
- * </ol>
*/
static const StaticTypeWarningCode INCONSISTENT_METHOD_INHERITANCE = const StaticTypeWarningCode.con1('INCONSISTENT_METHOD_INHERITANCE', 3, "'%s' is inherited by at least two interfaces inconsistently, from %s");
@@ -3500,7 +3495,7 @@
/**
* 12.14.4 Function Expression Invocation: A function expression invocation <i>i</i> has the form
- * <i>e<sub>f</sub>(a<sub>1</sub>, … a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
+ * <i>e<sub>f</sub>(a<sub>1</sub>, …, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
* …, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>, where <i>e<sub>f</sub></i> is an expression.
*
* It is a static type warning if the static type <i>F</i> of <i>e<sub>f</sub></i> may not be
@@ -3559,12 +3554,13 @@
* <i>G</i>.
*
* 15.8 Parameterized Types: If <i>S</i> is the static type of a member <i>m</i> of <i>G</i>, then
- * the static type of the member <i>m</i> of <i>G<A<sub>1</sub>, … A<sub>n</sub>></i>
- * is <i>[A<sub>1</sub>, …, A<sub>n</sub>/T<sub>1</sub>, …, T<sub>n</sub>]S</i>
- * where <i>T<sub>1</sub>, … T<sub>n</sub></i> are the formal type parameters of <i>G</i>.
- * Let <i>B<sub>i</sub></i> be the bounds of <i>T<sub>i</sub>, 1 <= i <= n</i>. It is a
- * static type warning if <i>A<sub>i</sub></i> is not a subtype of <i>[A<sub>1</sub>, …,
- * A<sub>n</sub>/T<sub>1</sub>, …, T<sub>n</sub>]B<sub>i</sub>, 1 <= i <= n</i>.
+ * the static type of the member <i>m</i> of <i>G<A<sub>1</sub>, …,
+ * A<sub>n</sub>></i> is <i>[A<sub>1</sub>, …, A<sub>n</sub>/T<sub>1</sub>, …,
+ * T<sub>n</sub>]S</i> where <i>T<sub>1</sub>, …, T<sub>n</sub></i> are the formal type
+ * parameters of <i>G</i>. Let <i>B<sub>i</sub></i> be the bounds of <i>T<sub>i</sub>, 1 <= i
+ * <= n</i>. It is a static type warning if <i>A<sub>i</sub></i> is not a subtype of
+ * <i>[A<sub>1</sub>, …, A<sub>n</sub>/T<sub>1</sub>, …,
+ * T<sub>n</sub>]B<sub>i</sub>, 1 <= i <= n</i>.
*
* 7.6.2 Factories: It is a static type warning if any of the type arguments to <i>k'</i> are not
* subtypes of the bounds of the corresponding formal type parameters of type.
@@ -3585,13 +3581,24 @@
static const StaticTypeWarningCode TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND = const StaticTypeWarningCode.con1('TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND', 14, "'%s' cannot be a supertype of its upper bound");
/**
+ * 12.15.3 Unqualified Invocation: If there exists a lexically visible declaration named
+ * <i>id</i>, let <i>f<sub>id</sub></i> be the innermost such declaration. Then: [skip].
+ * Otherwise, <i>f<sub>id</sub></i> is considered equivalent to the ordinary method invocation
+ * <b>this</b>.<i>id</i>(<i>a<sub>1</sub></i>, ..., <i>a<sub>n</sub></i>, <i>x<sub>n+1</sub></i> :
+ * <i>a<sub>n+1</sub></i>, ..., <i>x<sub>n+k</sub></i> : <i>a<sub>n+k</sub></i>).
+ *
+ * @param methodName the name of the method that is undefined
+ */
+ static const StaticTypeWarningCode UNDEFINED_FUNCTION = const StaticTypeWarningCode.con1('UNDEFINED_FUNCTION', 15, "The function '%s' is not defined");
+
+ /**
* 12.17 Getter Invocation: Let <i>T</i> be the static type of <i>e</i>. It is a static type
* warning if <i>T</i> does not have a getter named <i>m</i>.
*
* @param getterName the name of the getter
* @param enclosingType the name of the enclosing type where the getter is being looked for
*/
- static const StaticTypeWarningCode UNDEFINED_GETTER = const StaticTypeWarningCode.con1('UNDEFINED_GETTER', 15, "There is no such getter '%s' in '%s'");
+ static const StaticTypeWarningCode UNDEFINED_GETTER = const StaticTypeWarningCode.con1('UNDEFINED_GETTER', 16, "There is no such getter '%s' in '%s'");
/**
* 12.15.1 Ordinary Invocation: Let <i>T</i> be the static type of <i>o</i>. It is a static type
@@ -3600,7 +3607,7 @@
* @param methodName the name of the method that is undefined
* @param typeName the resolved type name that the method lookup is happening on
*/
- static const StaticTypeWarningCode UNDEFINED_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_METHOD', 16, "The method '%s' is not defined for the class '%s'");
+ static const StaticTypeWarningCode UNDEFINED_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_METHOD', 17, "The method '%s' is not defined for the class '%s'");
/**
* 12.18 Assignment: Evaluation of an assignment of the form
@@ -3618,7 +3625,7 @@
* @param operator the name of the operator
* @param enclosingType the name of the enclosing type where the operator is being looked for
*/
- static const StaticTypeWarningCode UNDEFINED_OPERATOR = const StaticTypeWarningCode.con1('UNDEFINED_OPERATOR', 17, "There is no such operator '%s' in '%s'");
+ static const StaticTypeWarningCode UNDEFINED_OPERATOR = const StaticTypeWarningCode.con1('UNDEFINED_OPERATOR', 18, "There is no such operator '%s' in '%s'");
/**
* 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>. It is a static type
@@ -3628,7 +3635,7 @@
* @param enclosingType the name of the enclosing type where the setter is being looked for
* @see #INACCESSIBLE_SETTER
*/
- static const StaticTypeWarningCode UNDEFINED_SETTER = const StaticTypeWarningCode.con1('UNDEFINED_SETTER', 18, "There is no such setter '%s' in '%s'");
+ static const StaticTypeWarningCode UNDEFINED_SETTER = const StaticTypeWarningCode.con1('UNDEFINED_SETTER', 19, "There is no such setter '%s' in '%s'");
/**
* 12.15.4 Super Invocation: A super method invocation <i>i</i> has the form
@@ -3639,7 +3646,7 @@
* @param methodName the name of the method that is undefined
* @param typeName the resolved type name that the method lookup is happening on
*/
- static const StaticTypeWarningCode UNDEFINED_SUPER_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_SUPER_METHOD', 19, "There is no such method '%s' in '%s'");
+ static const StaticTypeWarningCode UNDEFINED_SUPER_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_SUPER_METHOD', 20, "There is no such method '%s' in '%s'");
/**
* 12.15.1 Ordinary Invocation: It is a static type warning if <i>T</i> does not have an
@@ -3649,7 +3656,7 @@
* able to find the name defined in a supertype. It exists to provide a more informative error
* message.
*/
- static const StaticTypeWarningCode UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER = const StaticTypeWarningCode.con1('UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER', 20, "Static members from supertypes must be qualified by the name of the defining type");
+ static const StaticTypeWarningCode UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER = const StaticTypeWarningCode.con1('UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER', 21, "Static members from supertypes must be qualified by the name of the defining type");
/**
* 15.8 Parameterized Types: It is a static type warning if <i>G</i> is not a generic type with
@@ -3661,7 +3668,7 @@
* @see CompileTimeErrorCode#CONST_WITH_INVALID_TYPE_PARAMETERS
* @see CompileTimeErrorCode#NEW_WITH_INVALID_TYPE_PARAMETERS
*/
- static const StaticTypeWarningCode WRONG_NUMBER_OF_TYPE_ARGUMENTS = const StaticTypeWarningCode.con1('WRONG_NUMBER_OF_TYPE_ARGUMENTS', 21, "The type '%s' is declared with %d type parameters, but %d type arguments were given");
+ static const StaticTypeWarningCode WRONG_NUMBER_OF_TYPE_ARGUMENTS = const StaticTypeWarningCode.con1('WRONG_NUMBER_OF_TYPE_ARGUMENTS', 22, "The type '%s' is declared with %d type parameters, but %d type arguments were given");
static const List<StaticTypeWarningCode> values = const [
EXPECTED_ONE_LIST_TYPE_ARGUMENTS,
@@ -3679,6 +3686,7 @@
RETURN_OF_INVALID_TYPE,
TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND,
+ UNDEFINED_FUNCTION,
UNDEFINED_GETTER,
UNDEFINED_METHOD,
UNDEFINED_OPERATOR,
diff --git a/pkg/analyzer/lib/src/generated/html.dart b/pkg/analyzer/lib/src/generated/html.dart
index 2b26d5b..8438a54 100644
--- a/pkg/analyzer/lib/src/generated/html.dart
+++ b/pkg/analyzer/lib/src/generated/html.dart
@@ -1860,7 +1860,20 @@
/**
* A set containing the names of tags that do not have a closing tag.
*/
- static Set<String> SELF_CLOSING = new Set<String>();
+ static Set<String> SELF_CLOSING = new Set<String>.from(JavaArrays.asList(<String> [
+ "area",
+ "base",
+ "basefont",
+ "br",
+ "col",
+ "frame",
+ "hr",
+ "img",
+ "input",
+ "link",
+ "meta",
+ "param",
+ "!"]));
/**
* Given the contents of an embedded expression that occurs at the given offset, parse it as a
diff --git a/pkg/analyzer/lib/src/generated/index.dart b/pkg/analyzer/lib/src/generated/index.dart
index 4eda02c..544f582 100644
--- a/pkg/analyzer/lib/src/generated/index.dart
+++ b/pkg/analyzer/lib/src/generated/index.dart
@@ -17,7 +17,6 @@
import 'resolver.dart' show Namespace, NamespaceBuilder;
import 'engine.dart';
import 'html.dart' as ht;
-import 'utilities_collection.dart';
/**
* Instances of the [RemoveSourceOperation] implement an operation that removes from the index
@@ -1505,9 +1504,9 @@
importElementsMap[importElement] = elements;
}
// use import namespace to choose correct one
- for (MapIterator<ImportElement, Set<Element>> iter = SingleMapIterator.forMap(importElementsMap); iter.moveNext();) {
- if (iter.value.contains(usedElement)) {
- return iter.key;
+ for (MapEntry<ImportElement, Set<Element>> entry in getMapEntrySet(importElementsMap)) {
+ if (entry.getValue().contains(usedElement)) {
+ return entry.getKey();
}
}
// not found
diff --git a/pkg/analyzer/lib/src/generated/java_core.dart b/pkg/analyzer/lib/src/generated/java_core.dart
index a075ca8..3ee1055 100644
--- a/pkg/analyzer/lib/src/generated/java_core.dart
+++ b/pkg/analyzer/lib/src/generated/java_core.dart
@@ -220,6 +220,9 @@
}
return sb.toString();
}
+ static String join(Iterable iter, [String separator = " "]) {
+ return iter.join(separator);
+ }
}
class Math {
@@ -257,7 +260,7 @@
}
class UnsupportedOperationException extends JavaException {
- String toString() => "UnsupportedOperationException";
+ UnsupportedOperationException([message = ""]) : super(message);
}
class NoSuchElementException extends JavaException {
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 2721627..dab5fb6 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -1458,6 +1458,12 @@
*/
bool _inSwitch = false;
+ /**
+ * A flag indicating whether the parser is currently in a constructor field initializer, with no
+ * intervening parens, braces, or brackets.
+ */
+ bool _inInitializer = false;
+
static String _HIDE = "hide";
static String _OF = "of";
@@ -1629,28 +1635,34 @@
// Even though unnamed arguments must all appear before any named arguments, we allow them to
// appear in any order so that we can recover faster.
//
- Expression argument = parseArgument();
- arguments.add(argument);
- bool foundNamedArgument = argument is NamedExpression;
- bool generatedError = false;
- while (_optional(TokenType.COMMA)) {
- argument = parseArgument();
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression argument = parseArgument();
arguments.add(argument);
- if (foundNamedArgument) {
- if (!generatedError && argument is! NamedExpression) {
- // Report the error, once, but allow the arguments to be in any order in the AST.
- _reportErrorForCurrentToken(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
- generatedError = true;
+ bool foundNamedArgument = argument is NamedExpression;
+ bool generatedError = false;
+ while (_optional(TokenType.COMMA)) {
+ argument = parseArgument();
+ arguments.add(argument);
+ if (foundNamedArgument) {
+ if (!generatedError && argument is! NamedExpression) {
+ // Report the error, once, but allow the arguments to be in any order in the AST.
+ _reportErrorForCurrentToken(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
+ generatedError = true;
+ }
+ } else if (argument is NamedExpression) {
+ foundNamedArgument = true;
}
- } else if (argument is NamedExpression) {
- foundNamedArgument = true;
}
+ // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see whether there is a
+ // matching right parenthesis. If there is, then we're more likely missing a comma and should
+ // go back to parsing arguments.
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see whether there is a
- // matching right parenthesis. If there is, then we're more likely missing a comma and should
- // go back to parsing arguments.
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
}
/**
@@ -3072,6 +3084,10 @@
* @return `true` if the given token appears to be the beginning of a function expression
*/
bool _isFunctionExpression(Token startToken) {
+ // Function expressions aren't allowed in initializer lists.
+ if (_inInitializer) {
+ return false;
+ }
Token afterParameters = _skipFormalParameterList(startToken);
if (afterParameters == null) {
return false;
@@ -3504,9 +3520,15 @@
Expression _parseAssignableSelector(Expression prefix, bool optional) {
if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
Token leftBracket = andAdvance;
- Expression index = parseExpression2();
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- return new IndexExpression.forTarget(prefix, leftBracket, index, rightBracket);
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression index = parseExpression2();
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ return new IndexExpression.forTarget(prefix, leftBracket, index, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
} else if (_matches(TokenType.PERIOD)) {
Token period = andAdvance;
return new PropertyAccess(prefix, period, parseSimpleIdentifier());
@@ -3617,10 +3639,16 @@
functionName = parseSimpleIdentifier();
} else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
Token leftBracket = andAdvance;
- Expression index = parseExpression2();
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- expression = new IndexExpression.forCascade(period, leftBracket, index, rightBracket);
- period = null;
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression index = parseExpression2();
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ expression = new IndexExpression.forCascade(period, leftBracket, index, rightBracket);
+ period = null;
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
} else {
_reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken, [_currentToken.lexeme]);
functionName = _createSyntheticIdentifier();
@@ -3830,13 +3858,12 @@
typeParameters = parseTypeParameterList();
}
Token equals = _expect(TokenType.EQ);
- if (_matchesKeyword(Keyword.ABSTRACT)) {
- abstractKeyword = andAdvance;
- }
TypeName superclass = parseTypeName();
WithClause withClause = null;
if (_matchesKeyword(Keyword.WITH)) {
withClause = parseWithClause();
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
}
ImplementsClause implementsClause = null;
if (_matchesKeyword(Keyword.IMPLEMENTS)) {
@@ -4235,20 +4262,26 @@
}
SimpleIdentifier fieldName = parseSimpleIdentifier();
Token equals = _expect(TokenType.EQ);
- Expression expression = parseConditionalExpression();
- TokenType tokenType = _currentToken.type;
- if (tokenType == TokenType.PERIOD_PERIOD) {
- List<Expression> cascadeSections = new List<Expression>();
- while (tokenType == TokenType.PERIOD_PERIOD) {
- Expression section = _parseCascadeSection();
- if (section != null) {
- cascadeSections.add(section);
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = true;
+ try {
+ Expression expression = parseConditionalExpression();
+ TokenType tokenType = _currentToken.type;
+ if (tokenType == TokenType.PERIOD_PERIOD) {
+ List<Expression> cascadeSections = new List<Expression>();
+ while (tokenType == TokenType.PERIOD_PERIOD) {
+ Expression section = _parseCascadeSection();
+ if (section != null) {
+ cascadeSections.add(section);
+ }
+ tokenType = _currentToken.type;
}
- tokenType = _currentToken.type;
+ expression = new CascadeExpression(expression, cascadeSections);
}
- expression = new CascadeExpression(expression, cascadeSections);
+ return new ConstructorFieldInitializer(keyword, period, fieldName, equals, expression);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- return new ConstructorFieldInitializer(keyword, period, fieldName, equals, expression);
}
/**
@@ -5055,16 +5088,22 @@
if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
return new ListLiteral(modifier, typeArguments, leftBracket, null, andAdvance);
}
- List<Expression> elements = new List<Expression>();
- elements.add(parseExpression2());
- while (_optional(TokenType.COMMA)) {
- if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
- return new ListLiteral(modifier, typeArguments, leftBracket, elements, andAdvance);
- }
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ List<Expression> elements = new List<Expression>();
elements.add(parseExpression2());
+ while (_optional(TokenType.COMMA)) {
+ if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+ return new ListLiteral(modifier, typeArguments, leftBracket, elements, andAdvance);
+ }
+ elements.add(parseExpression2());
+ }
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ return new ListLiteral(modifier, typeArguments, leftBracket, elements, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- return new ListLiteral(modifier, typeArguments, leftBracket, elements, rightBracket);
}
/**
@@ -5133,15 +5172,21 @@
if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
}
- entries.add(parseMapLiteralEntry());
- while (_optional(TokenType.COMMA)) {
- if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
- }
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
entries.add(parseMapLiteralEntry());
+ while (_optional(TokenType.COMMA)) {
+ if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
+ }
+ entries.add(parseMapLiteralEntry());
+ }
+ Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+ return new MapLiteral(modifier, typeArguments, leftBracket, entries, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
- return new MapLiteral(modifier, typeArguments, leftBracket, entries, rightBracket);
}
/**
@@ -5679,9 +5724,15 @@
return parseFunctionExpression();
}
Token leftParenthesis = andAdvance;
- Expression expression = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- return new ParenthesizedExpression(leftParenthesis, expression, rightParenthesis);
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression expression = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ return new ParenthesizedExpression(leftParenthesis, expression, rightParenthesis);
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
} else if (_matches(TokenType.LT)) {
return _parseListOrMapLiteral(null);
} else if (_matches(TokenType.QUESTION)) {
@@ -5884,9 +5935,15 @@
while (hasMore) {
if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
Token openToken = andAdvance;
- Expression expression = parseExpression2();
- Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
- elements.add(new InterpolationExpression(openToken, expression, rightBracket));
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression expression = parseExpression2();
+ Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+ elements.add(new InterpolationExpression(openToken, expression, rightBracket));
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
} else {
Token openToken = andAdvance;
Expression expression = null;
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 3572534..b933093 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -16,7 +16,6 @@
import 'scanner.dart' as sc;
import 'utilities_dart.dart';
import 'utilities_general.dart';
-import 'utilities_collection.dart';
import 'ast.dart';
import 'parser.dart' show Parser, ParserErrorCode;
import 'sdk.dart' show DartSdk, SdkLibrary;
@@ -5671,9 +5670,13 @@
MethodElement propagatedMethod = _lookUpMethod(leftHandSide, propagatedType, methodName);
node.propagatedElement = propagatedMethod;
if (_shouldReportMissingMember(staticType, staticMethod)) {
- _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_METHOD, operator, [methodName, staticType.displayName]);
+ if (_doesClassElementHaveProxy(staticType.element)) {
+ _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_METHOD, operator, [methodName, staticType.displayName]);
+ }
} else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
- _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_METHOD, operator, [methodName, propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(propagatedType.element)) {
+ _resolver.reportErrorForToken(HintCode.UNDEFINED_METHOD, operator, [methodName, propagatedType.displayName]);
+ }
}
}
}
@@ -5694,9 +5697,13 @@
MethodElement propagatedMethod = _lookUpMethod(leftOperand, propagatedType, methodName);
node.propagatedElement = propagatedMethod;
if (_shouldReportMissingMember(staticType, staticMethod)) {
- _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+ if (_doesClassElementHaveProxy(staticType.element)) {
+ _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+ }
} else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
- _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(propagatedType.element)) {
+ _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+ }
}
}
}
@@ -6126,15 +6133,17 @@
}
if (identical(errorCode, StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION)) {
_resolver.reportErrorForNode(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName, [methodName.name]);
- } else if (identical(errorCode, CompileTimeErrorCode.UNDEFINED_FUNCTION)) {
- _resolver.reportErrorForNode(CompileTimeErrorCode.UNDEFINED_FUNCTION, methodName, [methodName.name]);
+ } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) {
+ _resolver.reportErrorForNode(StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName, [methodName.name]);
} else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) {
String targetTypeName;
if (target == null) {
ClassElement enclosingClass = _resolver.enclosingClass;
targetTypeName = enclosingClass.displayName;
- ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(_resolver.enclosingClass, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+ ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD);
+ if (_doesClassElementHaveProxy(_resolver.enclosingClass)) {
+ _resolver.reportErrorForNode(proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+ }
} else {
// ignore Function "call"
// (if we are about to create a hint using type propagation, then we can use type
@@ -6155,8 +6164,10 @@
return null;
}
targetTypeName = targetType == null ? null : targetType.displayName;
- ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(targetType.element, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+ ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD);
+ if (_doesClassElementHaveProxy(targetType.element)) {
+ _resolver.reportErrorForNode(proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+ }
}
} else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) {
// Generate the type name.
@@ -6191,9 +6202,13 @@
MethodElement propagatedMethod = _lookUpMethod(operand, propagatedType, methodName);
node.propagatedElement = propagatedMethod;
if (_shouldReportMissingMember(staticType, staticMethod)) {
- _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]);
+ if (_doesClassElementHaveProxy(staticType.element)) {
+ _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]);
+ }
} else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
- _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, node.operator, [methodName, propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(propagatedType.element)) {
+ _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, node.operator, [methodName, propagatedType.displayName]);
+ }
}
return null;
}
@@ -6270,9 +6285,13 @@
MethodElement propagatedMethod = _lookUpMethod(operand, propagatedType, methodName);
node.propagatedElement = propagatedMethod;
if (_shouldReportMissingMember(staticType, staticMethod)) {
- _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+ if (_doesClassElementHaveProxy(staticType.element)) {
+ _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+ }
} else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
- _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(propagatedType.element)) {
+ _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+ }
}
}
return null;
@@ -6366,7 +6385,9 @@
Annotation annotation = node.parent as Annotation;
_resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation, []);
} else {
- _resolver.reportProxyConditionalErrorForNode(_resolver.enclosingClass, StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
+ if (_doesClassElementHaveProxy(_resolver.enclosingClass)) {
+ _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
+ }
}
}
node.staticElement = element;
@@ -6520,7 +6541,7 @@
if (target == null) {
ClassElement enclosingClass = _resolver.enclosingClass;
if (enclosingClass == null) {
- return CompileTimeErrorCode.UNDEFINED_FUNCTION;
+ return StaticTypeWarningCode.UNDEFINED_FUNCTION;
} else if (element == null) {
// Proxy-conditional warning, based on state of resolver.getEnclosingClass()
return StaticTypeWarningCode.UNDEFINED_METHOD;
@@ -6537,7 +6558,7 @@
targetType = target.bestType;
}
if (targetType == null) {
- return CompileTimeErrorCode.UNDEFINED_FUNCTION;
+ return StaticTypeWarningCode.UNDEFINED_FUNCTION;
} else if (!targetType.isDynamic && !targetType.isBottom) {
// Proxy-conditional warning, based on state of targetType.getElement()
return StaticTypeWarningCode.UNDEFINED_METHOD;
@@ -6564,17 +6585,21 @@
if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
sc.Token leftBracket = node.leftBracket;
sc.Token rightBracket = node.rightBracket;
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR) as ErrorCode;
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR);
if (leftBracket == null || rightBracket == null) {
- _resolver.reportProxyConditionalErrorForNode(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, node, [
- methodName,
- shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(shouldReportMissingMember_static ? staticType.element : propagatedType.element)) {
+ _resolver.reportErrorForNode(errorCode, node, [
+ methodName,
+ shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+ }
} else {
int offset = leftBracket.offset;
int length = rightBracket.offset - offset + 1;
- _resolver.reportProxyConditionalErrorForOffset(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, offset, length, [
- methodName,
- shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+ if (_doesClassElementHaveProxy(shouldReportMissingMember_static ? staticType.element : propagatedType.element)) {
+ _resolver.reportErrorForOffset(errorCode, offset, length, [
+ methodName,
+ shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+ }
}
return true;
}
@@ -6647,6 +6672,22 @@
}
/**
+ * Return `true` iff the passed [Element] is a [ClassElement] and either has, or
+ * in that is or inherits proxy.
+ *
+ * @param element the enclosing element
+ * @return `true` iff the passed [Element] is a [ClassElement] and either has,
+ * or in that is or inherits proxy
+ * @see ClassElement#isOrInheritsProxy()
+ */
+ bool _doesClassElementHaveProxy(Element element) {
+ if (element is ClassElement) {
+ return !element.isOrInheritsProxy;
+ }
+ return true;
+ }
+
+ /**
* Look for any declarations of the given identifier that are imported using a prefix. Return the
* element that was found, or `null` if the name is not imported using a prefix.
*
@@ -6750,6 +6791,12 @@
return true;
} else if (type is InterfaceType) {
ClassElement classElement = type.element;
+ // 16078 from Gilad: If the type is a Functor with the @proxy annotation, treat it as an
+ // executable type.
+ // example code: NonErrorResolverTest.test_invocationOfNonFunction_proxyOnFunctionClass()
+ if (classElement.isProxy && type.isSubtypeOf(_resolver.typeProvider.functionType)) {
+ return true;
+ }
MethodElement methodElement = classElement.lookUpMethod(CALL_METHOD_NAME, _definingLibrary);
return methodElement != null;
}
@@ -7400,7 +7447,7 @@
String name = nameNode.name;
ParameterElement element = namedParameters[name];
if (element == null) {
- ErrorCode errorCode = (reportError ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER) as ErrorCode;
+ ErrorCode errorCode = (reportError ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER);
_resolver.reportErrorForNode(errorCode, nameNode, [name]);
} else {
resolvedParameters[i] = element;
@@ -7417,10 +7464,10 @@
}
}
if (positionalArgumentCount < requiredParameters.length) {
- ErrorCode errorCode = (reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS) as ErrorCode;
+ ErrorCode errorCode = (reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS);
_resolver.reportErrorForNode(errorCode, argumentList, [requiredParameters.length, positionalArgumentCount]);
} else if (positionalArgumentCount > unnamedParameterCount) {
- ErrorCode errorCode = (reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS) as ErrorCode;
+ ErrorCode errorCode = (reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS);
_resolver.reportErrorForNode(errorCode, argumentList, [unnamedParameterCount, positionalArgumentCount]);
}
return resolvedParameters;
@@ -7620,10 +7667,10 @@
if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
if (staticType.isVoid) {
if (propertyName.inSetterContext()) {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
_resolver.reportErrorForNode(errorCode, propertyName, [propertyName.name, staticType.displayName]);
} else if (propertyName.inGetterContext()) {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
_resolver.reportErrorForNode(errorCode, propertyName, [propertyName.name, staticType.displayName]);
} else {
_resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
@@ -7634,30 +7681,40 @@
bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt);
if (propertyName.inSetterContext()) {
if (isStaticProperty) {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
- propertyName.name,
- staticOrPropagatedEnclosingElt.displayName]);
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
+ if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+ _resolver.reportErrorForNode(errorCode, propertyName, [
+ propertyName.name,
+ staticOrPropagatedEnclosingElt.displayName]);
+ }
} else {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
- propertyName.name,
- staticOrPropagatedEnclosingElt.displayName]);
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
+ if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+ _resolver.reportErrorForNode(errorCode, propertyName, [
+ propertyName.name,
+ staticOrPropagatedEnclosingElt.displayName]);
+ }
}
} else if (propertyName.inGetterContext()) {
if (isStaticProperty) {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
- propertyName.name,
- staticOrPropagatedEnclosingElt.displayName]);
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
+ if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+ _resolver.reportErrorForNode(errorCode, propertyName, [
+ propertyName.name,
+ staticOrPropagatedEnclosingElt.displayName]);
+ }
} else {
- ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
- _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
- propertyName.name,
- staticOrPropagatedEnclosingElt.displayName]);
+ ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
+ if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+ _resolver.reportErrorForNode(errorCode, propertyName, [
+ propertyName.name,
+ staticOrPropagatedEnclosingElt.displayName]);
+ }
}
} else {
- _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
+ if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+ _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
+ }
}
}
}
@@ -7959,11 +8016,6 @@
void _resolveReferences(AstNode node, Scope scope) {
ResolverVisitor visitor = new ResolverVisitor.con3(_definingLibrary, _source, _typeProvider, scope, _errorListener);
node.accept(visitor);
- for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
- if (conditionalCode.shouldIncludeErrorCode()) {
- visitor.reportError(conditionalCode.analysisError);
- }
- }
}
void _resolveTypes(AstNode node, Scope scope) {
@@ -7984,12 +8036,20 @@
class InheritanceManager {
/**
* Given some array of [ExecutableElement]s, this method creates a synthetic element as
- * described in the Superinterfaces section of Inheritance and Overriding.
+ * described in 8.1.1:
*
- * TODO (jwren) Copy contents from the Spec into this javadoc.
+ * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
+ * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
+ * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
+ * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then let
+ * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+ * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
+ * If <i>r <= h</i> then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters
+ * of type <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
+ * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
*
* TODO (jwren) Associate a propagated type to the synthetic method element using least upper
- * bound calls
+ * bounds instead of dynamic
*/
static ExecutableElement _computeMergedExecutableElement(List<ExecutableElement> elementArrayToMerge) {
int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
@@ -8633,9 +8693,9 @@
//
// Loop through the entries in the unionMap, adding them to the resultMap appropriately.
//
- for (MapIterator<String, List<ExecutableElement>> iter = SingleMapIterator.forMap(unionMap); iter.moveNext();) {
- String key = iter.key;
- List<ExecutableElement> list = iter.value;
+ for (MapEntry<String, List<ExecutableElement>> entry in getMapEntrySet(unionMap)) {
+ String key = entry.getKey();
+ List<ExecutableElement> list = entry.getValue();
int numOfEltsWithMatchingNames = list.length;
if (numOfEltsWithMatchingNames == 1) {
//
@@ -10126,11 +10186,6 @@
ast.accept(new VariableResolverVisitor.con1(library, source, _typeProvider));
ResolverVisitor visitor = new ResolverVisitor.con1(library, source, _typeProvider);
ast.accept(visitor);
- for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
- if (conditionalCode.shouldIncludeErrorCode()) {
- visitor.reportError(conditionalCode.analysisError);
- }
- }
}
} finally {
timeCounter.stop();
@@ -10567,11 +10622,6 @@
ast.accept(new VariableResolverVisitor.con3(library, source, _typeProvider));
ResolverVisitor visitor = new ResolverVisitor.con4(library, source, _typeProvider);
ast.accept(visitor);
- for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
- if (conditionalCode.shouldIncludeErrorCode()) {
- visitor.reportError(conditionalCode.analysisError);
- }
- }
}
} finally {
timeCounter.stop();
@@ -10764,49 +10814,6 @@
}
/**
- * This class is a wrapper for an [AnalysisError] which can also be queried after resolution
- * to find out if the error should actually be reported. In this case, these errors are conditional
- * on the non-existence of an `@proxy` annotation.
- *
- * If we have other conditional error codes in the future, we should have this class implement some
- * ConditionalErrorCode so that after resolution, a list of ConditionalErrorCode can be visited
- * instead of multiple lists of *ConditionalErrorCodes.
- */
-class ProxyConditionalAnalysisError {
- /**
- * The enclosing [ClassElement], this is what will determine if the error code should, or
- * should not, be generated on the source.
- */
- final Element _enclosingElement;
-
- /**
- * The conditional analysis error.
- */
- final AnalysisError analysisError;
-
- /**
- * Instantiate a new [ProxyConditionalAnalysisError] with some enclosing element and the
- * conditional analysis error.
- *
- * @param enclosingElement the enclosing element
- * @param analysisError the conditional analysis error
- */
- ProxyConditionalAnalysisError(this._enclosingElement, this.analysisError);
-
- /**
- * Return `true` iff the enclosing class has the proxy annotation.
- *
- * @return `true` iff the enclosing class has the proxy annotation
- */
- bool shouldIncludeErrorCode() {
- if (_enclosingElement is ClassElement) {
- return !(_enclosingElement as ClassElement).isOrInheritsProxy;
- }
- return true;
- }
-}
-
-/**
* Instances of the class `Library` represent the data about a single library during the
* resolution of some (possibly different) library. They are not intended to be used except during
* the resolution process.
@@ -11119,11 +11126,6 @@
TypePromotionManager _promoteManager = new TypePromotionManager();
/**
- * Proxy conditional error codes.
- */
- List<ProxyConditionalAnalysisError> _proxyConditionalAnalysisErrors = new List<ProxyConditionalAnalysisError>();
-
- /**
* Initialize a newly created visitor to resolve the nodes in a compilation unit.
*
* @param library the library containing the compilation unit being resolved
@@ -11195,8 +11197,6 @@
*/
TypePromotionManager get promoteManager => _promoteManager;
- List<ProxyConditionalAnalysisError> get proxyConditionalAnalysisErrors => _proxyConditionalAnalysisErrors;
-
@override
Object visitAsExpression(AsExpression node) {
super.visitAsExpression(node);
@@ -11885,43 +11885,6 @@
}
}
- /**
- * Report a conditional analysis error with the given error code and arguments.
- *
- * @param enclosingElement the enclosing element
- * @param errorCode the error code of the error to be reported
- * @param node the node specifying the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportProxyConditionalErrorForNode(Element enclosingElement, ErrorCode errorCode, AstNode node, List<Object> arguments) {
- _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, node.offset, node.length, errorCode, arguments)));
- }
-
- /**
- * Report a conditional analysis error with the given error code and arguments.
- *
- * @param enclosingElement the enclosing element
- * @param errorCode the error code of the error to be reported
- * @param offset the offset of the location of the error
- * @param length the length of the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportProxyConditionalErrorForOffset(Element enclosingElement, ErrorCode errorCode, int offset, int length, List<Object> arguments) {
- _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, offset, length, errorCode, arguments)));
- }
-
- /**
- * Report a conditional analysis error with the given error code and arguments.
- *
- * @param enclosingElement the enclosing element
- * @param errorCode the error code of the error to be reported
- * @param token the token specifying the location of the error
- * @param arguments the arguments to the error, used to compose the error message
- */
- void reportProxyConditionalErrorForToken(Element enclosingElement, ErrorCode errorCode, sc.Token token, List<Object> arguments) {
- _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, token.offset, token.length, errorCode, arguments)));
- }
-
@override
void visitForEachStatementInScope(ForEachStatement node) {
//
@@ -12519,15 +12482,6 @@
return _nameScope;
}
- /**
- * Report an error with the given analysis error.
- *
- * @param errorCode analysis error
- */
- void reportError(AnalysisError analysisError) {
- _errorListener.onError(analysisError);
- }
-
@override
Object visitBlock(Block node) {
Scope outerScope = _nameScope;
@@ -13592,36 +13546,17 @@
}
}
_recordStaticType(node, _typeProvider.listType.substitute4(<DartType> [staticType]));
- NodeList<Expression> elements = node.elements;
- int count = elements.length;
- if (count > 0) {
- DartType propagatedType = elements[0].bestType;
- for (int i = 1; i < count; i++) {
- DartType elementType = elements[i].bestType;
- if (propagatedType != elementType) {
- propagatedType = _dynamicType;
- } else {
- propagatedType = propagatedType.getLeastUpperBound(elementType);
- if (propagatedType == null) {
- propagatedType = _dynamicType;
- }
- }
- }
- if (propagatedType.isMoreSpecificThan(staticType)) {
- _recordPropagatedType(node, _typeProvider.listType.substitute4(<DartType> [propagatedType]));
- }
- }
return null;
}
/**
* The Dart Language Specification, 12.7: <blockquote>The static type of a map literal of the form
- * <i><b>const</b> <String, V> {k<sub>1</sub>:e<sub>1</sub>, …,
- * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i><String, V> {k<sub>1</sub>:e<sub>1</sub>,
- * …, k<sub>n</sub>:e<sub>n</sub>}</i> is `Map<String, V>`. The static type a
- * map literal of the form <i><b>const</b> {k<sub>1</sub>:e<sub>1</sub>, …,
+ * <i><b>const</b> <K, V> {k<sub>1</sub>:e<sub>1</sub>, …,
+ * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i><K, V> {k<sub>1</sub>:e<sub>1</sub>,
+ * …, k<sub>n</sub>:e<sub>n</sub>}</i> is `Map<K, V>`. The static type a map
+ * literal of the form <i><b>const</b> {k<sub>1</sub>:e<sub>1</sub>, …,
* k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>{k<sub>1</sub>:e<sub>1</sub>, …,
- * k<sub>n</sub>:e<sub>n</sub>}</i> is `Map<String, dynamic>`.
+ * k<sub>n</sub>:e<sub>n</sub>}</i> is `Map<dynamic, dynamic>`.
*
* It is a compile-time error if the first type argument to a map literal is not
* <i>String</i>.</blockquote>
@@ -13647,45 +13582,6 @@
}
}
_recordStaticType(node, _typeProvider.mapType.substitute4(<DartType> [staticKeyType, staticValueType]));
- NodeList<MapLiteralEntry> entries = node.entries;
- int count = entries.length;
- if (count > 0) {
- MapLiteralEntry entry = entries[0];
- DartType propagatedKeyType = entry.key.bestType;
- DartType propagatedValueType = entry.value.bestType;
- for (int i = 1; i < count; i++) {
- entry = entries[i];
- DartType elementKeyType = entry.key.bestType;
- if (propagatedKeyType != elementKeyType) {
- propagatedKeyType = _dynamicType;
- } else {
- propagatedKeyType = propagatedKeyType.getLeastUpperBound(elementKeyType);
- if (propagatedKeyType == null) {
- propagatedKeyType = _dynamicType;
- }
- }
- DartType elementValueType = entry.value.bestType;
- if (propagatedValueType != elementValueType) {
- propagatedValueType = _dynamicType;
- } else {
- propagatedValueType = propagatedValueType.getLeastUpperBound(elementValueType);
- if (propagatedValueType == null) {
- propagatedValueType = _dynamicType;
- }
- }
- }
- bool betterKey = propagatedKeyType != null && propagatedKeyType.isMoreSpecificThan(staticKeyType);
- bool betterValue = propagatedValueType != null && propagatedValueType.isMoreSpecificThan(staticValueType);
- if (betterKey || betterValue) {
- if (!betterKey) {
- propagatedKeyType = staticKeyType;
- }
- if (!betterValue) {
- propagatedValueType = staticValueType;
- }
- _recordPropagatedType(node, _typeProvider.mapType.substitute4(<DartType> [propagatedKeyType, propagatedValueType]));
- }
- }
return null;
}
@@ -14994,8 +14890,8 @@
* @param overrides the overrides to be applied
*/
void applyOverrides(Map<Element, DartType> overrides) {
- for (MapIterator<Element, DartType> iter = SingleMapIterator.forMap(overrides); iter.moveNext();) {
- _overridenTypes[iter.key] = iter.value;
+ for (MapEntry<Element, DartType> entry in getMapEntrySet(overrides)) {
+ _overridenTypes[entry.getKey()] = entry.getValue();
}
}
@@ -15588,6 +15484,33 @@
}
@override
+ Object visitAnnotation(Annotation node) {
+ //
+ // Visit annotations, if the annotation is @proxy, on a class, and "proxy" resolves to the proxy
+ // annotation in dart.core, then create create the ElementAnnotationImpl and set it as the
+ // metadata on the enclosing class.
+ //
+ // Element resolution is done in the ElementResolver, and this work will be done in the general
+ // case for all annotations in the ElementResolver. The reason we resolve this particular
+ // element early is so that ClassElement.isProxy() returns the correct information during all
+ // phases of the ElementResolver.
+ //
+ super.visitAnnotation(node);
+ Identifier identifier = node.name;
+ if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && node.parent is ClassDeclaration) {
+ Element element = nameScope.lookup(identifier, definingLibrary);
+ if (element != null && element.library.isDartCore && element is PropertyAccessorElement) {
+ // This is the @proxy from dart.core
+ ClassDeclaration classDeclaration = node.parent as ClassDeclaration;
+ ElementAnnotationImpl elementAnnotation = new ElementAnnotationImpl(element);
+ node.elementAnnotation = elementAnnotation;
+ (classDeclaration.element as ClassElementImpl).metadata = <ElementAnnotationImpl> [elementAnnotation];
+ }
+ }
+ return null;
+ }
+
+ @override
Object visitCatchClause(CatchClause node) {
super.visitCatchClause(node);
SimpleIdentifier exception = node.exceptionParameter;
@@ -15922,7 +15845,7 @@
} else if (_isTypeNameInIsExpression(node)) {
reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, [typeName.name]);
} else if ((redirectingConstructorKind = _getRedirectingConstructorKind(node)) != null) {
- ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS) as ErrorCode;
+ ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS);
reportErrorForNode(errorCode, typeName, [typeName.name]);
} else if (_isTypeNameInTypeArgumentList(node)) {
reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, [typeName.name]);
@@ -15969,7 +15892,7 @@
} else if (_isTypeNameInIsExpression(node)) {
reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, [typeName.name]);
} else if ((redirectingConstructorKind = _getRedirectingConstructorKind(node)) != null) {
- ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS) as ErrorCode;
+ ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS);
reportErrorForNode(errorCode, typeName, [typeName.name]);
} else if (_isTypeNameInTypeArgumentList(node)) {
reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, [typeName.name]);
@@ -17345,8 +17268,8 @@
* @param namespace the namespace containing the names to be added to this namespace
*/
void _addAllFromMap(Map<String, Element> definedNames, Map<String, Element> newNames) {
- for (MapIterator<String, Element> iter = SingleMapIterator.forMap(newNames); iter.moveNext();) {
- definedNames[iter.key] = iter.value;
+ for (MapEntry<String, Element> entry in getMapEntrySet(newNames)) {
+ definedNames[entry.getKey()] = entry.getValue();
}
}
@@ -17428,8 +17351,8 @@
if (prefixElement != null) {
String prefix = prefixElement.name;
Map<String, Element> newNames = new Map<String, Element>();
- for (MapIterator<String, Element> iter = SingleMapIterator.forMap(definedNames); iter.moveNext();) {
- newNames["${prefix}.${iter.key}"] = iter.value;
+ for (MapEntry<String, Element> entry in getMapEntrySet(definedNames)) {
+ newNames["${prefix}.${entry.getKey()}"] = entry.getValue();
}
return newNames;
} else {
@@ -19219,9 +19142,9 @@
}
}
// Visit all of the states in the map to ensure that none were never initialized.
- for (MapIterator<FieldElement, INIT_STATE> iter = SingleMapIterator.forMap(fieldElementsMap); iter.moveNext();) {
- if (iter.value == INIT_STATE.NOT_INIT) {
- FieldElement fieldElement = iter.key;
+ for (MapEntry<FieldElement, INIT_STATE> entry in getMapEntrySet(fieldElementsMap)) {
+ if (entry.getValue() == INIT_STATE.NOT_INIT) {
+ FieldElement fieldElement = entry.getKey();
if (fieldElement.isConst) {
_errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_NOT_INITIALIZED, node.returnType, [fieldElement.name]);
foundError = true;
@@ -19395,21 +19318,22 @@
parameterIndex++;
}
// SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE & SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
- MapIterator<String, DartType> overriddenNamedPTIterator = SingleMapIterator.forMap(overriddenNamedPT);
- while (overriddenNamedPTIterator.moveNext()) {
- DartType overridingType = overridingNamedPT[overriddenNamedPTIterator.key];
+ JavaIterator<MapEntry<String, DartType>> overriddenNamedPTIterator = new JavaIterator(getMapEntrySet(overriddenNamedPT));
+ while (overriddenNamedPTIterator.hasNext) {
+ MapEntry<String, DartType> overriddenNamedPTEntry = overriddenNamedPTIterator.next();
+ DartType overridingType = overridingNamedPT[overriddenNamedPTEntry.getKey()];
if (overridingType == null) {
// Error, this is never reached- INVALID_OVERRIDE_NAMED would have been created above if
// this could be reached.
continue;
}
- if (!overriddenNamedPTIterator.value.isAssignableTo(overridingType)) {
+ if (!overriddenNamedPTEntry.getValue().isAssignableTo(overridingType)) {
// lookup the parameter for the error to select
ParameterElement parameterToSelect = null;
AstNode parameterLocationToSelect = null;
for (int i = 0; i < parameters.length; i++) {
ParameterElement parameter = parameters[i];
- if (parameter.parameterKind == ParameterKind.NAMED && overriddenNamedPTIterator.key == parameter.name) {
+ if (parameter.parameterKind == ParameterKind.NAMED && overriddenNamedPTEntry.getKey() == parameter.name) {
parameterToSelect = parameter;
parameterLocationToSelect = parameterLocations[i];
break;
@@ -19418,7 +19342,7 @@
if (parameterToSelect != null) {
_errorReporter.reportErrorForNode(StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE, parameterLocationToSelect, [
overridingType.displayName,
- overriddenNamedPTIterator.value.displayName,
+ overriddenNamedPTEntry.getValue().displayName,
overriddenExecutable.enclosingElement.displayName]);
return true;
}
@@ -19667,7 +19591,7 @@
if (redirectedConstructor.name != null) {
constructorStrName += ".${redirectedConstructor.name.name}";
}
- ErrorCode errorCode = (node.constKeyword != null ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR) as ErrorCode;
+ ErrorCode errorCode = (node.constKeyword != null ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR);
_errorReporter.reportErrorForNode(errorCode, redirectedConstructor, [constructorStrName, redirectedType.displayName]);
return true;
}
@@ -19756,9 +19680,9 @@
// check exported names
Namespace namespace = new NamespaceBuilder().createExportNamespaceForDirective(exportElement);
Map<String, Element> definedNames = namespace.definedNames;
- for (MapIterator<String, Element> iter = SingleMapIterator.forMap(definedNames); iter.moveNext();) {
- String name = iter.key;
- Element element = iter.value;
+ for (MapEntry<String, Element> definedEntry in getMapEntrySet(definedNames)) {
+ String name = definedEntry.getKey();
+ Element element = definedEntry.getValue();
Element prevElement = _exportedElements[name];
if (element != null && prevElement != null && prevElement != element) {
_errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_EXPORT, node, [
@@ -22816,6 +22740,10 @@
if (!classElement.type.isSubtypeOf(_typeProvider.functionType)) {
return false;
}
+ // If there is a noSuchMethod method, then don't report the warning, see dartbug.com/16078
+ if (classElement.getMethod(ElementResolver.NO_SUCH_METHOD_METHOD_NAME) != null) {
+ return false;
+ }
ExecutableElement callMethod = _inheritanceManager.lookupMember(classElement, "call");
if (callMethod == null || callMethod is! MethodElement || (callMethod as MethodElement).isAbstract) {
_errorReporter.reportErrorForNode(StaticWarningCode.FUNCTION_WITHOUT_CALL, node.name, []);
diff --git a/pkg/analyzer/lib/src/generated/sdk_io.dart b/pkg/analyzer/lib/src/generated/sdk_io.dart
index d09350b..fc67528 100644
--- a/pkg/analyzer/lib/src/generated/sdk_io.dart
+++ b/pkg/analyzer/lib/src/generated/sdk_io.dart
@@ -88,11 +88,6 @@
static String _BIN_DIRECTORY_NAME = "bin";
/**
- * The name of the directory on Mac that contains dartium.
- */
- static String _DARTIUM_DIRECTORY_NAME_MAC = "Chromium.app";
-
- /**
* The name of the directory on non-Mac that contains dartium.
*/
static String _DARTIUM_DIRECTORY_NAME = "chromium";
@@ -232,21 +227,25 @@
DirectoryBasedDartSdk(JavaFile sdkDirectory, [bool useDart2jsPaths = false]) {
this._sdkDirectory = sdkDirectory.getAbsoluteFile();
_libraryMap = initialLibraryMap(useDart2jsPaths);
- _analysisContext = new AnalysisContextImpl();
- _analysisContext.sourceFactory = new SourceFactory([new DartUriResolver(this)]);
- List<String> uris = this.uris;
- ChangeSet changeSet = new ChangeSet();
- for (String uri in uris) {
- changeSet.addedSource(_analysisContext.sourceFactory.forUri(uri));
- }
- _analysisContext.applyChanges(changeSet);
}
@override
Source fromEncoding(UriKind kind, Uri uri) => new FileBasedSource.con2(new JavaFile.fromUri(uri), kind);
@override
- AnalysisContext get context => _analysisContext;
+ AnalysisContext get context {
+ if (_analysisContext == null) {
+ _analysisContext = new AnalysisContextImpl();
+ _analysisContext.sourceFactory = new SourceFactory([new DartUriResolver(this)]);
+ List<String> uris = this.uris;
+ ChangeSet changeSet = new ChangeSet();
+ for (String uri in uris) {
+ changeSet.addedSource(_analysisContext.sourceFactory.forUri(uri));
+ }
+ _analysisContext.applyChanges(changeSet);
+ }
+ return _analysisContext;
+ }
/**
* Return the file containing the dart2js executable, or `null` if it does not exist.
diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart
index 0be26ec..554985d 100644
--- a/pkg/analyzer/lib/src/generated/source_io.dart
+++ b/pkg/analyzer/lib/src/generated/source_io.dart
@@ -168,6 +168,105 @@
}
/**
+ * An implementation of an non-existing [Source].
+ */
+class NonExistingSource implements Source {
+ final String _name;
+
+ final UriKind uriKind;
+
+ NonExistingSource(this._name, this.uriKind);
+
+ @override
+ bool exists() => false;
+
+ @override
+ TimestampedData<String> get contents {
+ throw new UnsupportedOperationException("${_name}does not exist.");
+ }
+
+ @override
+ String get encoding {
+ throw new UnsupportedOperationException("${_name}does not exist.");
+ }
+
+ @override
+ String get fullName => _name;
+
+ @override
+ int get modificationStamp => 0;
+
+ @override
+ String get shortName => _name;
+
+ @override
+ bool get isInSystemLibrary => false;
+
+ @override
+ Source resolveRelative(Uri relativeUri) {
+ throw new UnsupportedOperationException("${_name}does not exist.");
+ }
+}
+
+/**
+ * Instances of the class `RelativeFileUriResolver` resolve `file` URI's.
+ */
+class RelativeFileUriResolver extends UriResolver {
+ /**
+ * The name of the `file` scheme.
+ */
+ static String FILE_SCHEME = "file";
+
+ /**
+ * Return `true` if the given URI is a `file` URI.
+ *
+ * @param uri the URI being tested
+ * @return `true` if the given URI is a `file` URI
+ */
+ static bool isFileUri(Uri uri) => uri.scheme == FILE_SCHEME;
+
+ /**
+ * The directories for the relatvie URI's
+ */
+ final List<JavaFile> _relativeDirectories;
+
+ /**
+ * The root directory for all the source trees
+ */
+ final JavaFile _rootDirectory;
+
+ /**
+ * Initialize a newly created resolver to resolve `file` URI's relative to the given root
+ * directory.
+ */
+ RelativeFileUriResolver(this._rootDirectory, this._relativeDirectories) : super();
+
+ @override
+ Source fromEncoding(UriKind kind, Uri uri) {
+ if (kind == UriKind.FILE_URI) {
+ return new FileBasedSource.con2(new JavaFile.fromUri(uri), kind);
+ }
+ return null;
+ }
+
+ @override
+ Source resolveAbsolute(Uri uri) {
+ String rootPath = _rootDirectory.toURI().path;
+ String uriPath = uri.path;
+ if (uriPath != null && uriPath.startsWith(rootPath)) {
+ String filePath = uri.path.substring(rootPath.length);
+ for (JavaFile dir in _relativeDirectories) {
+ JavaFile file = new JavaFile.relative(dir, filePath);
+ if (file.exists()) {
+ return new FileBasedSource.con2(file, UriKind.FILE_URI);
+ }
+ }
+ }
+ return null;
+ }
+}
+
+/**
* Instances of the class `PackageUriResolver` resolve `package` URI's in the context of
* an application.
*
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index b11960a..b4e0daf 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
name: analyzer
-version: 0.13.5
+version: 0.13.6
author: Dart Team <misc@dartlang.org>
description: Static analyzer for Dart.
homepage: http://www.dartlang.org
diff --git a/pkg/analyzer/test/generated/ast_test.dart b/pkg/analyzer/test/generated/ast_test.dart
index 5a478f4..a01561c 100644
--- a/pkg/analyzer/test/generated/ast_test.dart
+++ b/pkg/analyzer/test/generated/ast_test.dart
@@ -369,7 +369,7 @@
static CatchClause catchClause4(TypeName exceptionType, String exceptionParameter, List<Statement> statements) => catchClause5(exceptionType, exceptionParameter, null, statements);
- static CatchClause catchClause5(TypeName exceptionType, String exceptionParameter, String stackTraceParameter, List<Statement> statements) => new CatchClause(exceptionType == null ? null : TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "on"), exceptionType, exceptionParameter == null ? null : TokenFactory.tokenFromKeyword(Keyword.CATCH), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.OPEN_PAREN), identifier3(exceptionParameter), stackTraceParameter == null ? null : TokenFactory.tokenFromType(TokenType.COMMA), stackTraceParameter == null ? null : identifier3(stackTraceParameter), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), block(statements));
+ static CatchClause catchClause5(TypeName exceptionType, String exceptionParameter, String stackTraceParameter, List<Statement> statements) => new CatchClause(exceptionType == null ? null : TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "on"), exceptionType, exceptionParameter == null ? null : TokenFactory.tokenFromKeyword(Keyword.CATCH), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.OPEN_PAREN), exceptionParameter == null ? null : identifier3(exceptionParameter), stackTraceParameter == null ? null : TokenFactory.tokenFromType(TokenType.COMMA), stackTraceParameter == null ? null : identifier3(stackTraceParameter), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), block(statements));
static ClassDeclaration classDeclaration(Keyword abstractKeyword, String name, TypeParameterList typeParameters, ExtendsClause extendsClause, WithClause withClause, ImplementsClause implementsClause, List<ClassMember> members) => new ClassDeclaration(null, null, abstractKeyword == null ? null : TokenFactory.tokenFromKeyword(abstractKeyword), TokenFactory.tokenFromKeyword(Keyword.CLASS), identifier3(name), typeParameters, extendsClause, withClause, implementsClause, TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), list(members), TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
@@ -2408,10 +2408,14 @@
_assertSource("A this.a", AstFactory.fieldFormalParameter(null, AstFactory.typeName4("A", []), "a"));
}
- void test_visitForEachStatement() {
+ void test_visitForEachStatement_declared() {
_assertSource("for (a in b) {}", AstFactory.forEachStatement(AstFactory.declaredIdentifier3("a"), AstFactory.identifier3("b"), AstFactory.block([])));
}
+ void test_visitForEachStatement_variable() {
+ _assertSource("for (a in b) {}", new ForEachStatement.con2(TokenFactory.tokenFromKeyword(Keyword.FOR), TokenFactory.tokenFromType(TokenType.OPEN_PAREN), AstFactory.identifier3("a"), TokenFactory.tokenFromKeyword(Keyword.IN), AstFactory.identifier3("b"), TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), AstFactory.block([])));
+ }
+
void test_visitFormalParameterList_empty() {
_assertSource("()", AstFactory.formalParameterList([]));
}
@@ -3566,9 +3570,13 @@
final __test = new ToSourceVisitorTest();
runJUnitTest(__test, __test.test_visitFieldFormalParameter_type);
});
- _ut.test('test_visitForEachStatement', () {
+ _ut.test('test_visitForEachStatement_declared', () {
final __test = new ToSourceVisitorTest();
- runJUnitTest(__test, __test.test_visitForEachStatement);
+ runJUnitTest(__test, __test.test_visitForEachStatement_declared);
+ });
+ _ut.test('test_visitForEachStatement_variable', () {
+ final __test = new ToSourceVisitorTest();
+ runJUnitTest(__test, __test.test_visitForEachStatement_variable);
});
_ut.test('test_visitForStatement_c', () {
final __test = new ToSourceVisitorTest();
diff --git a/pkg/analyzer/test/generated/element_test.dart b/pkg/analyzer/test/generated/element_test.dart
index 8f4db1f..a8babb9 100644
--- a/pkg/analyzer/test/generated/element_test.dart
+++ b/pkg/analyzer/test/generated/element_test.dart
@@ -703,6 +703,89 @@
JUnitTestCase.assertEquals(3, InterfaceTypeImpl.computeLongestInheritancePathToObject(classC.type));
}
+ void test_computeSuperinterfaceSet_genericInterfacePath() {
+ //
+ // A
+ // | implements
+ // B<T>
+ // | implements
+ // C<T>
+ //
+ // D
+ //
+ ClassElementImpl classA = ElementFactory.classElement2("A", []);
+ ClassElementImpl classB = ElementFactory.classElement2("B", ["T"]);
+ ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]);
+ ClassElement classD = ElementFactory.classElement2("D", []);
+ InterfaceType typeA = classA.type;
+ classB.interfaces = <InterfaceType> [typeA];
+ InterfaceTypeImpl typeBT = new InterfaceTypeImpl.con1(classB);
+ DartType typeT = classC.type.typeArguments[0];
+ typeBT.typeArguments = <DartType> [typeT];
+ classC.interfaces = <InterfaceType> [typeBT];
+ // A
+ Set<InterfaceType> superinterfacesOfA = InterfaceTypeImpl.computeSuperinterfaceSet(typeA);
+ EngineTestCase.assertSizeOfSet(1, superinterfacesOfA);
+ InterfaceType typeObject = ElementFactory.object.type;
+ JUnitTestCase.assertTrue(superinterfacesOfA.contains(typeObject));
+ // B<D>
+ InterfaceTypeImpl typeBD = new InterfaceTypeImpl.con1(classB);
+ typeBD.typeArguments = <DartType> [classD.type];
+ Set<InterfaceType> superinterfacesOfBD = InterfaceTypeImpl.computeSuperinterfaceSet(typeBD);
+ EngineTestCase.assertSizeOfSet(2, superinterfacesOfBD);
+ JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeObject));
+ JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeA));
+ // C<D>
+ InterfaceTypeImpl typeCD = new InterfaceTypeImpl.con1(classC);
+ typeCD.typeArguments = <DartType> [classD.type];
+ Set<InterfaceType> superinterfacesOfCD = InterfaceTypeImpl.computeSuperinterfaceSet(typeCD);
+ EngineTestCase.assertSizeOfSet(3, superinterfacesOfCD);
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeObject));
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeA));
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeBD));
+ }
+
+ void test_computeSuperinterfaceSet_genericSuperclassPath() {
+ //
+ // A
+ // |
+ // B<T>
+ // |
+ // C<T>
+ //
+ // D
+ //
+ ClassElement classA = ElementFactory.classElement2("A", []);
+ InterfaceType typeA = classA.type;
+ ClassElement classB = ElementFactory.classElement("B", typeA, ["T"]);
+ ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]);
+ InterfaceTypeImpl typeBT = new InterfaceTypeImpl.con1(classB);
+ DartType typeT = classC.type.typeArguments[0];
+ typeBT.typeArguments = <DartType> [typeT];
+ classC.supertype = typeBT;
+ ClassElement classD = ElementFactory.classElement2("D", []);
+ // A
+ Set<InterfaceType> superinterfacesOfA = InterfaceTypeImpl.computeSuperinterfaceSet(typeA);
+ EngineTestCase.assertSizeOfSet(1, superinterfacesOfA);
+ InterfaceType typeObject = ElementFactory.object.type;
+ JUnitTestCase.assertTrue(superinterfacesOfA.contains(typeObject));
+ // B<D>
+ InterfaceTypeImpl typeBD = new InterfaceTypeImpl.con1(classB);
+ typeBD.typeArguments = <DartType> [classD.type];
+ Set<InterfaceType> superinterfacesOfBD = InterfaceTypeImpl.computeSuperinterfaceSet(typeBD);
+ EngineTestCase.assertSizeOfSet(2, superinterfacesOfBD);
+ JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeObject));
+ JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeA));
+ // C<D>
+ InterfaceTypeImpl typeCD = new InterfaceTypeImpl.con1(classC);
+ typeCD.typeArguments = <DartType> [classD.type];
+ Set<InterfaceType> superinterfacesOfCD = InterfaceTypeImpl.computeSuperinterfaceSet(typeCD);
+ EngineTestCase.assertSizeOfSet(3, superinterfacesOfCD);
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeObject));
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeA));
+ JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeBD));
+ }
+
void test_computeSuperinterfaceSet_multipleInterfacePaths() {
ClassElementImpl classA = ElementFactory.classElement2("A", []);
ClassElementImpl classB = ElementFactory.classElement2("B", []);
@@ -1140,7 +1223,7 @@
InterfaceType doubleType = _typeProvider.doubleType;
InterfaceType listOfIntType = listType.substitute4(<DartType> [intType]);
InterfaceType listOfDoubleType = listType.substitute4(<DartType> [doubleType]);
- JUnitTestCase.assertEquals(listType.substitute4(<DartType> [_typeProvider.dynamicType]), listOfIntType.getLeastUpperBound(listOfDoubleType));
+ JUnitTestCase.assertEquals(_typeProvider.objectType, listOfIntType.getLeastUpperBound(listOfDoubleType));
}
void test_getLeastUpperBound_typeParameters_same() {
@@ -1999,6 +2082,14 @@
final __test = new InterfaceTypeImplTest();
runJUnitTest(__test, __test.test_computeLongestInheritancePathToObject_singleSuperclassPath);
});
+ _ut.test('test_computeSuperinterfaceSet_genericInterfacePath', () {
+ final __test = new InterfaceTypeImplTest();
+ runJUnitTest(__test, __test.test_computeSuperinterfaceSet_genericInterfacePath);
+ });
+ _ut.test('test_computeSuperinterfaceSet_genericSuperclassPath', () {
+ final __test = new InterfaceTypeImplTest();
+ runJUnitTest(__test, __test.test_computeSuperinterfaceSet_genericSuperclassPath);
+ });
_ut.test('test_computeSuperinterfaceSet_multipleInterfacePaths', () {
final __test = new InterfaceTypeImplTest();
runJUnitTest(__test, __test.test_computeSuperinterfaceSet_multipleInterfacePaths);
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 3672606..fef7f9d 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -134,6 +134,34 @@
JUnitTestCase.assertTrue(literal.isSynthetic);
}
+ void test_function_literal_allowed_at_toplevel() {
+ ParserTestCase.parseCompilationUnit("var x = () {};", []);
+ }
+
+ void test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = f(() {}); }", []);
+ }
+
+ void test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = x[() {}]; }", []);
+ }
+
+ void test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = [() {}]; }", []);
+ }
+
+ void test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = {'key': () {}}; }", []);
+ }
+
+ void test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = (() {}); }", []);
+ }
+
+ void test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer() {
+ ParserTestCase.parseCompilationUnit("class C { C() : a = \"\${(){}}\"; }", []);
+ }
+
void test_isFunctionDeclaration_nameButNoReturn_block() {
JUnitTestCase.assertTrue(_isFunctionDeclaration("f() {}"));
}
@@ -1138,41 +1166,29 @@
JUnitTestCase.assertNotNull(constructor.body);
}
- void test_parseClassTypeAlias() {
- Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
- ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B;");
- JUnitTestCase.assertNotNull(classTypeAlias.keyword);
- JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
- JUnitTestCase.assertNotNull(classTypeAlias.equals);
- JUnitTestCase.assertNull(classTypeAlias.abstractKeyword);
- JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
- JUnitTestCase.assertNull(classTypeAlias.withClause);
- JUnitTestCase.assertNull(classTypeAlias.implementsClause);
- JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
- }
-
void test_parseClassTypeAlias_abstract() {
- Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
- ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = abstract B;");
+ Token classToken = TokenFactory.tokenFromKeyword(Keyword.CLASS);
+ Token abstractToken = TokenFactory.tokenFromKeyword(Keyword.ABSTRACT);
+ ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), abstractToken, classToken], "A = B with C;");
JUnitTestCase.assertNotNull(classTypeAlias.keyword);
JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
JUnitTestCase.assertNotNull(classTypeAlias.equals);
JUnitTestCase.assertNotNull(classTypeAlias.abstractKeyword);
JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
- JUnitTestCase.assertNull(classTypeAlias.withClause);
+ JUnitTestCase.assertNotNull(classTypeAlias.withClause);
JUnitTestCase.assertNull(classTypeAlias.implementsClause);
JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
}
void test_parseClassTypeAlias_implements() {
Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
- ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B implements C;");
+ ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B with C implements D;");
JUnitTestCase.assertNotNull(classTypeAlias.keyword);
JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
JUnitTestCase.assertNotNull(classTypeAlias.equals);
JUnitTestCase.assertNull(classTypeAlias.abstractKeyword);
JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
- JUnitTestCase.assertNull(classTypeAlias.withClause);
+ JUnitTestCase.assertNotNull(classTypeAlias.withClause);
JUnitTestCase.assertNotNull(classTypeAlias.implementsClause);
JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
}
@@ -1669,7 +1685,7 @@
}
void test_parseCompilationUnitMember_typeAlias_abstract() {
- ClassTypeAlias typeAlias = ParserTestCase.parse("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "class C = abstract S with M;");
+ ClassTypeAlias typeAlias = ParserTestCase.parse("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "abstract class C = S with M;");
JUnitTestCase.assertNotNull(typeAlias.keyword);
JUnitTestCase.assertEquals("C", typeAlias.name.name);
JUnitTestCase.assertNull(typeAlias.typeParameters);
@@ -1801,6 +1817,19 @@
void test_parseConstructor() {
}
+ void test_parseConstructor_with_pseudo_function_literal() {
+ // "(b) {}" should not be misinterpreted as a function literal even though it looks like one.
+ ClassMember classMember = ParserTestCase.parse("parseClassMember", <Object> ["C"], "C() : a = (b) {}");
+ EngineTestCase.assertInstanceOf((obj) => obj is ConstructorDeclaration, ConstructorDeclaration, classMember);
+ ConstructorDeclaration constructor = classMember as ConstructorDeclaration;
+ NodeList<ConstructorInitializer> initializers = constructor.initializers;
+ EngineTestCase.assertSizeOfList(1, initializers);
+ ConstructorInitializer initializer = initializers[0];
+ EngineTestCase.assertInstanceOf((obj) => obj is ConstructorFieldInitializer, ConstructorFieldInitializer, initializer);
+ EngineTestCase.assertInstanceOf((obj) => obj is ParenthesizedExpression, ParenthesizedExpression, (initializer as ConstructorFieldInitializer).expression);
+ EngineTestCase.assertInstanceOf((obj) => obj is BlockFunctionBody, BlockFunctionBody, constructor.body);
+ }
+
void test_parseConstructorFieldInitializer_qualified() {
ConstructorFieldInitializer invocation = ParserTestCase.parse4("parseConstructorFieldInitializer", "this.a = b", []);
JUnitTestCase.assertNotNull(invocation.equals);
@@ -4700,6 +4729,34 @@
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_createSyntheticStringLiteral);
});
+ _ut.test('test_function_literal_allowed_at_toplevel', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_at_toplevel);
+ });
+ _ut.test('test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer);
+ });
+ _ut.test('test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer);
+ });
+ _ut.test('test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer);
+ });
+ _ut.test('test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer);
+ });
+ _ut.test('test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer);
+ });
+ _ut.test('test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer);
+ });
_ut.test('test_isFunctionDeclaration_nameButNoReturn_block', () {
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_isFunctionDeclaration_nameButNoReturn_block);
@@ -5192,10 +5249,6 @@
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_parseClassMember_redirectingFactory_nonConst);
});
- _ut.test('test_parseClassTypeAlias', () {
- final __test = new SimpleParserTest();
- runJUnitTest(__test, __test.test_parseClassTypeAlias);
- });
_ut.test('test_parseClassTypeAlias_abstract', () {
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_parseClassTypeAlias_abstract);
@@ -5528,6 +5581,10 @@
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_parseConstructorName_unnamed_prefixed);
});
+ _ut.test('test_parseConstructor_with_pseudo_function_literal', () {
+ final __test = new SimpleParserTest();
+ runJUnitTest(__test, __test.test_parseConstructor_with_pseudo_function_literal);
+ });
_ut.test('test_parseContinueStatement_label', () {
final __test = new SimpleParserTest();
runJUnitTest(__test, __test.test_parseContinueStatement_label);
@@ -8716,6 +8773,20 @@
JUnitTestCase.assertTrue(syntheticExpression.isSynthetic);
}
+ void test_functionExpression_in_ConstructorFieldInitializer() {
+ CompilationUnit unit = ParserTestCase.parseCompilationUnit("class A { A() : a = (){}; var v; }", [
+ ParserErrorCode.MISSING_IDENTIFIER,
+ ParserErrorCode.UNEXPECTED_TOKEN]);
+ // Make sure we recovered and parsed "var v" correctly
+ ClassDeclaration declaration = unit.declarations[0] as ClassDeclaration;
+ NodeList<ClassMember> members = declaration.members;
+ ClassMember fieldDecl = members[1];
+ EngineTestCase.assertInstanceOf((obj) => obj is FieldDeclaration, FieldDeclaration, fieldDecl);
+ NodeList<VariableDeclaration> vars = (fieldDecl as FieldDeclaration).fields.variables;
+ EngineTestCase.assertSizeOfList(1, vars);
+ JUnitTestCase.assertEquals("v", vars[0].name.name);
+ }
+
void test_incomplete_topLevelVariable() {
CompilationUnit unit = ParserTestCase.parseCompilationUnit("String", [ParserErrorCode.EXPECTED_EXECUTABLE]);
NodeList<CompilationUnitMember> declarations = unit.declarations;
@@ -9161,6 +9232,10 @@
final __test = new RecoveryParserTest();
runJUnitTest(__test, __test.test_expressionList_multiple_start);
});
+ _ut.test('test_functionExpression_in_ConstructorFieldInitializer', () {
+ final __test = new RecoveryParserTest();
+ runJUnitTest(__test, __test.test_functionExpression_in_ConstructorFieldInitializer);
+ });
_ut.test('test_incomplete_topLevelVariable', () {
final __test = new RecoveryParserTest();
runJUnitTest(__test, __test.test_incomplete_topLevelVariable);
@@ -9298,6 +9373,15 @@
}
class IncrementalParserTest extends EngineTestCase {
+ void fail_replace_identifier_with_functionLiteral_in_initializer() {
+ // Function literals aren't allowed inside initializers; incremental parsing needs to gather
+ // the appropriate context.
+ //
+ // "class A { var a; A(b) : a = b ? b : 0 { } }"
+ // "class A { var a; A(b) : a = b ? () {} : 0 { } }"
+ _assertParse("class A { var a; A(b) : a = b ? ", "b", "() {}", " : 0 { } }");
+ }
+
void test_delete_everything() {
// "f() => a + b;"
// ""
@@ -9547,7 +9631,7 @@
// Validate that the results of the incremental parse are the same as the full parse of the
// modified source.
//
- JUnitTestCase.assertTrue(AstComparator.equalUnits(modifiedUnit, incrementalUnit));
+ JUnitTestCase.assertTrue(AstComparator.equalNodes(modifiedUnit, incrementalUnit));
}
static dartSuite() {
@@ -9850,6 +9934,14 @@
ParserTestCase.parseStatement("() {for (; x;) {break;}};", []);
}
+ void test_classTypeAlias_abstractAfterEq() {
+ // This syntax has been removed from the language in favor of "abstract class A = B with C;"
+ // (issue 18098).
+ ParserTestCase.parse3("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "class A = abstract B with C;", [
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_TOKEN]);
+ }
+
void test_constAndFinal() {
ParserTestCase.parse3("parseClassMember", <Object> ["C"], "const final int x;", [ParserErrorCode.CONST_AND_FINAL]);
}
@@ -9927,11 +10019,11 @@
}
void test_deprecatedClassTypeAlias() {
- ParserTestCase.parseCompilationUnit("typedef C = abstract S with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
+ ParserTestCase.parseCompilationUnit("typedef C = S with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
}
void test_deprecatedClassTypeAlias_withGeneric() {
- ParserTestCase.parseCompilationUnit("typedef C<T> = abstract S<T> with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
+ ParserTestCase.parseCompilationUnit("typedef C<T> = S<T> with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
}
void test_directiveAfterDeclaration_classBeforeDirective() {
@@ -10040,7 +10132,7 @@
void test_expectedToken_semicolonAfterClass() {
Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
- ParserTestCase.parse3("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B", [ParserErrorCode.EXPECTED_TOKEN]);
+ ParserTestCase.parse3("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B with C", [ParserErrorCode.EXPECTED_TOKEN]);
}
void test_expectedToken_semicolonMissingAfterExport() {
@@ -10494,6 +10586,10 @@
ParserTestCase.parse4("parseFormalParameterList", "(a, [b], {c})", [ParserErrorCode.MIXED_PARAMETER_GROUPS]);
}
+ void test_mixin_application_lacks_with_clause() {
+ ParserTestCase.parseCompilationUnit("class Foo = Bar;", [ParserErrorCode.EXPECTED_TOKEN]);
+ }
+
void test_multipleExtendsClauses() {
ParserTestCase.parseCompilationUnit("class A extends B extends C {}", [ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES]);
}
@@ -10871,6 +10967,10 @@
final __test = new ErrorParserTest();
runJUnitTest(__test, __test.test_breakOutsideOfLoop_functionExpression_withALoop);
});
+ _ut.test('test_classTypeAlias_abstractAfterEq', () {
+ final __test = new ErrorParserTest();
+ runJUnitTest(__test, __test.test_classTypeAlias_abstractAfterEq);
+ });
_ut.test('test_constAndFinal', () {
final __test = new ErrorParserTest();
runJUnitTest(__test, __test.test_constAndFinal);
@@ -11483,6 +11583,10 @@
final __test = new ErrorParserTest();
runJUnitTest(__test, __test.test_mixedParameterGroups_positionalNamed);
});
+ _ut.test('test_mixin_application_lacks_with_clause', () {
+ final __test = new ErrorParserTest();
+ runJUnitTest(__test, __test.test_mixin_application_lacks_with_clause);
+ });
_ut.test('test_multipleExtendsClauses', () {
final __test = new ErrorParserTest();
runJUnitTest(__test, __test.test_multipleExtendsClauses);
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index b719bc4..73bde01 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -20,7 +20,6 @@
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
-import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
import 'package:unittest/unittest.dart' as _ut;
@@ -658,7 +657,13 @@
BlockFunctionBody body = function.functionExpression.body as BlockFunctionBody;
ReturnStatement statement = body.block.statements[1] as ReturnStatement;
IndexExpression indexExpression = statement.expression as IndexExpression;
- JUnitTestCase.assertSame(typeProvider.intType, indexExpression.propagatedType);
+ JUnitTestCase.assertNull(indexExpression.propagatedType);
+ Expression v = indexExpression.target;
+ InterfaceType propagatedType = v.propagatedType as InterfaceType;
+ JUnitTestCase.assertSame(typeProvider.listType.element, propagatedType.element);
+ List<DartType> typeArguments = propagatedType.typeArguments;
+ EngineTestCase.assertLength(1, typeArguments);
+ JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[0]);
}
void test_mapLiteral_different() {
@@ -701,8 +706,8 @@
JUnitTestCase.assertSame(typeProvider.mapType.element, propagatedType.element);
List<DartType> typeArguments = propagatedType.typeArguments;
EngineTestCase.assertLength(2, typeArguments);
- JUnitTestCase.assertSame(typeProvider.stringType, typeArguments[0]);
- JUnitTestCase.assertSame(typeProvider.intType, typeArguments[1]);
+ JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[0]);
+ JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[1]);
}
void test_propagatedReturnType_function_hasReturnType_returnsNull() {
@@ -1728,7 +1733,8 @@
"class A<E extends num> {",
" A(E x, E y);",
"}",
- "class B<E extends num> = A<E>;",
+ "class M {}",
+ "class B<E extends num> = A<E> with M;",
"void main() {",
" B<int> x = new B<int>(0,0);",
"}"]));
@@ -1932,6 +1938,26 @@
verify([source]);
}
+ void test_functionWithoutCall_doesNotImplementFunction() {
+ Source source = addSource(EngineTestCase.createSource(["class A {}"]));
+ resolve(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
+ void test_functionWithoutCall_withNoSuchMethod() {
+ // 16078
+ Source source = addSource(EngineTestCase.createSource([
+ "class A implements Function {",
+ " noSuchMethod(inv) {",
+ " return 42;",
+ " }",
+ "}"]));
+ resolve(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
void test_implicitThisReferenceInInitializer_constructorName() {
Source source = addSource(EngineTestCase.createSource([
"class A {",
@@ -2808,6 +2834,24 @@
verify([source]);
}
+ void test_invocationOfNonFunction_proxyOnFunctionClass() {
+ // 16078
+ Source source = addSource(EngineTestCase.createSource([
+ "@proxy",
+ "class Functor implements Function {",
+ " noSuchMethod(inv) {",
+ " return 42;",
+ " }",
+ "}",
+ "main() {",
+ " Functor f = new Functor();",
+ " f();",
+ "}"]));
+ resolve(source);
+ assertErrors(source, []);
+ verify([source]);
+ }
+
void test_listElementTypeNotAssignable() {
Source source = addSource(EngineTestCase.createSource(["var v1 = <int> [42];", "var v2 = const <int> [42];"]));
resolve(source);
@@ -3685,6 +3729,19 @@
assertNoErrors(source);
}
+ void test_proxy_annotation_proxyHasPrefixedIdentifier() {
+ Source source = addSource(EngineTestCase.createSource([
+ "library L;",
+ "import 'dart:core' as core;",
+ "@core.proxy class PrefixProxy {}",
+ "main() {",
+ " new PrefixProxy().foo;",
+ " new PrefixProxy().foo();",
+ "}"]));
+ resolve(source);
+ assertNoErrors(source);
+ }
+
void test_proxy_annotation_simple() {
Source source = addSource(EngineTestCase.createSource([
"library L;",
@@ -5078,6 +5135,14 @@
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_functionWithoutCall);
});
+ _ut.test('test_functionWithoutCall_doesNotImplementFunction', () {
+ final __test = new NonErrorResolverTest();
+ runJUnitTest(__test, __test.test_functionWithoutCall_doesNotImplementFunction);
+ });
+ _ut.test('test_functionWithoutCall_withNoSuchMethod', () {
+ final __test = new NonErrorResolverTest();
+ runJUnitTest(__test, __test.test_functionWithoutCall_withNoSuchMethod);
+ });
_ut.test('test_implicitThisReferenceInInitializer_constructorName', () {
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_implicitThisReferenceInInitializer_constructorName);
@@ -5370,6 +5435,10 @@
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_invocationOfNonFunction_localVariable_dynamic2);
});
+ _ut.test('test_invocationOfNonFunction_proxyOnFunctionClass', () {
+ final __test = new NonErrorResolverTest();
+ runJUnitTest(__test, __test.test_invocationOfNonFunction_proxyOnFunctionClass);
+ });
_ut.test('test_listElementTypeNotAssignable', () {
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_listElementTypeNotAssignable);
@@ -5678,6 +5747,10 @@
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_proxy_annotation_prefixed3);
});
+ _ut.test('test_proxy_annotation_proxyHasPrefixedIdentifier', () {
+ final __test = new NonErrorResolverTest();
+ runJUnitTest(__test, __test.test_proxy_annotation_proxyHasPrefixedIdentifier);
+ });
_ut.test('test_proxy_annotation_simple', () {
final __test = new NonErrorResolverTest();
runJUnitTest(__test, __test.test_proxy_annotation_simple);
@@ -6328,6 +6401,19 @@
verify([source]);
}
+ void test_ambiguousImport_function() {
+ Source source = addSource(EngineTestCase.createSource([
+ "import 'lib1.dart';",
+ "import 'lib2.dart';",
+ "g() { return f(); }"]));
+ addNamedSource("/lib1.dart", EngineTestCase.createSource(["library lib1;", "f() {}"]));
+ addNamedSource("/lib2.dart", EngineTestCase.createSource(["library lib2;", "f() {}"]));
+ resolve(source);
+ assertErrors(source, [
+ StaticWarningCode.AMBIGUOUS_IMPORT,
+ StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+ }
+
void test_expectedOneListTypeArgument() {
Source source = addSource(EngineTestCase.createSource(["main() {", " <int, int> [];", "}"]));
resolve(source);
@@ -7177,12 +7263,59 @@
assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
}
+ void test_undefinedFunction() {
+ Source source = addSource(EngineTestCase.createSource(["void f() {", " g();", "}"]));
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+ }
+
+ void test_undefinedFunction_hasImportPrefix() {
+ Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f(); }"]));
+ addNamedSource("/lib.dart", "library lib;");
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+ }
+
+ void test_undefinedFunction_inCatch() {
+ Source source = addSource(EngineTestCase.createSource([
+ "void f() {",
+ " try {",
+ " } on Object {",
+ " g();",
+ " }",
+ "}"]));
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+ }
+
+ void test_undefinedFunction_inImportedLib() {
+ Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f.g(); }"]));
+ addNamedSource("/lib.dart", EngineTestCase.createSource(["library lib;", "h() {}"]));
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+ }
+
void test_undefinedGetter() {
Source source = addSource(EngineTestCase.createSource(["class T {}", "f(T e) { return e.m; }"]));
resolve(source);
assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
}
+ void test_undefinedGetter_proxy_annotation_fakeProxy() {
+ Source source = addSource(EngineTestCase.createSource([
+ "library L;",
+ "class Fake {",
+ " const Fake();",
+ "}",
+ "const proxy = const Fake();",
+ "@proxy class PrefixProxy {}",
+ "main() {",
+ " new PrefixProxy().foo;",
+ "}"]));
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
+ }
+
void test_undefinedGetter_static() {
Source source = addSource(EngineTestCase.createSource(["class A {}", "var a = A.B;"]));
resolve(source);
@@ -7286,6 +7419,21 @@
assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
}
+ void test_undefinedMethod_proxy_annotation_fakeProxy() {
+ Source source = addSource(EngineTestCase.createSource([
+ "library L;",
+ "class Fake {",
+ " const Fake();",
+ "}",
+ "const proxy = const Fake();",
+ "@proxy class PrefixProxy {}",
+ "main() {",
+ " new PrefixProxy().foo();",
+ "}"]));
+ resolve(source);
+ assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
+ }
+
void test_undefinedOperator_indexBoth() {
Source source = addSource(EngineTestCase.createSource(["class A {}", "f(A a) {", " a[0]++;", "}"]));
resolve(source);
@@ -7400,7 +7548,10 @@
}
void test_wrongNumberOfTypeArguments_classAlias() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class B<F extends num> = A<F>;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class B<F extends num> = A<F> with M;"]));
resolve(source);
assertErrors(source, [StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS]);
verify([source]);
@@ -7446,6 +7597,10 @@
static dartSuite() {
_ut.group('StaticTypeWarningCodeTest', () {
+ _ut.test('test_ambiguousImport_function', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_ambiguousImport_function);
+ });
_ut.test('test_expectedOneListTypeArgument', () {
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_expectedOneListTypeArgument);
@@ -7766,10 +7921,30 @@
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_typePromotion_if_with_notMoreSpecific_dynamic);
});
+ _ut.test('test_undefinedFunction', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedFunction);
+ });
+ _ut.test('test_undefinedFunction_hasImportPrefix', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedFunction_hasImportPrefix);
+ });
+ _ut.test('test_undefinedFunction_inCatch', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedFunction_inCatch);
+ });
+ _ut.test('test_undefinedFunction_inImportedLib', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedFunction_inImportedLib);
+ });
_ut.test('test_undefinedGetter', () {
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_undefinedGetter);
});
+ _ut.test('test_undefinedGetter_proxy_annotation_fakeProxy', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedGetter_proxy_annotation_fakeProxy);
+ });
_ut.test('test_undefinedGetter_static', () {
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_undefinedGetter_static);
@@ -7806,6 +7981,10 @@
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_undefinedMethod_private);
});
+ _ut.test('test_undefinedMethod_proxy_annotation_fakeProxy', () {
+ final __test = new StaticTypeWarningCodeTest();
+ runJUnitTest(__test, __test.test_undefinedMethod_proxy_annotation_fakeProxy);
+ });
_ut.test('test_undefinedOperator_indexBoth', () {
final __test = new StaticTypeWarningCodeTest();
runJUnitTest(__test, __test.test_undefinedOperator_indexBoth);
@@ -9804,6 +9983,16 @@
}
/**
+ * In the rare cases we want to group several tests into single "test_" method, so need a way to
+ * reset test instance to reuse it.
+ *
+ * @param options the analysis options for the context
+ */
+ void resetWithOptions(AnalysisOptions options) {
+ _analysisContext = AnalysisContextFactory.contextWithCoreAndOptions(options);
+ }
+
+ /**
* Given a library and all of its parts, resolve the contents of the library and the contents of
* the parts. This assumes that the sources for the library and its parts have already been added
* to the content provider using the method [addNamedSource].
@@ -11204,19 +11393,6 @@
verify([source]);
}
- void test_ambiguousImport_function() {
- Source source = addSource(EngineTestCase.createSource([
- "import 'lib1.dart';",
- "import 'lib2.dart';",
- "g() { return f(); }"]));
- addNamedSource("/lib1.dart", EngineTestCase.createSource(["library lib1;", "f() {}"]));
- addNamedSource("/lib2.dart", EngineTestCase.createSource(["library lib2;", "f() {}"]));
- resolve(source);
- assertErrors(source, [
- StaticWarningCode.AMBIGUOUS_IMPORT,
- CompileTimeErrorCode.UNDEFINED_FUNCTION]);
- }
-
void test_builtInIdentifierAsMixinName_classTypeAlias() {
Source source = addSource(EngineTestCase.createSource(["class A {}", "class B {}", "class as = A with B;"]));
resolve(source);
@@ -12067,42 +12243,42 @@
}
void test_extendsDisallowedClass_classTypeAlias_bool() {
- Source source = addSource(EngineTestCase.createSource(["class C = bool;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = bool with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
void test_extendsDisallowedClass_classTypeAlias_double() {
- Source source = addSource(EngineTestCase.createSource(["class C = double;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = double with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
void test_extendsDisallowedClass_classTypeAlias_int() {
- Source source = addSource(EngineTestCase.createSource(["class C = int;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = int with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
void test_extendsDisallowedClass_classTypeAlias_Null() {
- Source source = addSource(EngineTestCase.createSource(["class C = Null;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = Null with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
void test_extendsDisallowedClass_classTypeAlias_num() {
- Source source = addSource(EngineTestCase.createSource(["class C = num;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = num with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
void test_extendsDisallowedClass_classTypeAlias_String() {
- Source source = addSource(EngineTestCase.createSource(["class C = String;"]));
+ Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = String with M;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
@@ -12369,49 +12545,70 @@
}
void test_implementsDisallowedClass_classTypeAlias_bool() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements bool;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements bool;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_double() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements double;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements double;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_int() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements int;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements int;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_Null() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements Null;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements Null;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_num() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements num;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements num;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_String() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements String;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements String;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
verify([source]);
}
void test_implementsDisallowedClass_classTypeAlias_String_num() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements String, num;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "class C = A with M implements String, num;"]));
resolve(source);
assertErrors(source, [
CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS,
@@ -12434,7 +12631,11 @@
}
void test_implementsNonClass_typeAlias() {
- Source source = addSource(EngineTestCase.createSource(["class A {}", "int B;", "class C = A implements B;"]));
+ Source source = addSource(EngineTestCase.createSource([
+ "class A {}",
+ "class M {}",
+ "int B;",
+ "class C = A with M implements B;"]));
resolve(source);
assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_NON_CLASS]);
verify([source]);
@@ -14209,31 +14410,6 @@
verify([source]);
}
- void test_undefinedFunction() {
- Source source = addSource(EngineTestCase.createSource(["void f() {", " g();", "}"]));
- resolve(source);
- assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
- }
-
- void test_undefinedFunction_hasImportPrefix() {
- Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f(); }"]));
- addNamedSource("/lib.dart", "library lib;");
- resolve(source);
- assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
- }
-
- void test_undefinedFunction_inCatch() {
- Source source = addSource(EngineTestCase.createSource([
- "void f() {",
- " try {",
- " } on Object {",
- " g();",
- " }",
- "}"]));
- resolve(source);
- assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
- }
-
void test_undefinedNamedParameter() {
Source source = addSource(EngineTestCase.createSource([
"class A {",
@@ -14436,10 +14612,6 @@
final __test = new CompileTimeErrorCodeTest();
runJUnitTest(__test, __test.test_ambiguousExport);
});
- _ut.test('test_ambiguousImport_function', () {
- final __test = new CompileTimeErrorCodeTest();
- runJUnitTest(__test, __test.test_ambiguousImport_function);
- });
_ut.test('test_builtInIdentifierAsMixinName_classTypeAlias', () {
final __test = new CompileTimeErrorCodeTest();
runJUnitTest(__test, __test.test_builtInIdentifierAsMixinName_classTypeAlias);
@@ -15728,18 +15900,6 @@
final __test = new CompileTimeErrorCodeTest();
runJUnitTest(__test, __test.test_undefinedConstructorInInitializer_implicit);
});
- _ut.test('test_undefinedFunction', () {
- final __test = new CompileTimeErrorCodeTest();
- runJUnitTest(__test, __test.test_undefinedFunction);
- });
- _ut.test('test_undefinedFunction_hasImportPrefix', () {
- final __test = new CompileTimeErrorCodeTest();
- runJUnitTest(__test, __test.test_undefinedFunction_hasImportPrefix);
- });
- _ut.test('test_undefinedFunction_inCatch', () {
- final __test = new CompileTimeErrorCodeTest();
- runJUnitTest(__test, __test.test_undefinedFunction_inCatch);
- });
_ut.test('test_undefinedNamedParameter', () {
final __test = new CompileTimeErrorCodeTest();
runJUnitTest(__test, __test.test_undefinedNamedParameter);
@@ -16037,10 +16197,9 @@
@override
void setUp() {
- super.setUp();
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.hint = false;
- analysisContext.analysisOptions = options;
+ resetWithOptions(options);
}
void test_assert_is() {
@@ -21278,7 +21437,23 @@
*
* @return the analysis context that was created
*/
- static AnalysisContextImpl contextWithCore() => initContextWithCore(new DelegatingAnalysisContextImpl());
+ static AnalysisContextImpl contextWithCore() {
+ AnalysisContextImpl context = new AnalysisContextImpl_AnalysisContextFactory_contextWithCore();
+ return initContextWithCore(context);
+ }
+
+ /**
+ * Create an analysis context that uses the given options and has a fake core library already
+ * resolved.
+ *
+ * @param options the options to be applied to the context
+ * @return the analysis context that was created
+ */
+ static AnalysisContextImpl contextWithCoreAndOptions(AnalysisOptions options) {
+ AnalysisContextImpl context = new AnalysisContextImpl();
+ context.analysisOptions = options;
+ return initContextWithCore(context);
+ }
/**
* Initialize the given analysis context with a fake core library already resolved.
@@ -21287,16 +21462,19 @@
* @return the analysis context that was created
*/
static AnalysisContextImpl initContextWithCore(AnalysisContextImpl context) {
- AnalysisContext sdkContext = DirectoryBasedDartSdk.defaultSdk.context;
- SourceFactory sourceFactory = sdkContext.sourceFactory;
+ SourceFactory sourceFactory = new SourceFactory([
+ new DartUriResolver(DirectoryBasedDartSdk.defaultSdk),
+ new FileUriResolver()]);
+ context.sourceFactory = sourceFactory;
//
// dart:core
//
TestTypeProvider provider = new TestTypeProvider();
CompilationUnitElementImpl coreUnit = new CompilationUnitElementImpl("core.dart");
Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
- sdkContext.setContents(coreSource, "");
+ context.setContents(coreSource, "");
coreUnit.source = coreSource;
+ ClassElementImpl proxyClassElement = ElementFactory.classElement2("_Proxy", []);
coreUnit.types = <ClassElement> [
provider.boolType.element,
provider.deprecatedType.element,
@@ -21308,12 +21486,13 @@
provider.nullType.element,
provider.numType.element,
provider.objectType.element,
+ proxyClassElement,
provider.stackTraceType.element,
provider.stringType.element,
provider.symbolType.element,
provider.typeType.element];
coreUnit.functions = <FunctionElement> [ElementFactory.functionElement3("identical", provider.boolType.element, <ClassElement> [provider.objectType.element, provider.objectType.element], null)];
- TopLevelVariableElement proxyTopLevelVariableElt = ElementFactory.topLevelVariableElement3("proxy", true, false, ElementFactory.classElement2("_Proxy", []).type);
+ TopLevelVariableElement proxyTopLevelVariableElt = ElementFactory.topLevelVariableElement3("proxy", true, false, proxyClassElement.type);
TopLevelVariableElement deprecatedTopLevelVariableElt = ElementFactory.topLevelVariableElement3("deprecated", true, false, provider.deprecatedType);
coreUnit.accessors = <PropertyAccessorElement> [
proxyTopLevelVariableElt.getter,
@@ -21321,14 +21500,14 @@
deprecatedTopLevelVariableElt.getter,
deprecatedTopLevelVariableElt.setter];
coreUnit.topLevelVariables = <TopLevelVariableElement> [proxyTopLevelVariableElt, deprecatedTopLevelVariableElt];
- LibraryElementImpl coreLibrary = new LibraryElementImpl(sdkContext, AstFactory.libraryIdentifier2(["dart", "core"]));
+ LibraryElementImpl coreLibrary = new LibraryElementImpl(context, AstFactory.libraryIdentifier2(["dart", "core"]));
coreLibrary.definingCompilationUnit = coreUnit;
//
// dart:html
//
CompilationUnitElementImpl htmlUnit = new CompilationUnitElementImpl("html_dartium.dart");
Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML);
- sdkContext.setContents(htmlSource, "");
+ context.setContents(htmlSource, "");
htmlUnit.source = htmlSource;
ClassElementImpl elementElement = ElementFactory.classElement2("Element", []);
InterfaceType elementType = elementElement.type;
@@ -21349,20 +21528,28 @@
TopLevelVariableElementImpl document = ElementFactory.topLevelVariableElement3("document", false, true, htmlDocumentElement.type);
htmlUnit.topLevelVariables = <TopLevelVariableElement> [document];
htmlUnit.accessors = <PropertyAccessorElement> [document.getter];
- LibraryElementImpl htmlLibrary = new LibraryElementImpl(sdkContext, AstFactory.libraryIdentifier2(["dart", "dom", "html"]));
+ LibraryElementImpl htmlLibrary = new LibraryElementImpl(context, AstFactory.libraryIdentifier2(["dart", "dom", "html"]));
htmlLibrary.definingCompilationUnit = htmlUnit;
Map<Source, LibraryElement> elementMap = new Map<Source, LibraryElement>();
elementMap[coreSource] = coreLibrary;
elementMap[htmlSource] = htmlLibrary;
- (sdkContext as AnalysisContextImpl).recordLibraryElements(elementMap);
- sourceFactory = new SourceFactory([
- new DartUriResolver(sdkContext.sourceFactory.dartSdk),
- new FileUriResolver()]);
- context.sourceFactory = sourceFactory;
+ context.recordLibraryElements(elementMap);
return context;
}
}
+class AnalysisContextImpl_AnalysisContextFactory_contextWithCore extends AnalysisContextImpl {
+ @override
+ void set analysisOptions(AnalysisOptions options) {
+ AnalysisOptions currentOptions = analysisOptions;
+ bool needsRecompute = currentOptions.analyzeFunctionBodies != options.analyzeFunctionBodies || currentOptions.generateSdkErrors != options.generateSdkErrors || currentOptions.dart2jsHint != options.dart2jsHint || (currentOptions.hint && !options.hint) || currentOptions.preserveComments != options.preserveComments;
+ if (needsRecompute) {
+ JUnitTestCase.fail("Cannot set options that cause the sources to be reanalyzed in a test context");
+ }
+ super.analysisOptions = options;
+ }
+}
+
class LibraryImportScopeTest extends ResolverTestCase {
void test_conflictingImports() {
AnalysisContext context = new AnalysisContextImpl();
@@ -22786,8 +22973,8 @@
EngineTestCase.assertSizeOfMap(0, namedTypes);
} else {
EngineTestCase.assertSizeOfMap(expectedNamedTypes.length, namedTypes);
- for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(expectedNamedTypes); iter.moveNext();) {
- JUnitTestCase.assertSame(iter.value, namedTypes[iter.key]);
+ for (MapEntry<String, DartType> entry in getMapEntrySet(expectedNamedTypes)) {
+ JUnitTestCase.assertSame(entry.getValue(), namedTypes[entry.getKey()]);
}
}
JUnitTestCase.assertSame(expectedReturnType, functionType.returnType);
@@ -25591,6 +25778,79 @@
}
}
+class ChangeSetTest extends EngineTestCase {
+ void test_changedContent() {
+ TestSource source = new TestSource();
+ String content = "";
+ ChangeSet changeSet = new ChangeSet();
+ changeSet.changedContent(source, content);
+ EngineTestCase.assertSizeOfList(0, changeSet.addedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.changedSources);
+ Map<Source, String> map = changeSet.changedContents;
+ EngineTestCase.assertSizeOfMap(1, map);
+ JUnitTestCase.assertSame(content, map[source]);
+ EngineTestCase.assertSizeOfMap(0, changeSet.changedRanges);
+ EngineTestCase.assertSizeOfList(0, changeSet.deletedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.removedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.removedContainers);
+ }
+
+ void test_changedRange() {
+ TestSource source = new TestSource();
+ String content = "";
+ ChangeSet changeSet = new ChangeSet();
+ changeSet.changedRange(source, content, 1, 2, 3);
+ EngineTestCase.assertSizeOfList(0, changeSet.addedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.changedSources);
+ EngineTestCase.assertSizeOfMap(0, changeSet.changedContents);
+ Map<Source, ChangeSet_ContentChange> map = changeSet.changedRanges;
+ EngineTestCase.assertSizeOfMap(1, map);
+ ChangeSet_ContentChange change = map[source];
+ JUnitTestCase.assertNotNull(change);
+ JUnitTestCase.assertEquals(content, change.contents);
+ JUnitTestCase.assertEquals(1, change.offset);
+ JUnitTestCase.assertEquals(2, change.oldLength);
+ JUnitTestCase.assertEquals(3, change.newLength);
+ EngineTestCase.assertSizeOfList(0, changeSet.deletedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.removedSources);
+ EngineTestCase.assertSizeOfList(0, changeSet.removedContainers);
+ }
+
+ void test_toString() {
+ ChangeSet changeSet = new ChangeSet();
+ changeSet.addedSource(new TestSource());
+ changeSet.changedSource(new TestSource());
+ changeSet.changedContent(new TestSource(), "");
+ changeSet.changedRange(new TestSource(), "", 0, 0, 0);
+ changeSet.deletedSource(new TestSource());
+ changeSet.removedSource(new TestSource());
+ changeSet.removedContainer(new SourceContainer_ChangeSetTest_test_toString());
+ JUnitTestCase.assertNotNull(changeSet.toString());
+ }
+
+ static dartSuite() {
+ _ut.group('ChangeSetTest', () {
+ _ut.test('test_changedContent', () {
+ final __test = new ChangeSetTest();
+ runJUnitTest(__test, __test.test_changedContent);
+ });
+ _ut.test('test_changedRange', () {
+ final __test = new ChangeSetTest();
+ runJUnitTest(__test, __test.test_changedRange);
+ });
+ _ut.test('test_toString', () {
+ final __test = new ChangeSetTest();
+ runJUnitTest(__test, __test.test_toString);
+ });
+ });
+ }
+}
+
+class SourceContainer_ChangeSetTest_test_toString implements SourceContainer {
+ @override
+ bool contains(Source source) => false;
+}
+
class ScopeBuilderTest extends EngineTestCase {
void test_scopeFor_ClassDeclaration() {
GatheringErrorListener listener = new GatheringErrorListener();
@@ -25848,4 +26108,5 @@
// StaticWarningCodeTest.dartSuite();
// StrictModeTest.dartSuite();
// TypePropagationTest.dartSuite();
+// ChangeSetTest.dartSuite();
}
\ No newline at end of file
diff --git a/pkg/analyzer/test/generated/test_support.dart b/pkg/analyzer/test/generated/test_support.dart
index 435d3ce..f3c93be 100644
--- a/pkg/analyzer/test/generated/test_support.dart
+++ b/pkg/analyzer/test/generated/test_support.dart
@@ -10,7 +10,6 @@
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/java_junit.dart';
import 'package:analyzer/src/generated/java_engine.dart';
-import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/scanner.dart';
@@ -143,9 +142,9 @@
//
// Compare the expected and actual number of each type of error.
//
- for (MapIterator<ErrorCode, int> iter = SingleMapIterator.forMap(expectedCounts); iter.moveNext();) {
- ErrorCode code = iter.key;
- int expectedCount = iter.value;
+ for (MapEntry<ErrorCode, int> entry in getMapEntrySet(expectedCounts)) {
+ ErrorCode code = entry.getKey();
+ int expectedCount = entry.getValue();
int actualCount;
List<AnalysisError> list = errorsByCode.remove(code);
if (list == null) {
@@ -169,9 +168,9 @@
//
// Check that there are no more errors in the actual-errors map, otherwise, record message.
//
- for (MapIterator<ErrorCode, List<AnalysisError>> iter = SingleMapIterator.forMap(errorsByCode); iter.moveNext();) {
- ErrorCode code = iter.key;
- List<AnalysisError> actualErrors = iter.value;
+ for (MapEntry<ErrorCode, List<AnalysisError>> entry in getMapEntrySet(errorsByCode)) {
+ ErrorCode code = entry.getKey();
+ List<AnalysisError> actualErrors = entry.getValue();
int actualCount = actualErrors.length;
if (builder.length == 0) {
builder.append("Expected ");
@@ -821,6 +820,8 @@
}
class TestSource implements Source {
+ String _name;
+ TestSource([this._name = '/test.dart']);
int get hashCode => 0;
bool operator ==(Object object) {
return object is TestSource;
@@ -832,10 +833,10 @@
throw new UnsupportedOperationException();
}
String get fullName {
- throw new UnsupportedOperationException();
+ return _name;
}
String get shortName {
- throw new UnsupportedOperationException();
+ return _name;
}
String get encoding {
throw new UnsupportedOperationException();
diff --git a/pkg/analyzer/test/services/formatter_test.dart b/pkg/analyzer/test/services/formatter_test.dart
index e0a982d..d571119 100644
--- a/pkg/analyzer/test/services/formatter_test.dart
+++ b/pkg/analyzer/test/services/formatter_test.dart
@@ -29,11 +29,12 @@
});
/// Data-driven compilation unit tests
- group('cu_tests.data', () {
- runTests('cu_tests.data', (input, expectedOutput) {
- expectCUFormatsTo(input, expectedOutput);
- });
- });
+ /// TODO(scheglov) https://code.google.com/p/dart/issues/detail?id=18315
+// group('cu_tests.data', () {
+// runTests('cu_tests.data', (input, expectedOutput) {
+// expectCUFormatsTo(input, expectedOutput);
+// });
+// });
/// Data-driven Style Guide acceptance tests
group('style_guide_tests.data', () {
diff --git a/pkg/barback/CHANGELOG.md b/pkg/barback/CHANGELOG.md
index c62245e..4a46d38 100644
--- a/pkg/barback/CHANGELOG.md
+++ b/pkg/barback/CHANGELOG.md
@@ -2,6 +2,13 @@
* Only run `Transformer.isPrimary` once for each asset.
+* Don't warn if a lazy or declaring transformer doesn't emit outputs that it has
+ declared. This is valid for transformers like dart2js that need to read their
+ primary input in order to determine whether they should run.
+
+* Fix a deadlock bug when a lazy primary input to a lazy transformer became
+ dirty while the transformer's `apply` method was running.
+
## 0.13.0
* `Transformer.isPrimary` now takes an `AssetId` rather than an `Asset`.
diff --git a/pkg/barback/lib/src/asset_node.dart b/pkg/barback/lib/src/asset_node.dart
index 5e79443..16aff40 100644
--- a/pkg/barback/lib/src/asset_node.dart
+++ b/pkg/barback/lib/src/asset_node.dart
@@ -58,6 +58,11 @@
/// lazy.
Function _lazyCallback;
+ /// Whether this asset's transform is deferred.
+ ///
+ /// See [TransformNode.deferred].
+ bool get deferred => transform != null && transform.deferred;
+
/// A broadcast stream that emits an event whenever the node changes state.
///
/// This stream is synchronous to ensure that when a source asset is modified
@@ -191,9 +196,14 @@
/// Marks the node as [AssetState.DIRTY].
void setDirty() {
assert(node._state != AssetState.REMOVED);
- node._state = AssetState.DIRTY;
node._asset = null;
node._lazyCallback = null;
+
+ // Don't re-emit a dirty event to avoid cases where we try to dispatch an
+ // event while handling another event (e.g. an output is marked lazy, which
+ // causes it to be forced, which causes it to be marked dirty).
+ if (node._state.isDirty) return;
+ node._state = AssetState.DIRTY;
node._stateChangeController.add(AssetState.DIRTY);
}
diff --git a/pkg/barback/lib/src/phase.dart b/pkg/barback/lib/src/phase.dart
index aa7dc78..9f48aa9 100644
--- a/pkg/barback/lib/src/phase.dart
+++ b/pkg/barback/lib/src/phase.dart
@@ -170,8 +170,6 @@
void addInput(AssetNode node) {
if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
- node.force();
-
// Each group is one channel along which an asset may be forwarded, as is
// each transformer.
var forwarder = new PhaseForwarder(
diff --git a/pkg/barback/lib/src/phase_input.dart b/pkg/barback/lib/src/phase_input.dart
index db33779..0f8a365 100644
--- a/pkg/barback/lib/src/phase_input.dart
+++ b/pkg/barback/lib/src/phase_input.dart
@@ -38,6 +38,9 @@
/// The asset node for this input.
AssetNode get input => _inputForwarder.node;
+ /// The subscription to [input]'s [AssetNode.onStateChange] stream.
+ StreamSubscription _inputSubscription;
+
/// A stream that emits an event whenever [this] is no longer dirty.
///
/// This is synchronous in order to guarantee that it will emit an event as
@@ -53,7 +56,8 @@
final _onAssetPool = new StreamPool<AssetNode>.broadcast();
/// Whether [this] is dirty and still has more processing to do.
- bool get isDirty => _transforms.any((transform) => transform.isDirty);
+ bool get isDirty => (input.state.isDirty && !input.deferred) ||
+ _transforms.any((transform) => transform.isDirty);
/// A stream that emits an event whenever any transforms that use [input] as
/// their primary input log an entry.
@@ -62,13 +66,20 @@
PhaseInput(this._phase, AssetNode input, this._location)
: _inputForwarder = new AssetForwarder(input) {
- input.whenRemoved(remove);
+ _inputSubscription = input.onStateChange.listen((state) {
+ if (state.isRemoved) {
+ remove();
+ } else if (state.isAvailable) {
+ if (!isDirty) _onDoneController.add(null);
+ }
+ });
}
/// Removes this input.
///
/// This marks all outputs of the input as removed.
void remove() {
+ _inputSubscription.cancel();
_onDoneController.close();
_onAssetPool.close();
_onLogPool.close();
diff --git a/pkg/barback/lib/src/serialize.dart b/pkg/barback/lib/src/serialize.dart
index 7114039..adc365d 100644
--- a/pkg/barback/lib/src/serialize.dart
+++ b/pkg/barback/lib/src/serialize.dart
@@ -45,10 +45,8 @@
return callbackStream(() {
var receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
- // TODO(nweiz): use a const constructor for StreamTransformer when issue
- // 14971 is fixed.
return receivePort.transform(
- new StreamTransformer(_deserializeTransformer));
+ const StreamTransformer(_deserializeTransformer));
});
}
diff --git a/pkg/barback/lib/src/transform_node.dart b/pkg/barback/lib/src/transform_node.dart
index ecd8d8f..1cd5d71 100644
--- a/pkg/barback/lib/src/transform_node.dart
+++ b/pkg/barback/lib/src/transform_node.dart
@@ -45,10 +45,25 @@
StreamSubscription<AssetNode> _phaseSubscription;
/// Whether [this] is dirty and still has more processing to do.
- bool get isDirty => _state != _State.NOT_PRIMARY && _state != _State.APPLIED;
+ bool get isDirty => _state != _State.NOT_PRIMARY &&
+ _state != _State.APPLIED && _state != _State.DECLARED;
- /// Whether [transformer] is lazy and this transform has yet to be forced.
- bool _isLazy;
+ /// Whether this transform is deferred.
+ ///
+ /// A transform is deferred if either its transformer is lazy or if its
+ /// transformer is declaring and its primary input comes from a deferred
+ /// transformer.
+ final bool deferred;
+
+ /// Whether this is a deferred transform waiting for [force] to be called to
+ /// generate inputs.
+ ///
+ /// This defaults to `true` for deferred transforms and `false` otherwise.
+ /// During or after running `isPrimary` or `declareOutputs`, this may become
+ /// `false`, indicating that the transform has been forced and should generate
+ /// outputs as soon as possible. It will only be set back to `true` if an
+ /// input changes *after* `apply` has completed.
+ bool _awaitingForce;
/// The subscriptions to each input's [AssetNode.onStateChange] stream.
final _inputSubscriptions = new Map<AssetId, StreamSubscription>();
@@ -96,7 +111,7 @@
final _onLogController = new StreamController<LogEntry>.broadcast(sync: true);
/// The current state of [this].
- var _state = _State.COMPUTING_IS_PRIMARY;
+ var _state = _State.DECLARING;
/// Whether [this] has been marked as removed.
bool get _isRemoved => _onAssetController.isClosed;
@@ -105,7 +120,7 @@
/// consumes the primary input.
///
/// Defaults to `false`. This is not meaningful unless [_state] is
- /// [_State.APPLIED].
+ /// [_State.APPLIED] or [_State.DECLARED].
bool _consumePrimary = false;
/// The set of output ids that [transformer] declared it would emit.
@@ -114,22 +129,32 @@
/// [declareOutputs] has been run successfully.
Set<AssetId> _declaredOutputs;
- TransformNode(this.phase, Transformer transformer, this.primary,
+ TransformNode(this.phase, Transformer transformer, AssetNode primary,
this._location)
: transformer = transformer,
- _isLazy = transformer is LazyTransformer {
+ primary = primary,
+ deferred = transformer is LazyTransformer ||
+ (transformer is DeclaringTransformer && primary.deferred) {
+ _awaitingForce = deferred;
+
_onLogPool.add(_onLogController.stream);
_primarySubscription = primary.onStateChange.listen((state) {
if (state.isRemoved) {
remove();
} else {
+ if (state.isDirty && !deferred) primary.force();
+ // If this is deferred but applying, that means it must have been
+ // forced, so we should ensure its input remains forced as well.
+ if (deferred && _state == _State.APPLYING) primary.force();
_dirty();
}
});
_phaseSubscription = phase.previous.onAsset.listen((node) {
- if (_missingInputs.contains(node.id)) _dirty();
+ if (!_missingInputs.contains(node.id)) return;
+ if (!deferred) node.force();
+ _dirty();
});
_isPrimary();
@@ -161,13 +186,12 @@
}
}
- /// If [transformer] is lazy, ensures that its concrete outputs will be
+ /// If [this] is deferred, ensures that its concrete outputs will be
/// generated.
void force() {
- // TODO(nweiz): we might want to have a timeout after which, if the
- // transform's outputs have gone unused, we switch it back to lazy mode.
- if (!_isLazy) return;
- _isLazy = false;
+ if (!_awaitingForce) return;
+ primary.force();
+ _awaitingForce = false;
_dirty();
}
@@ -179,14 +203,34 @@
_emitPassThrough();
return;
}
- if (_state == _State.COMPUTING_IS_PRIMARY || _isLazy) return;
+
+ // If we're in the process of running [isPrimary] or [declareOutputs], we
+ // already know that [apply] needs to be run so there's nothing we need to
+ // mark as dirty.
+ if (_state == _State.DECLARING) return;
+
+ // If we're waiting until [force] is called to run [apply], we don't want to
+ // run [apply] too early.
+ if (_awaitingForce) return;
+
+ if (_state == _State.APPLIED && deferred) {
+ // Transition to DECLARED, indicating that we know what outputs [apply]
+ // will emit but we're waiting to emit them concretely until [force] is
+ // called.
+ _state = _State.DECLARED;
+ _awaitingForce = true;
+ for (var controller in _outputControllers.values) {
+ controller.setLazy(force);
+ }
+ return;
+ }
if (_passThroughController != null) _passThroughController.setDirty();
for (var controller in _outputControllers.values) {
controller.setDirty();
}
- if (_state == _State.APPLIED) {
+ if (_state == _State.APPLIED || _state == _State.DECLARED) {
_apply();
} else {
_state = _State.NEEDS_APPLY;
@@ -210,10 +254,11 @@
}).then((isPrimary) {
if (_isRemoved) return null;
if (isPrimary) {
+ if (!deferred) primary.force();
return _declareOutputs().then((_) {
if (_isRemoved) return;
- if (_isLazy) {
- _state = _State.APPLIED;
+ if (_awaitingForce) {
+ _state = _State.DECLARED;
_onDoneController.add(null);
} else {
_apply();
@@ -253,7 +298,7 @@
if (!_declaredOutputs.contains(primary.id)) _emitPassThrough();
for (var id in _declaredOutputs) {
- var controller = transformer is LazyTransformer
+ var controller = _awaitingForce
? new AssetNodeController.lazy(id, force, this)
: new AssetNodeController(id, this);
_outputControllers[id] = controller;
@@ -267,7 +312,7 @@
/// Applies this transform.
void _apply() {
- assert(!_isRemoved && !_isLazy);
+ assert(!_isRemoved && !_awaitingForce);
// Clear input subscriptions here as well as in [_process] because [_apply]
// may be restarted independently if only a secondary input changes.
@@ -315,7 +360,7 @@
}
_inputSubscriptions.putIfAbsent(node.id, () {
- return node.onStateChange.listen((_) => _dirty());
+ return node.onStateChange.listen((state) => _dirty());
});
return node.asset;
@@ -369,16 +414,6 @@
phase.cascade.reportError(new InvalidOutputException(info, id));
}
- if (_declaredOutputs != null) {
- var missingOutputs = _declaredOutputs.difference(
- newOutputs.map((asset) => asset.id).toSet());
- if (missingOutputs.isNotEmpty) {
- _warn("This transformer didn't emit declared "
- "${pluralize('output asset', missingOutputs.length)} "
- "${toSentence(missingOutputs)}.");
- }
- }
-
// Remove outputs that used to exist but don't anymore.
for (var id in _outputControllers.keys.toList()) {
if (newOutputs.containsId(id)) continue;
@@ -466,12 +501,23 @@
/// The enum of states that [TransformNode] can be in.
class _State {
- /// The transform is running [Transformer.isPrimary].
+ /// The transform is running [Transformer.isPrimary] followed by
+ /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]).
///
- /// This is the initial state of the transformer. Once [Transformer.isPrimary]
- /// finishes running, this will transition to [APPLYING] if the input is
- /// primary, or [NOT_PRIMARY] if it's not.
- static final COMPUTING_IS_PRIMARY = const _State._("computing isPrimary");
+ /// This is the initial state of the transformer, and it will only occur once
+ /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs]
+ /// are independent of the contents of the primary input. Once the two methods
+ /// finish running, this will transition to [NOT_PRIMARY] if the input isn't
+ /// primary, [DECLARED] if the transform is deferred, and [APPLYING]
+ /// otherwise.
+ static final DECLARING = const _State._("computing isPrimary");
+
+ /// The transform is deferred and has run
+ /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced.
+ ///
+ /// This will transition to [APPLYING] when one of the outputs has been
+ /// forced.
+ static final DECLARED = const _State._("declared");
/// The transform is running [Transformer.apply].
///
@@ -490,11 +536,12 @@
/// The transform has finished running [Transformer.apply], whether or not it
/// emitted an error.
///
- /// If the transformer is lazy, the [TransformNode] can also be in this state
- /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has
- /// not.
+ /// If the transformer is deferred, the [TransformNode] can also be in this
+ /// state when [Transformer.declareOutputs] has been run but
+ /// [Transformer.apply] has not.
///
- /// If an input changes, this will transition to [APPLYING].
+ /// If an input changes, this will transition to [DECLARED] if the transform
+ /// is deferred and [APPLYING] otherwise.
static final APPLIED = const _State._("applied");
/// The transform has finished running [Transformer.isPrimary], which returned
diff --git a/pkg/barback/test/package_graph/declaring_transformer_test.dart b/pkg/barback/test/package_graph/declaring_transformer_test.dart
index 94aef14..19ac9ce 100644
--- a/pkg/barback/test/package_graph/declaring_transformer_test.dart
+++ b/pkg/barback/test/package_graph/declaring_transformer_test.dart
@@ -61,18 +61,19 @@
buildShouldSucceed();
});
- test("fails to get a consumed asset before apply is finished", () {
- var transformer = new DeclaringRewriteTransformer("blub", "blab")
- ..consumePrimary = true;
- initGraph(["app|foo.blub"], {"app": [[transformer]]});
-
- transformer.pauseApply();
- updateSources(["app|foo.blub"]);
- expectNoAsset("app|foo.blub");
-
- transformer.resumeApply();
- buildShouldSucceed();
- });
+ // TODO(nweiz): Enable this test when issue 18226 is fixed.
+ // test("fails to get a consumed asset before apply is finished", () {
+ // var transformer = new DeclaringRewriteTransformer("blub", "blab")
+ // ..consumePrimary = true;
+ // initGraph(["app|foo.blub"], {"app": [[transformer]]});
+ //
+ // transformer.pauseApply();
+ // updateSources(["app|foo.blub"]);
+ // expectNoAsset("app|foo.blub");
+ //
+ // transformer.resumeApply();
+ // buildShouldSucceed();
+ // });
test("waits until apply is finished to get an overwritten asset", () {
var transformer = new DeclaringRewriteTransformer("blub", "blub");
diff --git a/pkg/barback/test/package_graph/get_all_assets_test.dart b/pkg/barback/test/package_graph/get_all_assets_test.dart
index 7cadbdf..08da238 100644
--- a/pkg/barback/test/package_graph/get_all_assets_test.dart
+++ b/pkg/barback/test/package_graph/get_all_assets_test.dart
@@ -4,6 +4,9 @@
library barback.test.barback_test;
+import 'dart:async';
+
+import 'package:barback/barback.dart';
import 'package:scheduled_test/scheduled_test.dart';
import '../utils.dart';
@@ -74,4 +77,24 @@
isTransformerException(equals(BadTransformer.ERROR))
]));
});
+
+ // Regression test.
+ test("getAllAssets() is called synchronously after after initializing "
+ "barback", () {
+ var provider = new MockProvider({
+ "app|a.txt": "a",
+ "app|b.txt": "b",
+ "app|c.txt": "c"
+ });
+ var barback = new Barback(provider);
+ barback.updateSources([
+ new AssetId.parse("app|a.txt"),
+ new AssetId.parse("app|b.txt"),
+ new AssetId.parse("app|c.txt")
+ ]);
+
+ expect(barback.getAllAssets().then((assets) {
+ return Future.wait(assets.map((asset) => asset.readAsString()));
+ }), completion(unorderedEquals(["a", "b", "c"])));
+ });
}
diff --git a/pkg/barback/test/package_graph/lazy_transformer_test.dart b/pkg/barback/test/package_graph/lazy_transformer_test.dart
index 2f08772..24ae2e5 100644
--- a/pkg/barback/test/package_graph/lazy_transformer_test.dart
+++ b/pkg/barback/test/package_graph/lazy_transformer_test.dart
@@ -82,29 +82,139 @@
expect(transformer.numRuns, completion(equals(1)));
});
- // test("a lazy asset piped into a declaring transformer isn't eagerly "
- // "compiled", () {
- // var transformer1 = new LazyRewriteTransformer("blub", "blab");
- // var transformer2 = new DeclaringRewriteTransformer("blab", "blib");
- // initGraph(["app|foo.blub"], {"app": [
- // [transformer1], [transformer2]
- // ]});
- // updateSources(["app|foo.blub"]);
- // buildShouldSucceed();
- // expect(transformer1.numRuns, completion(equals(0)));
- // expect(transformer2.numRuns, completion(equals(0)));
- // });
- //
- // test("a lazy asset piped into a declaring transformer is compiled "
- // "on-demand", () {
- // initGraph(["app|foo.blub"], {"app": [
- // [new LazyRewriteTransformer("blub", "blab")],
- // [new DeclaringRewriteTransformer("blab", "blib")]
- // ]});
- // updateSources(["app|foo.blub"]);
- // expectAsset("app|foo.blib", "foo.blab.blib");
- // buildShouldSucceed();
- // });
+ test("a lazy asset piped into a declaring transformer isn't eagerly "
+ "compiled", () {
+ var transformer1 = new LazyRewriteTransformer("blub", "blab");
+ var transformer2 = new DeclaringRewriteTransformer("blab", "blib");
+ initGraph(["app|foo.blub"], {"app": [
+ [transformer1], [transformer2]
+ ]});
+ updateSources(["app|foo.blub"]);
+ buildShouldSucceed();
+ expect(transformer1.numRuns, completion(equals(0)));
+ expect(transformer2.numRuns, completion(equals(0)));
+ });
+
+ test("a lazy asset piped into a declaring transformer is compiled "
+ "on-demand", () {
+ initGraph(["app|foo.blub"], {"app": [
+ [new LazyRewriteTransformer("blub", "blab")],
+ [new DeclaringRewriteTransformer("blab", "blib")]
+ ]});
+ updateSources(["app|foo.blub"]);
+ expectAsset("app|foo.blib", "foo.blab.blib");
+ buildShouldSucceed();
+ });
+
+ test("a lazy asset piped through many declaring transformers isn't eagerly "
+ "compiled", () {
+ var transformer1 = new LazyRewriteTransformer("one", "two");
+ var transformer2 = new DeclaringRewriteTransformer("two", "three");
+ var transformer3 = new DeclaringRewriteTransformer("three", "four");
+ var transformer4 = new DeclaringRewriteTransformer("four", "five");
+ initGraph(["app|foo.one"], {"app": [
+ [transformer1], [transformer2], [transformer3], [transformer4]
+ ]});
+ updateSources(["app|foo.one"]);
+ buildShouldSucceed();
+ expect(transformer1.numRuns, completion(equals(0)));
+ expect(transformer2.numRuns, completion(equals(0)));
+ expect(transformer3.numRuns, completion(equals(0)));
+ expect(transformer4.numRuns, completion(equals(0)));
+ });
+
+ test("a lazy asset piped through many declaring transformers is compiled "
+ "on-demand", () {
+ initGraph(["app|foo.one"], {"app": [
+ [new LazyRewriteTransformer("one", "two")],
+ [new DeclaringRewriteTransformer("two", "three")],
+ [new DeclaringRewriteTransformer("three", "four")],
+ [new DeclaringRewriteTransformer("four", "five")]
+ ]});
+ updateSources(["app|foo.one"]);
+ expectAsset("app|foo.five", "foo.two.three.four.five");
+ buildShouldSucceed();
+ });
+
+ test("a lazy asset piped into a non-lazy transformer that doesn't use its "
+ "outputs isn't eagerly compiled", () {
+ var transformer = new LazyRewriteTransformer("blub", "blab");
+ initGraph(["app|foo.blub"], {"app": [
+ [transformer],
+ [new RewriteTransformer("txt", "out")]
+ ]});
+ updateSources(["app|foo.blub"]);
+ buildShouldSucceed();
+ expect(transformer.numRuns, completion(equals(0)));
+ });
+
+ test("a lazy asset piped into a non-lazy transformer that doesn't use its "
+ "outputs is compiled on-demand", () {
+ initGraph(["app|foo.blub"], {"app": [
+ [new LazyRewriteTransformer("blub", "blab")],
+ [new RewriteTransformer("txt", "out")]
+ ]});
+ updateSources(["app|foo.blub"]);
+ expectAsset("app|foo.blab", "foo.blab");
+ buildShouldSucceed();
+ });
+
+ test("a lazy transformer followed by a non-lazy transformer is re-run "
+ "eagerly", () {
+ var rewrite = new LazyRewriteTransformer("one", "two");
+ initGraph(["app|foo.one"], {"app": [
+ [rewrite],
+ [new RewriteTransformer("two", "three")]
+ ]});
+
+ updateSources(["app|foo.one"]);
+ expectAsset("app|foo.three", "foo.two.three");
+ buildShouldSucceed();
+
+ updateSources(["app|foo.one"]);
+ buildShouldSucceed();
+
+ expect(rewrite.numRuns, completion(equals(2)));
+ });
+
+ test("a lazy transformer followed by a declaring transformer isn't re-run "
+ "eagerly", () {
+ var rewrite = new LazyRewriteTransformer("one", "two");
+ initGraph(["app|foo.one"], {"app": [
+ [rewrite],
+ [new DeclaringRewriteTransformer("two", "three")]
+ ]});
+
+ updateSources(["app|foo.one"]);
+ expectAsset("app|foo.three", "foo.two.three");
+ buildShouldSucceed();
+
+ updateSources(["app|foo.one"]);
+ buildShouldSucceed();
+
+ expect(rewrite.numRuns, completion(equals(1)));
+ });
+
+ test("a declaring transformer added after a materialized lazy transformer "
+ "is still deferred", () {
+ var lazy = new LazyRewriteTransformer("one", "two");
+ var declaring = new DeclaringRewriteTransformer("two", "three");
+ initGraph(["app|foo.one"], {"app": [[lazy]]});
+
+ updateSources(["app|foo.one"]);
+ expectAsset("app|foo.two", "foo.two");
+ buildShouldSucceed();
+
+ updateTransformers("app", [[lazy], [declaring]]);
+ expectAsset("app|foo.three", "foo.two.three");
+ buildShouldSucceed();
+
+ updateSources(["app|foo.one"]);
+ buildShouldSucceed();
+
+ expect(lazy.numRuns, completion(equals(1)));
+ expect(declaring.numRuns, completion(equals(1)));
+ });
test("a lazy asset works as a cross-package input", () {
initGraph({
@@ -134,8 +244,7 @@
buildShouldSucceed();
});
- test("once a lazy transformer is materialized, it runs eagerly afterwards",
- () {
+ test("after being materialized a lazy transformer is still lazy", () {
var transformer = new LazyRewriteTransformer("blub", "blab");
initGraph(["app|foo.blub"], {"app": [[transformer]]});
@@ -149,7 +258,25 @@
updateSources(["app|foo.blub"]);
buildShouldSucceed();
- expect(transformer.numRuns, completion(equals(2)));
+ expect(transformer.numRuns, completion(equals(1)));
+ });
+
+ test("after being materialized a lazy transformer can be materialized again",
+ () {
+ var transformer = new LazyRewriteTransformer("blub", "blab");
+ initGraph(["app|foo.blub"], {"app": [[transformer]]});
+
+ updateSources(["app|foo.blub"]);
+ buildShouldSucceed();
+
+ // Request the asset once to force it to be materialized.
+ expectAsset("app|foo.blab", "foo.blab");
+ buildShouldSucceed();
+
+ modifyAsset("app|foo.blub", "bar");
+ updateSources(["app|foo.blub"]);
+ expectAsset("app|foo.blab", "bar.blab");
+ buildShouldSucceed();
});
test("an error emitted in a lazy transformer's declareOutputs method is "
@@ -227,7 +354,8 @@
buildShouldSucceed();
});
- // Regression test.
+ // Regression tests.
+
test("a lazy transformer that doesn't apply updates its passed-through asset",
() {
initGraph(["app|foo.txt"], {"app": [
@@ -249,4 +377,36 @@
expectAsset("app|foo.txt", "bar");
buildShouldSucceed();
});
+
+ test("a lazy transformer is forced while the previous lazy transformer is "
+ "available, then the previous transformer becomes unavailable", () {
+ var assets = new LazyAssetsTransformer(["app|out.one", "app|out.two"]);
+ var rewrite = new LazyRewriteTransformer("two", "three");
+ initGraph(["app|foo.in"], {"app": [[assets], [rewrite]]});
+
+ updateSources(["app|foo.in"]);
+ // Request out.one so that [assets] runs but the second does not.
+ expectAsset("app|out.one", "app|out.one");
+ buildShouldSucceed();
+
+ // Start the [rewrite] running. The output from [assets] should still be
+ // available.
+ rewrite.pauseApply();
+ expectAssetDoesNotComplete("app|out.three");
+
+ // Mark [assets] as dirty. It should re-run, since [rewrite] still needs its
+ // input.
+ updateSources(["app|foo.in"]);
+ rewrite.resumeApply();
+
+ expectAsset("app|out.three", "app|out.two.three");
+ buildShouldSucceed();
+
+ // [assets] should run once for each time foo.in was updated.
+ expect(assets.numRuns, completion(equals(2)));
+
+ // [rewrite] should run once against [assets]'s original output and once
+ // against its new output.
+ expect(rewrite.numRuns, completion(equals(2)));
+ });
}
diff --git a/pkg/barback/test/transformer/declare_assets.dart b/pkg/barback/test/transformer/declare_assets.dart
index 9a14525..d1158b8 100644
--- a/pkg/barback/test/transformer/declare_assets.dart
+++ b/pkg/barback/test/transformer/declare_assets.dart
@@ -22,9 +22,10 @@
/// These assets' contents will be identical to their ids.
final List<AssetId> emitted;
- DeclareAssetsTransformer(Iterable<String> declared, Iterable<String> emitted)
+ DeclareAssetsTransformer(Iterable<String> declared, [Iterable<String> emitted])
: this.declared = declared.map((id) => new AssetId.parse(id)).toList(),
- this.emitted = emitted.map((id) => new AssetId.parse(id)).toList();
+ this.emitted = (emitted == null ? declared : emitted)
+ .map((id) => new AssetId.parse(id)).toList();
Future<bool> doIsPrimary(AssetId id) => new Future.value(true);
diff --git a/pkg/barback/test/transformer/declaring_rewrite.dart b/pkg/barback/test/transformer/declaring_rewrite.dart
index 063ebbe..3dd3f64 100644
--- a/pkg/barback/test/transformer/declaring_rewrite.dart
+++ b/pkg/barback/test/transformer/declaring_rewrite.dart
@@ -12,7 +12,7 @@
/// Like [RewriteTransformer], but declares its assets ahead of time.
class DeclaringRewriteTransformer extends RewriteTransformer
- implements LazyTransformer {
+ implements DeclaringTransformer {
DeclaringRewriteTransformer(String from, String to)
: super(from, to);
diff --git a/pkg/barback/test/transformer/lazy_assets.dart b/pkg/barback/test/transformer/lazy_assets.dart
new file mode 100644
index 0000000..b245d83
--- /dev/null
+++ b/pkg/barback/test/transformer/lazy_assets.dart
@@ -0,0 +1,18 @@
+// 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.
+
+library barback.test.transformer.lazy_assets;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+import 'declare_assets.dart';
+
+/// Like [DeclareAssetsTransformer], but lazy.
+class LazyAssetsTransformer extends DeclareAssetsTransformer
+ implements LazyTransformer {
+ LazyAssetsTransformer(Iterable<String> declared, [Iterable<String> emitted])
+ : super(declared, emitted);
+}
diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart
index 66c6abf..ee038fc 100644
--- a/pkg/barback/test/utils.dart
+++ b/pkg/barback/test/utils.dart
@@ -28,6 +28,7 @@
export 'transformer/declaring_rewrite.dart';
export 'transformer/emit_nothing.dart';
export 'transformer/has_input.dart';
+export 'transformer/lazy_assets.dart';
export 'transformer/lazy_bad.dart';
export 'transformer/lazy_many_to_one.dart';
export 'transformer/lazy_rewrite.dart';
@@ -78,30 +79,7 @@
if (assets == null) assets = [];
if (transformers == null) transformers = {};
- var assetList;
- if (assets is Map) {
- assetList = assets.keys.map((asset) {
- var id = new AssetId.parse(asset);
- return new _MockAsset(id, assets[asset]);
- });
- } else if (assets is Iterable) {
- assetList = assets.map((asset) {
- var id = new AssetId.parse(asset);
- var contents = pathos.basenameWithoutExtension(id.path);
- return new _MockAsset(id, contents);
- });
- }
-
- var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
- (package, assets) => new AssetSet.from(assets));
-
- // Make sure that packages that have transformers but no assets are considered
- // by MockProvider to exist.
- for (var package in transformers.keys) {
- assetMap.putIfAbsent(package, () => new AssetSet());
- }
-
- _provider = new MockProvider(assetMap);
+ _provider = new MockProvider(assets, additionalPackages: transformers.keys);
_barback = new Barback(_provider);
// Add a dummy listener to the log so it doesn't print to stdout.
_barback.log.listen((_) {});
@@ -484,7 +462,7 @@
class MockProvider implements PackageProvider {
Iterable<String> get packages => _assets.keys;
- Map<String, AssetSet> _assets;
+ final Map<String, AssetSet> _assets;
/// The set of assets for which [MockLoadException]s should be emitted if
/// they're loaded.
@@ -508,13 +486,40 @@
_pauseCompleter = null;
}
- MockProvider(this._assets) {
+ MockProvider(assets, {Iterable<String> additionalPackages})
+ : _assets = _normalizeAssets(assets, additionalPackages);
+
+ static Map<String, AssetSet> _normalizeAssets(assets,
+ Iterable<String> additionalPackages) {
+ var assetList;
+ if (assets is Map) {
+ assetList = assets.keys.map((asset) {
+ var id = new AssetId.parse(asset);
+ return new _MockAsset(id, assets[asset]);
+ });
+ } else if (assets is Iterable) {
+ assetList = assets.map((asset) {
+ var id = new AssetId.parse(asset);
+ var contents = pathos.basenameWithoutExtension(id.path);
+ return new _MockAsset(id, contents);
+ });
+ }
+
+ var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
+ (package, assets) => new AssetSet.from(assets));
+
+ // Make sure that packages that have transformers but no assets are
+ // considered by MockProvider to exist.
+ if (additionalPackages != null) {
+ for (var package in additionalPackages) {
+ assetMap.putIfAbsent(package, () => new AssetSet());
+ }
+ }
+
// If there are no assets or transformers, add a dummy package. This better
// simulates the real world, where there'll always be at least the
// entrypoint package.
- if (_assets.isEmpty) {
- _assets = {"app": new AssetSet()};
- }
+ return assetMap.isEmpty ? {"app": new AssetSet()} : assetMap;
}
void _modifyAsset(String name, String contents) {
diff --git a/pkg/code_transformers/lib/src/entry_point.dart b/pkg/code_transformers/lib/src/entry_point.dart
index ee7868e..ed9eb28 100644
--- a/pkg/code_transformers/lib/src/entry_point.dart
+++ b/pkg/code_transformers/lib/src/entry_point.dart
@@ -7,21 +7,29 @@
import 'package:analyzer/src/generated/ast.dart';
import 'package:barback/barback.dart';
-/// Checks to see if the provided Asset is a Dart entry point.
+/// Checks to see if the provided AssetId is a Dart file in a directory which
+/// may contain entry points.
///
-/// Assets are considered entry points if they are Dart files located in
-/// web/, test/, benchmark/ or example/ and have a main() function.
+/// Directories are considered entry points if they are Dart files located in
+/// web/, test/, benchmark/ or example/.
+bool isPossibleDartEntryId(AssetId id) {
+ if (id.extension != '.dart') return false;
+
+ return ['benchmark', 'example', 'test', 'web']
+ .any((dir) => id.path.startsWith("$dir/"));
+}
+
+/// Checks to see if the provided Asset is possibly a Dart entry point.
+///
+/// Assets are considered entry points if they pass [isPossibleDartEntryId] and
+/// have a main() method.
///
/// Because this only analyzes the primary asset this may return true for files
/// which are not dart entries if the file does not have a main() but does have
/// parts or exports.
Future<bool> isPossibleDartEntry(Asset asset) {
- if (asset.id.extension != '.dart') return new Future.value(false);
+ if (!isPossibleDartEntryId(asset.id)) return new Future.value(false);
- if (!['benchmark', 'example', 'test', 'web']
- .any((dir) => asset.id.path.startsWith("$dir/"))) {
- return new Future.value(false);
- }
return asset.readAsString().then((contents) {
return _couldBeEntrypoint(
analyzer.parseCompilationUnit(contents, suppressErrors: true));
diff --git a/pkg/code_transformers/lib/src/resolver_impl.dart b/pkg/code_transformers/lib/src/resolver_impl.dart
index ad73da4..10e323c 100644
--- a/pkg/code_transformers/lib/src/resolver_impl.dart
+++ b/pkg/code_transformers/lib/src/resolver_impl.dart
@@ -159,8 +159,11 @@
_context.applyChanges(changeSet);
// Force resolve each entry point (the getter will ensure the library is
// computed first).
- _entryLibraries = entryPoints
- .map((id) => _context.computeLibraryElement(sources[id])).toList();
+ _entryLibraries = entryPoints.map((id) {
+ var source = sources[id];
+ if (source == null) return null;
+ return _context.computeLibraryElement(source);
+ }).toList();
});
}
diff --git a/pkg/code_transformers/lib/src/resolvers.dart b/pkg/code_transformers/lib/src/resolvers.dart
index 077ea5b..4b768d4 100644
--- a/pkg/code_transformers/lib/src/resolvers.dart
+++ b/pkg/code_transformers/lib/src/resolvers.dart
@@ -5,8 +5,9 @@
library code_transformers.src.resolvers;
import 'dart:async';
-import 'package:barback/barback.dart' show AssetId, Transformer, Transform;
+import 'package:barback/barback.dart';
+import 'entry_point.dart';
import 'resolver.dart';
import 'resolver_impl.dart';
@@ -47,6 +48,24 @@
/// The cache of resolvers- must be set from subclass.
Resolvers resolvers;
+ /// By default only process prossible entry point assets.
+ ///
+ /// This is only a preliminary check based on the asset ID.
+ Future<bool> isPrimary(assetOrId) {
+ // assetOrId is to handle the transition from Asset to AssetID between
+ // pub 1.3 and 1.4. Once support for 1.3 is dropped this should only
+ // support AssetId.
+ var id = assetOrId is AssetId ? assetOrId : assetOrId.id;
+ return new Future.value(isPossibleDartEntryId(id));
+ }
+
+ /// Check to see if this should apply with the resolver on the provided asset.
+ ///
+ /// By default this will only apply on possible Dart entry points (see
+ /// [isPossibleDartEntry]).
+ Future<bool> shouldApplyResolver(Asset asset) => isPossibleDartEntry(asset);
+
+
/// This provides a default implementation of `Transformer.apply` that will
/// get and release resolvers automatically. Internally this:
/// * Gets a resolver associated with the transform primary input.
@@ -56,7 +75,11 @@
///
/// Use [applyToEntryPoints] instead if you need to override the entry points
/// to run the resolver on.
- Future apply(Transform transform) => applyToEntryPoints(transform);
+ Future apply(Transform transform) =>
+ shouldApplyResolver(transform.primaryInput).then((result) {
+ if (result) return applyToEntryPoints(transform);
+ });
+
/// Helper function to make it easy to write an `Transformer.apply` method
/// that automatically gets and releases the resolver. This is typically used
diff --git a/pkg/code_transformers/pubspec.yaml b/pkg/code_transformers/pubspec.yaml
index 5792858..497ff67 100644
--- a/pkg/code_transformers/pubspec.yaml
+++ b/pkg/code_transformers/pubspec.yaml
@@ -1,5 +1,5 @@
name: code_transformers
-version: 0.1.2-dev
+version: 0.1.3
author: "Dart Team <misc@dartlang.org>"
description: Collection of utilities related to creating barback transformers.
homepage: http://www.dartlang.org
diff --git a/pkg/code_transformers/test/resolver_test.dart b/pkg/code_transformers/test/resolver_test.dart
index 1403b1f..3ab00e7 100644
--- a/pkg/code_transformers/test/resolver_test.dart
+++ b/pkg/code_transformers/test/resolver_test.dart
@@ -27,10 +27,10 @@
group('Resolver', () {
- test('should handle empty files', () {
+ test('should handle initial files', () {
return validateResolver(
inputs: {
- 'a|web/main.dart': '',
+ 'a|web/main.dart': ' main() {}',
},
validator: (resolver) {
var source = resolver.sources[entryPoint];
@@ -38,14 +38,14 @@
var lib = resolver.getLibrary(entryPoint);
expect(lib, isNotNull);
- expect(lib.entryPoint, isNull);
});
});
test('should update when sources change', () {
return validateResolver(
inputs: {
- 'a|web/main.dart': ''' main() {} ''',
+ 'a|web/main.dart': ''' main() {
+ } ''',
},
validator: (resolver) {
var source = resolver.sources[entryPoint];
diff --git a/pkg/custom_element/lib/custom_element.dart b/pkg/custom_element/lib/custom_element.dart
index b0a3b5b..fbf80eb 100644
--- a/pkg/custom_element/lib/custom_element.dart
+++ b/pkg/custom_element/lib/custom_element.dart
@@ -183,6 +183,8 @@
host.children = value;
}
+ String get baseUri => host.baseUri;
+
List<Element> get children => host.children;
set children(List<Element> value) {
diff --git a/pkg/docgen/bin/docgen.dart b/pkg/docgen/bin/docgen.dart
index eba859c..f55a807 100644
--- a/pkg/docgen/bin/docgen.dart
+++ b/pkg/docgen/bin/docgen.dart
@@ -8,6 +8,7 @@
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
+// Must use relative paths because library imports mirrors via relative paths
import '../lib/docgen.dart';
/**
@@ -35,15 +36,15 @@
path.join(options['sdk'], 'bin', 'dart') : 'dart';
var excludedLibraries = options['exclude-lib'];
- if(excludedLibraries == null) excludedLibraries = [];
+ if (excludedLibraries == null) excludedLibraries = [];
+
+ var indentJSON = options['indent-json'] as bool;
docgen(files,
packageRoot: options['package-root'],
- outputToYaml: !options['json'],
includePrivate: options['include-private'],
includeSdk: includeSdk,
parseSdk: options['parse-sdk'],
- append: options['append'] && new Directory(options['out']).existsSync(),
introFileName: introduction,
out: options['out'],
excludeLibraries: excludedLibraries,
@@ -53,7 +54,8 @@
dartBinary: dartBinary,
pubScript: pubScript,
noDocs: options['no-docs'],
- startPage: startPage);
+ startPage: startPage,
+ indentJSON: indentJSON);
}
/**
@@ -102,11 +104,6 @@
callback: (verbose) {
if (verbose) Logger.root.level = Level.FINEST;
});
- parser.addFlag('json', abbr: 'j',
- help: 'Outputs to JSON. If negated, outputs to YAML. '
- 'If --append is used, it takes the file-format of the previous '
- 'run stated in library_list.json, ignoring the flag.',
- negatable: true, defaultsTo: true);
parser.addFlag('include-private',
help: 'Flag to include private declarations.', negatable: false);
parser.addFlag('include-sdk',
@@ -118,9 +115,6 @@
defaultsTo: false, negatable: false);
parser.addOption('package-root',
help: 'Sets the package root of the library being analyzed.');
- parser.addFlag('append',
- help: 'Append to the docs folder, library_list.json and index.txt',
- defaultsTo: false, negatable: false);
parser.addFlag('compile', help: 'Clone the documentation viewer repo locally '
'(if not already present) and compile with dart2js', defaultsTo: false,
negatable: false);
@@ -153,6 +147,9 @@
'of the package in this argument, e.g. --start-page=intl will make '
'the start page of the viewer be the intl package.',
defaultsTo: null);
+ parser.addFlag('indent-json',
+ help: 'Indents each level of JSON output by two spaces',
+ defaultsTo: false, negatable: true);
return parser;
}
diff --git a/pkg/docgen/lib/docgen.dart b/pkg/docgen/lib/docgen.dart
index f56bf69..c23cd7d 100644
--- a/pkg/docgen/lib/docgen.dart
+++ b/pkg/docgen/lib/docgen.dart
@@ -38,23 +38,23 @@
///
/// Returned Future completes with true if document generation is successful.
Future<bool> docgen(List<String> files, {String packageRoot,
- bool outputToYaml: false, bool includePrivate: false,
- bool includeSdk: false, bool parseSdk: false, bool append: false,
+ bool includePrivate: false, bool includeSdk: false, bool parseSdk: false,
String introFileName: '', String out: gen.DEFAULT_OUTPUT_DIRECTORY,
List<String> excludeLibraries: const [],
bool includeDependentPackages: false, bool compile: false,
bool serve: false, bool noDocs: false, String startPage, String pubScript,
- String dartBinary}) {
+ String dartBinary, bool indentJSON: false}) {
var result;
if (!noDocs) {
viewer.ensureMovedViewerCode();
result = gen.generateDocumentation(files, packageRoot: packageRoot,
- outputToYaml: outputToYaml, includePrivate: includePrivate,
- includeSdk: includeSdk, parseSdk: parseSdk, append: append,
+ includePrivate: includePrivate,
+ includeSdk: includeSdk, parseSdk: parseSdk,
introFileName: introFileName, out: out,
excludeLibraries: excludeLibraries,
includeDependentPackages: includeDependentPackages,
- startPage: startPage, pubScript: pubScript, dartBinary: dartBinary);
+ startPage: startPage, pubScript: pubScript, dartBinary: dartBinary,
+ indentJSON: indentJSON);
viewer.addBackViewerCode();
if (compile || serve) {
result.then((success) {
diff --git a/pkg/docgen/lib/src/dart2yaml.dart b/pkg/docgen/lib/src/dart2yaml.dart
deleted file mode 100644
index ac0a8ee..0000000
--- a/pkg/docgen/lib/src/dart2yaml.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * This library is used to convert data from a map to a YAML string.
- */
-library docgen.dart2yaml;
-
-/**
- * Gets a String representing the input Map in YAML format.
- */
-String getYamlString(Map documentData) {
- StringBuffer yaml = new StringBuffer();
- _addLevel(yaml, documentData, 0);
- return yaml.toString();
-}
-
-/**
- * This recursive function builds a YAML string from [documentData] and
- * adds it to [yaml].
- * The [level] input determines the indentation of the block being processed.
- * The [isList] input determines whether [documentData] is a member of an outer
- * lists of maps. A map must be preceeded with a '-' if it is to exist at the
- * same level of indentation in the YAML output as other members of the list.
- */
-void _addLevel(StringBuffer yaml, Map documentData, int level,
- {bool isList: false}) {
- // The order of the keys could be nondeterministic, but it is insufficient
- // to just sort the keys no matter what, as their order could be significant
- // (i.e. parameters to a method). The order of the keys should be enforced
- // by the caller of this function.
- var keys = documentData.keys.toList();
- keys.forEach((key) {
- _calcSpaces(level, yaml);
- // Only the first entry of the map should be preceeded with a '-' since
- // the map is a member of an outer list and the map as a whole must be
- // marked as a single member of that list. See example 2.4 at
- // http://www.yaml.org/spec/1.2/spec.html#id2759963
- if (isList && key == keys.first) {
- yaml.write("- ");
- level++;
- }
- yaml.write("\"$key\" : ");
- if (documentData[key] is Map) {
- yaml.write("\n");
- _addLevel(yaml, documentData[key], level + 1);
- } else if (documentData[key] is List) {
- var elements = documentData[key];
- yaml.write("\n");
- elements.forEach( (element) {
- if (element is Map) {
- _addLevel(yaml, element, level + 1, isList: true);
- } else {
- _calcSpaces(level + 1, yaml);
- yaml.write("- ${_processElement(element)}");
- }
- });
- } else {
- yaml.write(_processElement(documentData[key]));
- }
- });
-}
-
-/**
- * Returns an escaped String form of the inputted element.
- */
-String _processElement(var element) {
- var contents = element.toString()
- .replaceAll('\\', r'\\')
- .replaceAll('"', r'\"')
- .replaceAll('\n', r'\n');
- return '"$contents"\n';
-}
-
-/**
- * Based on the depth in the file, this function returns the correct spacing
- * for an element in the YAML output.
- */
-void _calcSpaces(int spaceLevel, StringBuffer yaml) {
- for (int i = 0; i < spaceLevel; i++) {
- yaml.write(" ");
- }
-}
\ No newline at end of file
diff --git a/pkg/docgen/lib/src/exports/dart2js_mirrors.dart b/pkg/docgen/lib/src/exports/dart2js_mirrors.dart
new file mode 100644
index 0000000..894dd51
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/dart2js_mirrors.dart
@@ -0,0 +1,7 @@
+// 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.
+
+library docgen.exports.dart2js_mirrors;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart';
diff --git a/pkg/docgen/lib/src/exports/libraries.dart b/pkg/docgen/lib/src/exports/libraries.dart
new file mode 100644
index 0000000..00163df
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/libraries.dart
@@ -0,0 +1,7 @@
+// 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.
+
+library docgen.exports.libraries;
+
+export '../../../../../sdk/lib/_internal/libraries.dart';
diff --git a/pkg/docgen/lib/src/exports/mirrors_util.dart b/pkg/docgen/lib/src/exports/mirrors_util.dart
new file mode 100644
index 0000000..2479fbe
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/mirrors_util.dart
@@ -0,0 +1,7 @@
+// 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.
+
+library docgen.exports.mirrors_util;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart';
diff --git a/pkg/docgen/lib/src/exports/source_mirrors.dart b/pkg/docgen/lib/src/exports/source_mirrors.dart
new file mode 100644
index 0000000..7c2ee9c
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/source_mirrors.dart
@@ -0,0 +1,8 @@
+// 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.
+
+library docgen.exports.source_mirrors;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart'
+ hide SourceLocation;
diff --git a/pkg/docgen/lib/src/generator.dart b/pkg/docgen/lib/src/generator.dart
index a77531b..9778b51 100644
--- a/pkg/docgen/lib/src/generator.dart
+++ b/pkg/docgen/lib/src/generator.dart
@@ -5,6 +5,7 @@
library docgen.generator;
import 'dart:async';
+import 'dart:collection';
import 'dart:convert';
import 'dart:io';
@@ -15,15 +16,13 @@
import '../../../../sdk/lib/_internal/compiler/implementation/filenames.dart';
import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/analyze.dart'
as dart2js;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
- as dart2js_mirrors;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
import '../../../../sdk/lib/_internal/compiler/implementation/source_file_provider.dart';
-import '../../../../sdk/lib/_internal/libraries.dart';
-import 'dart2yaml.dart';
+import 'exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import 'exports/libraries.dart';
+import 'exports/mirrors_util.dart' as dart2js_util;
+import 'exports/source_mirrors.dart';
+
import 'io.dart';
import 'library_helpers.dart';
import 'models.dart';
@@ -59,19 +58,19 @@
/// This option is useful when only the SDK libraries are needed.
///
/// Returned Future completes with true if document generation is successful.
-Future<bool> generateDocumentation(List<String> files, {String packageRoot, bool
- outputToYaml: true, bool includePrivate: false, bool includeSdk: false, bool
- parseSdk: false, bool append: false, String introFileName: '', out:
- DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries: const [], bool
- includeDependentPackages: false, String startPage, String dartBinary, String
- pubScript}) {
+Future<bool> generateDocumentation(List<String> files, {String packageRoot,
+ bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false,
+ bool parseSdk: false, String introFileName: '',
+ out: DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries: const [], bool
+ includeDependentPackages: false, String startPage, String dartBinary,
+ String pubScript, bool indentJSON: false}) {
_excluded = excludeLibraries;
_pubScript = pubScript;
_dartBinary = dartBinary;
logger.onRecord.listen((record) => print(record.message));
- _ensureOutputDirectory(out, append);
+ _ensureOutputDirectory(out);
var updatedPackageRoot = _obtainPackageRoot(packageRoot, parseSdk, files);
var requestedLibraries = _findLibrariesToDocument(files,
@@ -103,9 +102,8 @@
librariesToDocument.addAll((includeSdk || parseSdk) ? sdkLibraries : []);
librariesToDocument.removeWhere((x) => _excluded.contains(
dart2js_util.nameOf(x)));
- _documentLibraries(librariesToDocument, includeSdk: includeSdk,
- outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
- introFileName: introFileName, startPage: startPage);
+ _documentLibraries(librariesToDocument, includeSdk, parseSdk, introFileName,
+ startPage, indentJSON);
return true;
});
}
@@ -128,32 +126,20 @@
sdkRoot = path.normalize(path.absolute(path.join(root, 'dart-sdk')));
}
logger.info('SDK Root: ${sdkRoot}');
- return analyzeLibraries(libraries, sdkRoot,
- packageRoot: packageRoot);
+ return analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot);
}
/// Writes [text] to a file in the output directory.
-void _writeToFile(String text, String filename, {bool append: false}) {
+void _writeToFile(String text, String filename) {
if (text == null) return;
- Directory dir = new Directory(_outputDirectory);
- if (!dir.existsSync()) {
- dir.createSync();
- }
- if (path.split(filename).length > 1) {
- var splitList = path.split(filename);
- for (int i = 0; i < splitList.length; i++) {
- var level = splitList[i];
- }
- for (var level in path.split(filename)) {
- var subdir = new Directory(path.join(_outputDirectory, path.dirname(
- filename)));
- if (!subdir.existsSync()) {
- subdir.createSync();
- }
- }
- }
- File file = new File(path.join(_outputDirectory, filename));
- file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE);
+
+ var filePath = path.join(_outputDirectory, filename);
+
+ var parentDir = new Directory(path.dirname(filePath));
+ if (!parentDir.existsSync()) parentDir.createSync(recursive: true);
+
+ new File(filePath)
+ .writeAsStringSync(text, mode: FileMode.WRITE);
}
/// Resolve all the links in the introductory comments for a given library or
@@ -171,10 +157,24 @@
inlineSyntaxes: MARKDOWN_SYNTAXES);
}
+int _indexableComparer(Indexable a, Indexable b) {
+ if (a is Library && b is Library) {
+ var compare = a.packageName.compareTo(b.packageName);
+ if (compare == 0) {
+ compare = a.name.compareTo(b.name);
+ }
+ return compare;
+ }
+
+ if (a is Library) return -1;
+ if (b is Library) return 1;
+
+ return a.qualifiedName.compareTo(b.qualifiedName);
+}
+
/// Creates documentation for filtered libraries.
-void _documentLibraries(List<LibraryMirror> libs, {bool includeSdk: false, bool
- outputToYaml: true, bool append: false, bool parseSdk: false, String
- introFileName: '', String startPage}) {
+void _documentLibraries(List<LibraryMirror> libs, bool includeSdk,
+ bool parseSdk, String introFileName, String startPage, bool indentJson) {
libs.forEach((lib) {
// Files belonging to the SDK have a uri that begins with 'dart:'.
if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
@@ -182,101 +182,76 @@
}
});
- var filteredEntities = new Set<Indexable>();
- for (Map<String, Set<Indexable>> firstLevel in mirrorToDocgen.values) {
- for (Set<Indexable> items in firstLevel.values) {
- for (Indexable item in items) {
- if (isFullChainVisible(item)) {
- if (item is! Method ||
- (item is Method && item.methodInheritedFrom == null)) {
- filteredEntities.add(item);
- }
- }
+ var filteredEntities = new SplayTreeSet<Indexable>(_indexableComparer);
+ for (Indexable item in allIndexables) {
+ if (isFullChainVisible(item)) {
+ if (item is! Method ||
+ (item is Method && item.methodInheritedFrom == null)) {
+ filteredEntities.add(item);
}
}
}
// Outputs a JSON file with all libraries and their preview comments.
// This will help the viewer know what libraries are available to read in.
- Map<String, dynamic> libraryMap;
-
- if (append) {
- var docsDir = listDir(_outputDirectory);
- if (!docsDir.contains('$_outputDirectory/library_list.json')) {
- throw new StateError('No library_list.json');
- }
- libraryMap = JSON.decode(new File('$_outputDirectory/library_list.json'
- ).readAsStringSync());
- libraryMap['libraries'].addAll(filteredEntities.where((e) => e is Library
- ).map((e) => e.previewMap));
- var intro = libraryMap['introduction'];
- var spacing = intro.isEmpty ? '' : '<br/><br/>';
- libraryMap['introduction'] =
- "$intro$spacing${_readIntroductionFile(introFileName, includeSdk)}";
- outputToYaml = libraryMap['filetype'] == 'yaml';
- } else {
- libraryMap = {
+ Map<String, dynamic> libraryMap = {
'libraries': filteredEntities.where((e) => e is Library).map((e) =>
e.previewMap).toList(),
'introduction': _readIntroductionFile(introFileName, includeSdk),
- 'filetype': outputToYaml ? 'yaml' : 'json'
+ 'filetype': 'json'
};
- }
- _writeOutputFiles(libraryMap, filteredEntities, outputToYaml, append,
- startPage);
+
+ var encoder = new JsonEncoder.withIndent(indentJson ? ' ' : null);
+
+ _writeOutputFiles(libraryMap, filteredEntities, startPage, encoder);
}
-/// Output all of the libraries and classes into json or yaml files for
-/// consumption by a viewer.
+/// Output all of the libraries and classes into json files for consumption by a
+/// viewer.
void _writeOutputFiles(Map<String, dynamic> libraryMap, Iterable<Indexable>
- filteredEntities, bool outputToYaml, bool append, String startPage) {
+ filteredEntities, String startPage, JsonEncoder encoder) {
if (startPage != null) libraryMap['start-page'] = startPage;
- _writeToFile(JSON.encode(libraryMap), 'library_list.json');
+ _writeToFile(encoder.convert(libraryMap), 'library_list.json');
// Output libraries and classes to file after all information is generated.
filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
- _writeIndexableToFile(output, outputToYaml);
+ _writeIndexableToFile(output, encoder);
});
// Outputs all the qualified names documented with their type.
// This will help generate search results.
- var sortedEntities = filteredEntities.map((e) =>
- '${e.qualifiedName} ${e.typeName}').toList()..sort();
+ var sortedEntities = filteredEntities
+ .map((e) => '${e.qualifiedName} ${e.typeName}')
+ .toList();
- _writeToFile(sortedEntities.join('\n') + '\n', 'index.txt', append: append);
- var index = new Map.fromIterables(filteredEntities.map((e) => e.qualifiedName
- ), filteredEntities.map((e) => e.typeName));
- if (append) {
- var previousIndex = JSON.decode(new File('$_outputDirectory/index.json'
- ).readAsStringSync());
- index.addAll(previousIndex);
- }
- _writeToFile(JSON.encode(index), 'index.json');
+ sortedEntities.sort();
+
+ var buffer = new StringBuffer()
+ ..writeAll(sortedEntities, '\n')
+ ..write('\n');
+
+ _writeToFile(buffer.toString(), 'index.txt');
+
+ var index = new SplayTreeMap.fromIterable(filteredEntities,
+ key: (e) => e.qualifiedName, value: (e) => e.typeName);
+
+ _writeToFile(encoder.convert(index), 'index.json');
}
/// Helper method to serialize the given Indexable out to a file.
-void _writeIndexableToFile(Indexable result, bool outputToYaml) {
- var outputFile = result.fileName;
- var output;
- if (outputToYaml) {
- output = getYamlString(result.toMap());
- outputFile = outputFile + '.yaml';
- } else {
- output = JSON.encode(result.toMap());
- outputFile = outputFile + '.json';
- }
+void _writeIndexableToFile(Indexable result, JsonEncoder encoder) {
+ var outputFile = result.qualifiedName + '.json';
+ var output = encoder.convert(result.toMap());
_writeToFile(output, outputFile);
}
/// Set the location of the ouput directory, and ensure that the location is
/// available on the file system.
-void _ensureOutputDirectory(String outputDirectory, bool append) {
+void _ensureOutputDirectory(String outputDirectory) {
_outputDirectory = outputDirectory;
- if (!append) {
- var dir = new Directory(_outputDirectory);
- if (dir.existsSync()) dir.deleteSync(recursive: true);
- }
+ var dir = new Directory(_outputDirectory);
+ if (dir.existsSync()) dir.deleteSync(recursive: true);
}
/// Analyzes set of libraries and provides a mirror system which can be used
@@ -309,8 +284,8 @@
///
/// If packageRoot is not explicitly passed, we examine the files we're
/// documenting to attempt to find a package root.
-String _obtainPackageRoot(String packageRoot, bool parseSdk, List<String> files)
- {
+String _obtainPackageRoot(String packageRoot, bool parseSdk,
+ List<String> files) {
if (packageRoot == null && !parseSdk) {
var type = FileSystemEntity.typeSync(files.first);
if (type == FileSystemEntityType.DIRECTORY) {
@@ -438,7 +413,6 @@
logger.fine('Generated library for ${result.name}');
}
-
/// If we can't find the SDK introduction text, which will happen if running
/// from a snapshot and using --parse-sdk or --include-sdk, then use this
/// hard-coded version. This should be updated to be consistent with the text
diff --git a/pkg/docgen/lib/src/library_helpers.dart b/pkg/docgen/lib/src/library_helpers.dart
index 3bb5672..05d6f4b 100644
--- a/pkg/docgen/lib/src/library_helpers.dart
+++ b/pkg/docgen/lib/src/library_helpers.dart
@@ -7,11 +7,12 @@
import 'package:logging/logging.dart';
import 'package:markdown/markdown.dart' as markdown;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
+import 'exports/source_mirrors.dart';
+import 'exports/mirrors_util.dart' as dart2js_util;
-import 'models.dart';
+import 'models/indexable.dart';
+import 'models/library.dart';
+import 'models/dummy_mirror.dart';
typedef DeclarationMirror LookupFunction(DeclarationSourceMirror declaration,
String name);
@@ -40,7 +41,7 @@
/// Return true if this item and all of its owners are all visible.
bool isFullChainVisible(Indexable item) {
return includePrivateMembers || (!item.isPrivate && (item.owner != null ?
- isFullChainVisible(item.owner) : true));
+ isFullChainVisible(item.owner) : true));
}
/// Logger for printing out progress of documentation generation.
@@ -55,15 +56,6 @@
Iterable<LibraryMirror> get sdkLibraries => _sdkLibraries;
Iterable<LibraryMirror> _sdkLibraries;
-/// Index of all the dart2js mirrors examined to corresponding MirrorBased
-/// docgen objects.
-///
-/// Used for lookup because of the dart2js mirrors exports
-/// issue. The second level map is indexed by owner docName for faster lookup.
-/// Why two levels of lookup? Speed, man. Speed.
-final Map<String, Map<String, Set<Indexable>>> mirrorToDocgen = new Map<String,
- Map<String, Set<Indexable>>>();
-
////// Top level resolution functions
/// Converts all [foo] references in comments to <a>libraryName.foo</a>.
markdown.Node globalFixReference(String name) {
@@ -150,8 +142,8 @@
/// [DummyMirror] that simply returns the original mirror's qualifiedName
/// while behaving like a MirrorBased object.
Indexable getDocgenObject(DeclarationMirror mirror, [Indexable owner]) {
- Map<String, Set<Indexable>> docgenObj =
- mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)];
+ Map<String, Indexable> docgenObj = lookupIndexableMap(mirror);
+
if (docgenObj == null) {
return new DummyMirror(mirror, owner);
}
@@ -159,14 +151,12 @@
var setToExamine = new Set();
if (owner != null) {
var firstSet = docgenObj[owner.docName];
- if (firstSet != null) setToExamine.addAll(firstSet);
+ if (firstSet != null) setToExamine.add(firstSet);
if (_coreLibrary != null && docgenObj[_coreLibrary.docName] != null) {
- setToExamine.addAll(docgenObj[_coreLibrary.docName]);
+ setToExamine.add(docgenObj[_coreLibrary.docName]);
}
} else {
- for (var value in docgenObj.values) {
- setToExamine.addAll(value);
- }
+ setToExamine.addAll(docgenObj.values);
}
Set<Indexable> results = new Set<Indexable>();
diff --git a/pkg/docgen/lib/src/mdn.dart b/pkg/docgen/lib/src/mdn.dart
index 073dbdd..f5fd69b 100644
--- a/pkg/docgen/lib/src/mdn.dart
+++ b/pkg/docgen/lib/src/mdn.dart
@@ -17,24 +17,24 @@
/// Generates MDN comments from database.json.
String mdnComment(String root, Logger logger, String domName) {
- //Check if MDN is loaded.
- if (_mdn == null) {
- // Reading in MDN related json file.
- var mdnPath = p.join(root, 'utils/apidoc/mdn/database.json');
- var mdnFile = new File(mdnPath);
- if (mdnFile.existsSync()) {
- _mdn = JSON.decode(mdnFile.readAsStringSync());
- } else {
- logger.warning("Cannot find MDN docs expected at $mdnPath");
- _mdn = {};
- }
- }
+ //Check if MDN is loaded.
+ if (_mdn == null) {
+ // Reading in MDN related json file.
+ var mdnPath = p.join(root, 'utils/apidoc/mdn/database.json');
+ var mdnFile = new File(mdnPath);
+ if (mdnFile.existsSync()) {
+ _mdn = JSON.decode(mdnFile.readAsStringSync());
+ } else {
+ logger.warning("Cannot find MDN docs expected at $mdnPath");
+ _mdn = {};
+ }
+ }
- var parts = domName.split('.');
- if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
- if (parts.length == 1) return _mdnTypeComment(parts[0]);
+ var parts = domName.split('.');
+ if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
+ if (parts.length == 1) return _mdnTypeComment(parts[0]);
- throw new StateError('More than two items is not supported: $parts');
+ throw new StateError('More than two items is not supported: $parts');
}
/// Generates the MDN Comment for variables and method DOM elements.
diff --git a/pkg/docgen/lib/src/models.dart b/pkg/docgen/lib/src/models.dart
index 2e59a61..ad61145 100644
--- a/pkg/docgen/lib/src/models.dart
+++ b/pkg/docgen/lib/src/models.dart
@@ -4,1070 +4,10 @@
library docgen.models;
-import 'dart:io';
-
-import 'package:markdown/markdown.dart' as markdown;
-
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
- as dart2js_mirrors;
-
-import 'library_helpers.dart';
-import 'mdn.dart';
-import 'model_helpers.dart';
-import 'package_helpers.dart';
-
-/// Docgen representation of an item to be documented, that wraps around a
-/// dart2js mirror.
-abstract class MirrorBased<TMirror extends DeclarationMirror> {
- /// The original dart2js mirror around which this object wraps.
- TMirror get mirror;
-
- /// Return an informative [Object.toString] for debugging.
- String toString() => "${super.toString()} - $mirror";
-}
-
-/// A Docgen wrapper around the dart2js mirror for a generic type.
-class Generic extends MirrorBased<TypeVariableMirror> {
- final TypeVariableMirror mirror;
-
- Generic(this.mirror);
-
- Map toMap() => {
- 'name': dart2js_util.nameOf(mirror),
- 'type': dart2js_util.qualifiedNameOf(mirror.upperBound)
- };
-}
-
-/// For types that we do not explicitly create or have not yet created in our
-/// entity map (like core types).
-class DummyMirror implements Indexable {
- final DeclarationMirror mirror;
- /// The library that contains this element, if any. Used as a hint to help
- /// determine which object we're referring to when looking up this mirror in
- /// our map.
- final Indexable owner;
- DummyMirror(this.mirror, [this.owner]);
-
- String get docName {
- if (mirror == null) return '';
- if (mirror is LibraryMirror) {
- return dart2js_util.qualifiedNameOf(mirror).replaceAll('.','-');
- }
- var mirrorOwner = mirror.owner;
- if (mirrorOwner == null) return dart2js_util.qualifiedNameOf(mirror);
- var simpleName = dart2js_util.nameOf(mirror);
- if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
- // We name constructors specially -- repeating the class name and a
- // "-" to separate the constructor from its name (if any).
- simpleName = '${dart2js_util.nameOf(mirrorOwner)}-$simpleName';
- }
- return getDocgenObject(mirrorOwner, owner).docName + '.' +
- simpleName;
- }
-
- bool get isPrivate => mirror == null? false : mirror.isPrivate;
-
- String get packageName {
- var libMirror = _getOwningLibraryFromMirror(mirror);
- if (libMirror != null) {
- return getPackageName(libMirror);
- }
- return '';
- }
-
- String get packagePrefix => packageName == null || packageName.isEmpty ?
- '' : '$packageName/';
-
- LibraryMirror _getOwningLibraryFromMirror(DeclarationMirror mirror) {
- if (mirror is LibraryMirror) return mirror;
- if (mirror == null) return null;
- return _getOwningLibraryFromMirror(mirror.owner);
- }
-
- noSuchMethod(Invocation invocation) {
- throw new UnimplementedError(invocation.memberName.toString());
- }
-}
-
-/// An item that is categorized in our mirrorToDocgen map, as a distinct,
-/// searchable element.
-///
-/// These are items that refer to concrete entities (a Class, for example,
-/// but not a Type, which is a "pointer" to a class) that we wish to be
-/// globally resolvable. This includes things such as class methods and
-/// variables, but parameters for methods are not "Indexable" as we do not want
-/// the user to be able to search for a method based on its parameter names!
-/// The set of indexable items also includes Typedefs, since the user can refer
-/// to them as concrete entities in a particular scope.
-abstract class Indexable<TMirror extends DeclarationMirror>
- extends MirrorBased<TMirror> {
-
- Library get owningLibrary => owner.owningLibrary;
-
- String get qualifiedName => fileName;
- final TMirror mirror;
- final bool isPrivate;
- /// The comment text pre-resolution. We keep this around because inherited
- /// methods need to resolve links differently from the superclass.
- String _unresolvedComment = '';
-
- Indexable(TMirror mirror)
- : this.mirror = mirror,
- this.isPrivate = isHidden(mirror) {
-
- var map = mirrorToDocgen[dart2js_util.qualifiedNameOf(this.mirror)];
- if (map == null) map = new Map<String, Set<Indexable>>();
-
- var set = map[owner.docName];
- if (set == null) set = new Set<Indexable>();
- set.add(this);
- map[owner.docName] = set;
- mirrorToDocgen[dart2js_util.qualifiedNameOf(this.mirror)] = map;
- }
-
- /// Returns this object's qualified name, but following the conventions
- /// we're using in Dartdoc, which is that library names with dots in them
- /// have them replaced with hyphens.
- String get docName;
-
- /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
- markdown.Node fixReference(String name) {
- // Attempt the look up the whole name up in the scope.
- String elementName = findElementInScope(name);
- if (elementName != null) {
- return new markdown.Element.text('a', elementName);
- }
- return fixComplexReference(name);
- }
-
- /// Look for the specified name starting with the current member, and
- /// progressively working outward to the current library scope.
- String findElementInScope(String name) =>
- findElementInScopeWithPrefix(name, packagePrefix);
-
- /// The reference to this element based on where it is printed as a
- /// documentation file and also the unique URL to refer to this item.
- ///
- /// The qualified name (for URL purposes) and the file name are the same,
- /// of the form packageName/ClassName or packageName/ClassName.methodName.
- /// This defines both the URL and the directory structure.
- String get fileName => packagePrefix + ownerPrefix + name;
-
- /// The full docName of the owner element, appended with a '.' for this
- /// object's name to be appended.
- String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
-
- /// The prefix String to refer to the package that this item is in, for URLs
- /// and comment resolution.
- ///
- /// The prefix can be prepended to a qualified name to get a fully unique
- /// name among all packages.
- String get packagePrefix => '';
-
- /// Documentation comment with converted markdown and all links resolved.
- String _comment;
-
- /// Accessor to documentation comment with markdown converted to html and all
- /// links resolved.
- String get comment {
- if (_comment != null) return _comment;
-
- _comment = _commentToHtml();
- if (_comment.isEmpty) {
- _comment = _mdnComment();
- }
- return _comment;
- }
-
- void set comment(x) {
- _comment = x;
- }
-
- /// The simple name to refer to this item.
- String get name => dart2js_util.nameOf(mirror);
-
- /// Accessor to the parent item that owns this item.
- ///
- /// "Owning" is defined as the object one scope-level above which this item
- /// is defined. Ex: The owner for a top level class, would be its enclosing
- /// library. The owner of a local variable in a method would be the enclosing
- /// method.
- Indexable get owner => new DummyMirror(mirror.owner);
-
- /// Generates MDN comments from database.json.
- String _mdnComment();
-
- /// The type of this member to be used in index.txt.
- String get typeName => '';
-
- /// Creates a [Map] with this [Indexable]'s name and a preview comment.
- Map get previewMap {
- var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName };
- var preview = _preview;
- if(preview != null) finalMap['preview'] = preview;
- return finalMap;
- }
-
- String get _preview {
- if (comment != '') {
- var index = comment.indexOf('</p>');
- return index > 0 ?
- '${comment.substring(0, index)}</p>' :
- '<p><i>Comment preview not available</i></p>';
- }
- return null;
- }
-
- /// Accessor to obtain the raw comment text for a given item, _without_ any
- /// of the links resolved.
- String get _commentText {
- String commentText;
- mirror.metadata.forEach((metadata) {
- if (metadata is CommentInstanceMirror) {
- CommentInstanceMirror comment = metadata;
- if (comment.isDocComment) {
- if (commentText == null) {
- commentText = comment.trimmedText;
- } else {
- commentText = '$commentText\n${comment.trimmedText}';
- }
- }
- }
- });
- return commentText;
- }
-
- /// Returns any documentation comments associated with a mirror with
- /// simple markdown converted to html.
- ///
- /// By default we resolve any comment references within our own scope.
- /// However, if a method is inherited, we want the inherited comments, but
- /// links to the subclasses's version of the methods.
- String _commentToHtml([Indexable resolvingScope]) {
- if (resolvingScope == null) resolvingScope = this;
- var commentText = _commentText;
- _unresolvedComment = commentText;
-
- var linkResolver = (name) => resolvingScope.fixReference(name);
- commentText = commentText == null ? '' :
- markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver,
- inlineSyntaxes: MARKDOWN_SYNTAXES);
- return commentText;
- }
-
- /// Return a map representation of this type.
- Map toMap();
-
- /// Expand the method map [mapToExpand] into a more detailed map that
- /// separates out setters, getters, constructors, operators, and methods.
- Map _expandMethodMap(Map<String, Method> mapToExpand) => {
- 'setters': recurseMap(filterMap(mapToExpand,
- (key, val) => val.mirror.isSetter)),
- 'getters': recurseMap(filterMap(mapToExpand,
- (key, val) => val.mirror.isGetter)),
- 'constructors': recurseMap(filterMap(mapToExpand,
- (key, val) => val.mirror.isConstructor)),
- 'operators': recurseMap(filterMap(mapToExpand,
- (key, val) => val.mirror.isOperator)),
- 'methods': recurseMap(filterMap(mapToExpand,
- (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
- };
-
- /// Accessor to determine if this item and all of its owners are visible.
- bool get isVisible => isFullChainVisible(this);
-
- /// Returns true if [mirror] is the correct type of mirror that this Docgen
- /// object wraps. (Workaround for the fact that Types are not first class.)
- bool isValidMirror(DeclarationMirror mirror);
-}
-
-/// A class containing contents of a Dart library.
-class Library extends Indexable {
- final Map<String, Class> classes = {};
- final Map<String, Typedef> typedefs = {};
- final Map<String, Class> errors = {};
-
- /// Top-level variables in the library.
- Map<String, Variable> variables;
-
- /// Top-level functions in the library.
- Map<String, Method> functions;
-
- String packageName = '';
- bool _hasBeenCheckedForPackage = false;
- String packageIntro;
-
- Library get owningLibrary => this;
-
- /// Returns the [Library] for the given [mirror] if it has already been
- /// created, else creates it.
- factory Library(LibraryMirror mirror) {
- var library = getDocgenObject(mirror);
- if (library is DummyMirror) {
- library = new Library._(mirror);
- }
- return library;
- }
-
- Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
- var exported = calcExportedItems(libraryMirror);
- var exportedClasses = addAll(exported['classes'],
- dart2js_util.typesOf(libraryMirror.declarations));
- updateLibraryPackage(mirror);
- exportedClasses.forEach((String mirrorName, TypeMirror mirror) {
- if (mirror is TypedefMirror) {
- // This is actually a Dart2jsTypedefMirror, and it does define value,
- // but we don't have visibility to that type.
- if (includePrivateMembers || !mirror.isPrivate) {
- typedefs[dart2js_util.nameOf(mirror)] = new Typedef(mirror, this);
- }
- } else if (mirror is ClassMirror) {
- var clazz = new Class(mirror, this);
-
- if (clazz.isError()) {
- errors[dart2js_util.nameOf(mirror)] = clazz;
- } else {
- classes[dart2js_util.nameOf(mirror)] = clazz;
- }
- } else {
- throw new ArgumentError(
- '${dart2js_util.nameOf(mirror)} - no class type match. ');
- }
- });
- this.functions = createMethods(addAll(exported['methods'],
- libraryMirror.declarations.values.where(
- (mirror) => mirror is MethodMirror)).values, this);
- this.variables = createVariables(addAll(exported['variables'],
- dart2js_util.variablesOf(libraryMirror.declarations)).values, this);
- }
-
- /// Look for the specified name starting with the current member, and
- /// progressively working outward to the current library scope.
- String findElementInScope(String name) {
- var lookupFunc = determineLookupFunc(name);
- var libraryScope = lookupFunc(mirror, name);
- if (libraryScope != null) {
- var result = getDocgenObject(libraryScope, this);
- if (result is DummyMirror) return packagePrefix + result.docName;
- return result.packagePrefix + result.docName;
- }
- return super.findElementInScope(name);
- }
-
- String _mdnComment() => '';
-
- /// For a library's [mirror], determine the name of the package (if any) we
- /// believe it came from (because of its file URI).
- ///
- /// If no package could be determined, we return an empty string.
- void updateLibraryPackage(LibraryMirror mirror) {
- if (mirror == null) return;
- if (_hasBeenCheckedForPackage) return;
- _hasBeenCheckedForPackage = true;
- if (mirror.uri.scheme != 'file') return;
- packageName = getPackageName(mirror);
- // Associate the package readme with all the libraries. This is a bit
- // wasteful, but easier than trying to figure out which partial match
- // is best.
- packageIntro = _packageIntro(getPackageDirectory(mirror));
- }
-
- String _packageIntro(packageDir) {
- if (packageDir == null) return null;
- var dir = new Directory(packageDir);
- var files = dir.listSync();
- var readmes = files.where((FileSystemEntity each) => (each is File &&
- each.path.substring(packageDir.length + 1, each.path.length)
- .startsWith('README'))).toList();
- if (readmes.isEmpty) return '';
- // If there are multiples, pick the shortest name.
- readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
- var readme = readmes.first;
- var linkResolver = (name) => globalFixReference(name);
- var contents = markdown.markdownToHtml(readme
- .readAsStringSync(), linkResolver: linkResolver,
- inlineSyntaxes: MARKDOWN_SYNTAXES);
- return contents;
- }
-
- String get packagePrefix => packageName == null || packageName.isEmpty ?
- '' : '$packageName/';
-
- Map get previewMap {
- var basic = super.previewMap;
- basic['packageName'] = packageName;
- if (packageIntro != null) {
- basic['packageIntro'] = packageIntro;
- }
- return basic;
- }
-
- String get name => docName;
-
- String get docName {
- return dart2js_util.qualifiedNameOf(mirror).replaceAll('.','-');
- }
-
- /// Checks if the given name is a key for any of the Class Maps.
- bool containsKey(String name) =>
- classes.containsKey(name) || errors.containsKey(name);
-
- /// Generates a map describing the [Library] object.
- Map toMap() => {
- 'name': name,
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'variables': recurseMap(variables),
- 'functions': _expandMethodMap(functions),
- 'classes': {
- 'class': classes.values.where((c) => c.isVisible)
- .map((e) => e.previewMap).toList(),
- 'typedef': recurseMap(typedefs),
- 'error': errors.values.where((e) => e.isVisible)
- .map((e) => e.previewMap).toList()
- },
- 'packageName': packageName,
- 'packageIntro' : packageIntro
- };
-
- String get typeName => 'library';
-
- bool isValidMirror(DeclarationMirror mirror) => mirror is LibraryMirror;
-}
-
-abstract class OwnedIndexable<TMirror extends DeclarationMirror>
- extends Indexable<TMirror> {
- /// List of the meta annotations on this item.
- final List<Annotation> annotations;
-
- /// The object one scope-level above which this item is defined.
- ///
- /// Ex: The owner for a top level class, would be its enclosing library.
- /// The owner of a local variable in a method would be the enclosing method.
- final Indexable owner;
-
- /// Returns this object's qualified name, but following the conventions
- /// we're using in Dartdoc, which is that library names with dots in them
- /// have them replaced with hyphens.
- String get docName => owner.docName + '.' + dart2js_util.nameOf(mirror);
-
- OwnedIndexable(DeclarationMirror mirror, Indexable owner)
- : annotations = createAnnotations(mirror, owner.owningLibrary),
- this.owner = owner,
- super(mirror);
-
- /// Generates MDN comments from database.json.
- String _mdnComment() {
- var domAnnotation = this.annotations.firstWhere(
- (e) => e.mirror.qualifiedName == #metadata.DomName,
- orElse: () => null);
- if (domAnnotation == null) return '';
- var domName = domAnnotation.parameters.single;
-
- return mdnComment(rootDirectory, logger, domName);
- }
-
- String get packagePrefix => owner.packagePrefix;
-}
-
-/// A class containing contents of a Dart class.
-class Class
- extends OwnedIndexable<dart2js_mirrors.Dart2JsInterfaceTypeMirror>
- implements Comparable<Class> {
-
- /// List of the names of interfaces that this class implements.
- List<Class> interfaces = [];
-
- /// Names of classes that extends or implements this class.
- Set<Class> subclasses = new Set<Class>();
-
- /// Top-level variables in the class.
- Map<String, Variable> variables;
-
- /// Inherited variables in the class.
- final Map<String, Variable> inheritedVariables = {};
-
- /// Methods in the class.
- Map<String, Method> methods;
-
- final Map<String, Method> inheritedMethods = new Map<String, Method>();
-
- /// Generic infomation about the class.
- final Map<String, Generic> generics;
-
- Class superclass;
- bool get isAbstract => mirror.isAbstract;
-
- /// Make sure that we don't check for inherited comments more than once.
- bool _commentsEnsured = false;
-
- /// Returns the [Class] for the given [mirror] if it has already been created,
- /// else creates it.
- factory Class(ClassMirror mirror, Library owner) {
- var clazz = getDocgenObject(mirror, owner);
- if (clazz is DummyMirror) {
- clazz = new Class._(mirror, owner);
- }
- return clazz;
- }
-
- /// Called when we are constructing a superclass or interface class, but it
- /// is not known if it belongs to the same owner as the original class. In
- /// this case, we create an object whose owner is what the original mirror
- /// says it is.
- factory Class._possiblyDifferentOwner(ClassMirror mirror,
- Library originalOwner) {
- if (mirror.owner is LibraryMirror) {
- var realOwner = getDocgenObject(mirror.owner);
- if (realOwner is Library) {
- return new Class(mirror, realOwner);
- } else {
- return new Class(mirror, originalOwner);
- }
- } else {
- return new Class(mirror, originalOwner);
- }
- }
-
- Class._(ClassSourceMirror classMirror, Indexable owner)
- : generics = createGenerics(classMirror),
- super(classMirror, owner) {
-
- // The reason we do this madness is the superclass and interface owners may
- // not be this class's owner!! Example: BaseClient in http pkg.
- var superinterfaces = classMirror.superinterfaces.map(
- (interface) => new Class._possiblyDifferentOwner(interface, owner));
- this.superclass = classMirror.superclass == null? null :
- new Class._possiblyDifferentOwner(classMirror.superclass, owner);
-
- interfaces = superinterfaces.toList();
- variables = createVariables(
- dart2js_util.variablesOf(classMirror.declarations), this);
- methods = createMethods(classMirror.declarations.values.where(
- (mirror) => mirror is MethodMirror), this);
-
- // Tell superclass that you are a subclass, unless you are not
- // visible or an intermediary mixin class.
- if (!classMirror.isNameSynthetic && isVisible && superclass != null) {
- superclass.addSubclass(this);
- }
-
- if (this.superclass != null) addInherited(superclass);
- interfaces.forEach((interface) => addInherited(interface));
- }
-
- String _lookupInClassAndSuperclasses(String name) {
- var lookupFunc = determineLookupFunc(name);
- var classScope = this;
- while (classScope != null) {
- var classFunc = lookupFunc(classScope.mirror, name);
- if (classFunc != null) {
- return packagePrefix + getDocgenObject(classFunc, owner).docName;
- }
- classScope = classScope.superclass;
- }
- return null;
- }
-
- /// Look for the specified name starting with the current member, and
- /// progressively working outward to the current library scope.
- String findElementInScope(String name) {
- var lookupFunc = determineLookupFunc(name);
- var result = _lookupInClassAndSuperclasses(name);
- if (result != null) {
- return result;
- }
- result = owner.findElementInScope(name);
- return result == null ? super.findElementInScope(name) : result;
- }
-
- String get typeName => 'class';
-
- /// Add all inherited variables and methods from the provided superclass.
- /// If [_includePrivate] is true, it also adds the variables and methods from
- /// the superclass.
- void addInherited(Class superclass) {
- inheritedVariables.addAll(superclass.inheritedVariables);
- inheritedVariables.addAll(_allButStatics(superclass.variables));
- addInheritedMethod(superclass, this);
- }
-
- /** [newParent] refers to the actual class is currently using these methods.
- * which may be different because with the mirror system, we only point to the
- * original canonical superclasse's method.
- */
- void addInheritedMethod(Class parent, Class newParent) {
- parent.inheritedMethods.forEach((name, method) {
- if(!method.mirror.isConstructor){
- inheritedMethods[name] = new Method(method.mirror, newParent, method);
- }}
- );
- _allButStatics(parent.methods).forEach((name, method) {
- if (!method.mirror.isConstructor) {
- inheritedMethods[name] = new Method(method.mirror, newParent, method);
- }}
- );
- }
-
- /// Remove statics from the map of inherited items before adding them.
- Map _allButStatics(Map items) {
- var result = {};
- items.forEach((name, item) {
- if (!item.isStatic) {
- result[name] = item;
- }
- });
- return result;
- }
-
- /// Add the subclass to the class.
- ///
- /// If [this] is private (or an intermediary mixin class), it will add the
- /// subclass to the list of subclasses in the superclasses.
- void addSubclass(Class subclass) {
- if (docName == 'dart-core.Object') return;
-
- if (!includePrivateMembers && isPrivate || mirror.isNameSynthetic) {
- if (superclass != null) superclass.addSubclass(subclass);
- interfaces.forEach((interface) {
- interface.addSubclass(subclass);
- });
- } else {
- subclasses.add(subclass);
- }
- }
-
- /// Check if this [Class] is an error or exception.
- bool isError() {
- if (qualifiedName == 'dart-core.Error' ||
- qualifiedName == 'dart-core.Exception')
- return true;
- for (var interface in interfaces) {
- if (interface.isError()) return true;
- }
- if (superclass == null) return false;
- return superclass.isError();
- }
-
- /// Makes sure that all methods with inherited equivalents have comments.
- void ensureComments() {
- if (_commentsEnsured) return;
- _commentsEnsured = true;
- if (superclass != null) superclass.ensureComments();
- inheritedMethods.forEach((qualifiedName, inheritedMethod) {
- var method = methods[qualifiedName];
- if (method != null) {
- // if we have overwritten this method in this class, we still provide
- // the opportunity to inherit the comments.
- method.ensureCommentFor(inheritedMethod);
- }
- });
- // we need to populate the comments for all methods. so that the subclasses
- // can get for their inherited versions the comments.
- methods.forEach((qualifiedName, method) {
- if (!method.mirror.isConstructor) method.ensureCommentFor(method);
- });
- }
-
- /// If a class extends a private superclass, find the closest public
- /// superclass of the private superclass.
- String validSuperclass() {
- if (superclass == null) return 'dart-core.Object';
- if (superclass.isVisible) return superclass.qualifiedName;
- return superclass.validSuperclass();
- }
-
- /// Generates a map describing the [Class] object.
- Map toMap() => {
- 'name': name,
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'isAbstract' : isAbstract,
- 'superclass': validSuperclass(),
- 'implements': interfaces.where((i) => i.isVisible)
- .map((e) => e.qualifiedName).toList(),
- 'subclass': (subclasses.toList()..sort())
- .map((x) => x.qualifiedName).toList(),
- 'variables': recurseMap(variables),
- 'inheritedVariables': recurseMap(inheritedVariables),
- 'methods': _expandMethodMap(methods),
- 'inheritedMethods': _expandMethodMap(inheritedMethods),
- 'annotations': annotations.map((a) => a.toMap()).toList(),
- 'generics': recurseMap(generics)
- };
-
- int compareTo(Class other) => name.compareTo(other.name);
-
- bool isValidMirror(DeclarationMirror mirror) => mirror is ClassMirror;
-}
-
-class Typedef extends OwnedIndexable {
- final String returnType;
-
- final Map<String, Parameter> parameters;
-
- /// Generic information about the typedef.
- final Map<String, Generic> generics;
-
- /// Returns the [Library] for the given [mirror] if it has already been
- /// created, else creates it.
- factory Typedef(TypedefMirror mirror, Library owningLibrary) {
- var aTypedef = getDocgenObject(mirror, owningLibrary);
- if (aTypedef is DummyMirror) {
- aTypedef = new Typedef._(mirror, owningLibrary);
- }
- return aTypedef;
- }
-
- Typedef._(TypedefMirror mirror, Library owningLibrary)
- : returnType = getDocgenObject(mirror.referent.returnType).docName,
- generics = createGenerics(mirror),
- parameters = createParameters(mirror.referent.parameters,
- owningLibrary),
- super(mirror, owningLibrary);
-
- Map toMap() {
- var map = {
- 'name': name,
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'return': returnType,
- 'parameters': recurseMap(parameters),
- 'annotations': annotations.map((a) => a.toMap()).toList(),
- 'generics': recurseMap(generics)
- };
-
- // Typedef is displayed on the library page as a class, so a preview is
- // added manually
- var preview = _preview;
- if(preview != null) map['preview'] = preview;
-
- return map;
- }
-
- markdown.Node fixReference(String name) => null;
-
- String get typeName => 'typedef';
-
- bool isValidMirror(DeclarationMirror mirror) => mirror is TypedefMirror;
-}
-
-/// A class containing properties of a Dart variable.
-class Variable extends OwnedIndexable {
-
- bool isFinal;
- bool isStatic;
- bool isConst;
- Type type;
- String _variableName;
-
- factory Variable(String variableName, VariableMirror mirror,
- Indexable owner) {
- var variable = getDocgenObject(mirror);
- if (variable is DummyMirror) {
- return new Variable._(variableName, mirror, owner);
- }
- return variable;
- }
-
- Variable._(this._variableName, VariableMirror mirror, Indexable owner) :
- super(mirror, owner) {
- isFinal = mirror.isFinal;
- isStatic = mirror.isStatic;
- isConst = mirror.isConst;
- type = new Type(mirror.type, owner.owningLibrary);
- }
-
- String get name => _variableName;
-
- /// Generates a map describing the [Variable] object.
- Map toMap() => {
- 'name': name,
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'final': isFinal,
- 'static': isStatic,
- 'constant': isConst,
- 'type': new List.filled(1, type.toMap()),
- 'annotations': annotations.map((a) => a.toMap()).toList()
- };
-
- String get typeName => 'property';
-
- get comment {
- if (_comment != null) return _comment;
- if (owner is Class) {
- (owner as Class).ensureComments();
- }
- return super.comment;
- }
-
- String findElementInScope(String name) {
- var lookupFunc = determineLookupFunc(name);
- var result = lookupFunc(mirror, name);
- if (result != null) {
- result = getDocgenObject(result);
- if (result is DummyMirror) return packagePrefix + result.docName;
- return result.packagePrefix + result.docName;
- }
-
- if (owner != null) {
- var result = owner.findElementInScope(name);
- if (result != null) {
- return result;
- }
- }
- return super.findElementInScope(name);
- }
-
- bool isValidMirror(DeclarationMirror mirror) => mirror is VariableMirror;
-}
-
-/// A class containing properties of a Dart method.
-class Method extends OwnedIndexable {
-
- /// Parameters for this method.
- final Map<String, Parameter> parameters;
-
- final bool isStatic;
- final bool isAbstract;
- final bool isConst;
- final Type returnType;
- Method methodInheritedFrom;
-
- /// Qualified name to state where the comment is inherited from.
- String commentInheritedFrom = "";
-
- factory Method(MethodMirror mirror, Indexable owner,
- [Method methodInheritedFrom]) {
- var method = getDocgenObject(mirror, owner);
- if (method is DummyMirror) {
- method = new Method._(mirror, owner, methodInheritedFrom);
- }
- return method;
- }
-
- Method._(MethodMirror mirror, Indexable owner, this.methodInheritedFrom)
- : returnType = new Type(mirror.returnType, owner.owningLibrary),
- isStatic = mirror.isStatic,
- isAbstract = mirror.isAbstract,
- isConst = mirror.isConstConstructor,
- parameters = createParameters(mirror.parameters, owner),
- super(mirror, owner);
-
- Method get originallyInheritedFrom => methodInheritedFrom == null ?
- this : methodInheritedFrom.originallyInheritedFrom;
-
- /// Look for the specified name starting with the current member, and
- /// progressively working outward to the current library scope.
- String findElementInScope(String name) {
- var lookupFunc = determineLookupFunc(name);
-
- var memberScope = lookupFunc(this.mirror, name);
- if (memberScope != null) {
- // do we check for a dummy mirror returned here and look up with an owner
- // higher ooooor in getDocgenObject do we include more things in our
- // lookup
- var result = getDocgenObject(memberScope, owner);
- if (result is DummyMirror && owner.owner != null
- && owner.owner is! DummyMirror) {
- var aresult = getDocgenObject(memberScope, owner.owner);
- if (aresult is! DummyMirror) result = aresult;
- }
- if (result is DummyMirror) return packagePrefix + result.docName;
- return result.packagePrefix + result.docName;
- }
-
- if (owner != null) {
- var result = owner.findElementInScope(name);
- if (result != null) return result;
- }
- return super.findElementInScope(name);
- }
-
- String get docName {
- if ((mirror as MethodMirror).isConstructor) {
- // We name constructors specially -- including the class name again and a
- // "-" to separate the constructor from its name (if any).
- return '${owner.docName}.${dart2js_util.nameOf(mirror.owner)}-'
- '${dart2js_util.nameOf(mirror)}';
- }
- return super.docName;
- }
-
- String get fileName => packagePrefix + docName;
-
- /// Makes sure that the method with an inherited equivalent have comments.
- void ensureCommentFor(Method inheritedMethod) {
- if (comment.isNotEmpty) return;
-
- comment = inheritedMethod._commentToHtml(this);
- _unresolvedComment = inheritedMethod._unresolvedComment;
- commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
- new DummyMirror(inheritedMethod.mirror).docName :
- inheritedMethod.commentInheritedFrom;
- }
-
- /// Generates a map describing the [Method] object.
- Map toMap() => {
- 'name': name,
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'commentFrom': (methodInheritedFrom != null &&
- commentInheritedFrom == methodInheritedFrom.docName ? ''
- : commentInheritedFrom),
- 'inheritedFrom': (methodInheritedFrom == null? '' :
- originallyInheritedFrom.docName),
- 'static': isStatic,
- 'abstract': isAbstract,
- 'constant': isConst,
- 'return': [returnType.toMap()],
- 'parameters': recurseMap(parameters),
- 'annotations': annotations.map((a) => a.toMap()).toList()
- };
-
- String get typeName {
- MethodMirror theMirror = mirror;
- if (theMirror.isConstructor) return 'constructor';
- if (theMirror.isGetter) return 'getter';
- if (theMirror.isSetter) return'setter';
- if (theMirror.isOperator) return 'operator';
- return 'method';
- }
-
- get comment {
- if (_comment != null) return _comment;
- if (owner is Class) {
- (owner as Class).ensureComments();
- }
- var result = super.comment;
- if (result == '' && methodInheritedFrom != null) {
- // This should be NOT from the MIRROR, but from the COMMENT.
- methodInheritedFrom.comment; // Ensure comment field has been populated.
- _unresolvedComment = methodInheritedFrom._unresolvedComment;
-
- var linkResolver = (name) => fixReference(name);
- comment = _unresolvedComment == null ? '' :
- markdown.markdownToHtml(_unresolvedComment.trim(),
- linkResolver: linkResolver, inlineSyntaxes: MARKDOWN_SYNTAXES);
- commentInheritedFrom = comment != '' ?
- methodInheritedFrom.commentInheritedFrom : '';
- result = comment;
- }
- return result;
- }
-
- bool isValidMirror(DeclarationMirror mirror) => mirror is MethodMirror;
-}
-
-/// Docgen wrapper around the dart2js mirror for a Dart
-/// method/function parameter.
-class Parameter extends MirrorBased {
- final ParameterMirror mirror;
- final String name;
- final bool isOptional;
- final bool isNamed;
- final bool hasDefaultValue;
- final Type type;
- final String defaultValue;
- /// List of the meta annotations on the parameter.
- final List<Annotation> annotations;
-
- Parameter(ParameterMirror mirror, Library owningLibrary)
- : this.mirror = mirror,
- name = dart2js_util.nameOf(mirror),
- isOptional = mirror.isOptional,
- isNamed = mirror.isNamed,
- hasDefaultValue = mirror.hasDefaultValue,
- defaultValue = '${mirror.defaultValue}',
- type = new Type(mirror.type, owningLibrary),
- annotations = createAnnotations(mirror, owningLibrary);
-
- /// Generates a map describing the [Parameter] object.
- Map toMap() => {
- 'name': name,
- 'optional': isOptional,
- 'named': isNamed,
- 'default': hasDefaultValue,
- 'type': new List.filled(1, type.toMap()),
- 'value': defaultValue,
- 'annotations': annotations.map((a) => a.toMap()).toList()
- };
-}
-
-/// Docgen wrapper around the mirror for a return type, and/or its generic
-/// type parameters.
-///
-/// Return types are of a form [outer]<[inner]>.
-/// If there is no [inner] part, [inner] will be an empty list.
-///
-/// For example:
-/// int size()
-/// "return" :
-/// - "outer" : "dart-core.int"
-/// "inner" :
-///
-/// List<String> toList()
-/// "return" :
-/// - "outer" : "dart-core.List"
-/// "inner" :
-/// - "outer" : "dart-core.String"
-/// "inner" :
-///
-/// Map<String, List<int>>
-/// "return" :
-/// - "outer" : "dart-core.Map"
-/// "inner" :
-/// - "outer" : "dart-core.String"
-/// "inner" :
-/// - "outer" : "dart-core.List"
-/// "inner" :
-/// - "outer" : "dart-core.int"
-/// "inner" :
-class Type extends MirrorBased {
- final TypeMirror mirror;
- final Library owningLibrary;
-
- Type(this.mirror, this.owningLibrary);
-
- Map toMap() {
- var result = getDocgenObject(mirror, owningLibrary);
- return {
- // We may encounter types whose corresponding library has not been
- // processed yet, so look up with the owningLibrary at the last moment.
- 'outer': result.packagePrefix + result.docName,
- 'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
- };
- }
-
- /// Returns a list of [Type] objects constructed from TypeMirrors.
- List<Type> _createTypeGenerics(TypeMirror mirror) {
- if (mirror is! ClassMirror) return [];
- return mirror.typeArguments.map((e) => new Type(e, owningLibrary)).toList();
- }
-}
-
-/// Holds the name of the annotation, and its parameters.
-class Annotation extends MirrorBased {
- /// The class of this annotation.
- final ClassMirror mirror;
- final Library owningLibrary;
- List<String> parameters;
-
- Annotation(InstanceMirror originalMirror, this.owningLibrary)
- : mirror = originalMirror.type {
- parameters = dart2js_util.variablesOf(originalMirror.type.declarations)
- .where((e) => e.isFinal)
- .map((e) => originalMirror.getField(e.simpleName).reflectee)
- .where((e) => e != null)
- .toList();
- }
-
- Map toMap() => {
- 'name': getDocgenObject(mirror, owningLibrary).docName,
- 'parameters': parameters
- };
-}
+export 'models/class.dart';
+export 'models/indexable.dart';
+export 'models/library.dart';
+export 'models/method.dart';
+export 'models/parameter.dart';
+export 'models/typedef.dart';
+export 'models/variable.dart';
diff --git a/pkg/docgen/lib/src/models/annotation.dart b/pkg/docgen/lib/src/models/annotation.dart
new file mode 100644
index 0000000..86cd274
--- /dev/null
+++ b/pkg/docgen/lib/src/models/annotation.dart
@@ -0,0 +1,56 @@
+// 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.
+
+library docgen.models.annotation;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'library.dart';
+import 'mirror_based.dart';
+
+/// Holds the name of the annotation, and its parameters.
+class Annotation extends MirrorBased<ClassMirror> {
+ /// The class of this annotation.
+ final ClassMirror mirror;
+ final Library owningLibrary;
+ final List<String> parameters;
+
+ Annotation(InstanceMirror originalMirror, this.owningLibrary)
+ : mirror = originalMirror.type,
+ parameters = _createParamaters(originalMirror);
+
+ Map toMap() => {
+ 'name': getDocgenObject(mirror, owningLibrary).docName,
+ 'parameters': parameters
+ };
+}
+
+List<String> _createParamaters(InstanceMirror originalMirror) {
+ var curMirror = originalMirror.type;
+ Map<Symbol, DeclarationMirror> allDeclarations =
+ new Map.from(curMirror.declarations);
+ // This method assumes that our users aren't creating deep inheritance
+ // chains of custom annotation inheritance. If this is not the case,
+ // re-write this section for performance.
+ while (curMirror.superclass != null &&
+ curMirror.superclass.simpleName.toString() != 'Object') {
+ allDeclarations.addAll(curMirror.superclass.declarations);
+ curMirror = curMirror.superclass;
+ }
+
+ // TODO(efortuna): Some originalMirrors, such as the
+ // Dart2JsMapConstantMirror and Dart2JsListConstantMirror don't have a
+ // reflectee field, but we want the value of the parameter from them.
+ // Gross workaround is to assemble the object manually.
+ // See issue 18346.
+ return dart2js_util.variablesOf(allDeclarations)
+ .where((e) => e.isFinal &&
+ originalMirror.getField(e.simpleName).hasReflectee)
+ .map((e) => originalMirror.getField(e.simpleName).reflectee)
+ .where((e) => e != null)
+ .toList();
+}
diff --git a/pkg/docgen/lib/src/models/class.dart b/pkg/docgen/lib/src/models/class.dart
new file mode 100644
index 0000000..555b1dd
--- /dev/null
+++ b/pkg/docgen/lib/src/models/class.dart
@@ -0,0 +1,245 @@
+// 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.
+
+library docgen.models.clazz;
+
+import '../exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'dummy_mirror.dart';
+import 'generic.dart';
+import 'library.dart';
+import 'method.dart';
+import 'model_helpers.dart';
+import 'owned_indexable.dart';
+import 'variable.dart';
+
+/// A class containing contents of a Dart class.
+class Class extends OwnedIndexable<dart2js_mirrors.Dart2JsInterfaceTypeMirror>
+ implements Comparable<Class> {
+
+ /// List of the names of interfaces that this class implements.
+ List<Class> interfaces = [];
+
+ /// Names of classes that extends or implements this class.
+ Set<Class> subclasses = new Set<Class>();
+
+ /// Top-level variables in the class.
+ Map<String, Variable> variables;
+
+ /// Inherited variables in the class.
+ final Map<String, Variable> inheritedVariables = {};
+
+ /// Methods in the class.
+ Map<String, Method> methods;
+
+ final Map<String, Method> inheritedMethods = new Map<String, Method>();
+
+ /// Generic infomation about the class.
+ final Map<String, Generic> generics;
+
+ Class _superclass;
+ bool get isAbstract => mirror.isAbstract;
+
+ /// Make sure that we don't check for inherited comments more than once.
+ bool _commentsEnsured = false;
+
+ /// Returns the [Class] for the given [mirror] if it has already been created,
+ /// else creates it.
+ factory Class(ClassMirror mirror, Library owner) {
+ var clazz = getDocgenObject(mirror, owner);
+ if (clazz is DummyMirror) {
+ clazz = new Class._(mirror, owner);
+ }
+ return clazz;
+ }
+
+ /// Called when we are constructing a superclass or interface class, but it
+ /// is not known if it belongs to the same owner as the original class. In
+ /// this case, we create an object whose owner is what the original mirror
+ /// says it is.
+ factory Class._possiblyDifferentOwner(ClassMirror mirror,
+ Library originalOwner) {
+ var realOwner = getDocgenObject(mirror.owner);
+ if (realOwner is Library) {
+ return new Class(mirror, realOwner);
+ } else {
+ return new Class(mirror, originalOwner);
+ }
+ }
+
+ Class._(ClassSourceMirror classMirror, Library owner)
+ : generics = createGenerics(classMirror),
+ super(classMirror, owner) {
+
+ // The reason we do this madness is the superclass and interface owners may
+ // not be this class's owner!! Example: BaseClient in http pkg.
+ var superinterfaces = classMirror.superinterfaces.map(
+ (interface) => new Class._possiblyDifferentOwner(interface, owner));
+ this._superclass = classMirror.superclass == null? null :
+ new Class._possiblyDifferentOwner(classMirror.superclass, owner);
+
+ interfaces = superinterfaces.toList();
+ variables = createVariables(
+ dart2js_util.variablesOf(classMirror.declarations), this);
+ methods = createMethods(classMirror.declarations.values.where(
+ (mirror) => mirror is MethodMirror), this);
+
+ // Tell superclass that you are a subclass, unless you are not
+ // visible or an intermediary mixin class.
+ if (!classMirror.isNameSynthetic && isVisible && _superclass != null) {
+ _superclass.addSubclass(this);
+ }
+
+ if (this._superclass != null) addInherited(_superclass);
+ interfaces.forEach((interface) => addInherited(interface));
+ }
+
+ String _lookupInClassAndSuperclasses(String name) {
+ var lookupFunc = determineLookupFunc(name);
+ var classScope = this;
+ while (classScope != null) {
+ var classFunc = lookupFunc(classScope.mirror, name);
+ if (classFunc != null) {
+ return packagePrefix + getDocgenObject(classFunc, owner).docName;
+ }
+ classScope = classScope._superclass;
+ }
+ return null;
+ }
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = determineLookupFunc(name);
+ var result = _lookupInClassAndSuperclasses(name);
+ if (result != null) {
+ return result;
+ }
+ result = owner.findElementInScope(name);
+ return result == null ? super.findElementInScope(name) : result;
+ }
+
+ String get typeName => 'class';
+
+ /// Add all inherited variables and methods from the provided superclass.
+ /// If [_includePrivate] is true, it also adds the variables and methods from
+ /// the superclass.
+ void addInherited(Class superclass) {
+ inheritedVariables.addAll(superclass.inheritedVariables);
+ inheritedVariables.addAll(_allButStatics(superclass.variables));
+ addInheritedMethod(superclass, this);
+ }
+
+ /** [newParent] refers to the actual class is currently using these methods.
+ * which may be different because with the mirror system, we only point to the
+ * original canonical superclasse's method.
+ */
+ void addInheritedMethod(Class parent, Class newParent) {
+ parent.inheritedMethods.forEach((name, method) {
+ if (!method.mirror.isConstructor) {
+ inheritedMethods[name] = new Method(method.mirror, newParent, method);
+ }
+ });
+ _allButStatics(parent.methods).forEach((name, method) {
+ if (!method.mirror.isConstructor) {
+ inheritedMethods[name] = new Method(method.mirror, newParent, method);
+ }
+ });
+ }
+
+ /// Remove statics from the map of inherited items before adding them.
+ Map _allButStatics(Map items) {
+ var result = {};
+ items.forEach((name, item) {
+ if (!item.isStatic) {
+ result[name] = item;
+ }
+ });
+ return result;
+ }
+
+ /// Add the subclass to the class.
+ ///
+ /// If [this] is private (or an intermediary mixin class), it will add the
+ /// subclass to the list of subclasses in the superclasses.
+ void addSubclass(Class subclass) {
+ if (docName == 'dart-core.Object') return;
+
+ if (!includePrivateMembers && isPrivate || mirror.isNameSynthetic) {
+ if (_superclass != null) _superclass.addSubclass(subclass);
+ interfaces.forEach((interface) {
+ interface.addSubclass(subclass);
+ });
+ } else {
+ subclasses.add(subclass);
+ }
+ }
+
+ /// Check if this [Class] is an error or exception.
+ bool isError() {
+ if (qualifiedName == 'dart-core.Error' ||
+ qualifiedName == 'dart-core.Exception')
+ return true;
+ for (var interface in interfaces) {
+ if (interface.isError()) return true;
+ }
+ if (_superclass == null) return false;
+ return _superclass.isError();
+ }
+
+ /// Makes sure that all methods with inherited equivalents have comments.
+ void ensureComments() {
+ if (_commentsEnsured) return;
+ _commentsEnsured = true;
+ if (_superclass != null) _superclass.ensureComments();
+ inheritedMethods.forEach((qualifiedName, inheritedMethod) {
+ var method = methods[qualifiedName];
+ if (method != null) {
+ // if we have overwritten this method in this class, we still provide
+ // the opportunity to inherit the comments.
+ method.ensureCommentFor(inheritedMethod);
+ }
+ });
+ // we need to populate the comments for all methods. so that the subclasses
+ // can get for their inherited versions the comments.
+ methods.forEach((qualifiedName, method) {
+ if (!method.mirror.isConstructor) method.ensureCommentFor(method);
+ });
+ }
+
+ /// If a class extends a private superclass, find the closest public
+ /// superclass of the private superclass.
+ String validSuperclass() {
+ if (_superclass == null) return 'dart-core.Object';
+ if (_superclass.isVisible) return _superclass.qualifiedName;
+ return _superclass.validSuperclass();
+ }
+
+ /// Generates a map describing the [Class] object.
+ Map toMap() => {
+ 'name': name,
+ 'qualifiedName': qualifiedName,
+ 'comment': comment,
+ 'isAbstract' : isAbstract,
+ 'superclass': validSuperclass(),
+ 'implements': interfaces.where((i) => i.isVisible)
+ .map((e) => e.qualifiedName).toList(),
+ 'subclass': (subclasses.toList()..sort())
+ .map((x) => x.qualifiedName).toList(),
+ 'variables': recurseMap(variables),
+ 'inheritedVariables': recurseMap(inheritedVariables),
+ 'methods': expandMethodMap(methods),
+ 'inheritedMethods': expandMethodMap(inheritedMethods),
+ 'annotations': annotations.map((a) => a.toMap()).toList(),
+ 'generics': recurseMap(generics)
+ };
+
+ int compareTo(Class other) => name.compareTo(other.name);
+
+ bool isValidMirror(DeclarationMirror mirror) => mirror is ClassMirror;
+}
diff --git a/pkg/docgen/lib/src/models/doc_gen_type.dart b/pkg/docgen/lib/src/models/doc_gen_type.dart
new file mode 100644
index 0000000..15fd51d
--- /dev/null
+++ b/pkg/docgen/lib/src/models/doc_gen_type.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library docgen.models.doc_gen_type;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'library.dart';
+import 'mirror_based.dart';
+
+/// Docgen wrapper around the mirror for a return type, and/or its generic
+/// type parameters.
+///
+/// Return types are of a form [outer]<[inner]>.
+/// If there is no [inner] part, [inner] will be an empty list.
+///
+/// For example:
+/// int size()
+/// "return" :
+/// - "outer" : "dart-core.int"
+/// "inner" :
+///
+/// List<String> toList()
+/// "return" :
+/// - "outer" : "dart-core.List"
+/// "inner" :
+/// - "outer" : "dart-core.String"
+/// "inner" :
+///
+/// Map<String, List<int>>
+/// "return" :
+/// - "outer" : "dart-core.Map"
+/// "inner" :
+/// - "outer" : "dart-core.String"
+/// "inner" :
+/// - "outer" : "dart-core.List"
+/// "inner" :
+/// - "outer" : "dart-core.int"
+/// "inner" :
+class DocGenType extends MirrorBased {
+ final TypeMirror mirror;
+ final Library owningLibrary;
+
+ DocGenType(this.mirror, this.owningLibrary);
+
+ Map toMap() {
+ var result = getDocgenObject(mirror, owningLibrary);
+ return {
+ // We may encounter types whose corresponding library has not been
+ // processed yet, so look up with the owningLibrary at the last moment.
+ 'outer': result.packagePrefix + result.docName,
+ 'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
+ };
+ }
+
+ /// Returns a list of [DocGenType] objects constructed from TypeMirrors.
+ List<DocGenType> _createTypeGenerics(TypeMirror mirror) {
+ if (mirror is! ClassMirror) return [];
+ return mirror.typeArguments
+ .map((e) => new DocGenType(e, owningLibrary))
+ .toList();
+ }
+}
diff --git a/pkg/docgen/lib/src/models/dummy_mirror.dart b/pkg/docgen/lib/src/models/dummy_mirror.dart
new file mode 100644
index 0000000..9ad612a
--- /dev/null
+++ b/pkg/docgen/lib/src/models/dummy_mirror.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library docgen.models.dummy_mirror;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'indexable.dart';
+import 'model_helpers.dart';
+
+/// For types that we do not explicitly create or have not yet created in our
+/// entity map (like core types).
+class DummyMirror implements Indexable {
+ final DeclarationMirror mirror;
+ /// The library that contains this element, if any. Used as a hint to help
+ /// determine which object we're referring to when looking up this mirror in
+ /// our map.
+ final Indexable owner;
+
+ DummyMirror(this.mirror, [this.owner]);
+
+ String get docName {
+ if (mirror is LibraryMirror) {
+ return getLibraryDocName(mirror);
+ }
+ var mirrorOwner = mirror.owner;
+ if (mirrorOwner == null) return dart2js_util.qualifiedNameOf(mirror);
+ var simpleName = dart2js_util.nameOf(mirror);
+ if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
+ // We name constructors specially -- repeating the class name and a
+ // "-" to separate the constructor from its name (if any).
+ simpleName = '${dart2js_util.nameOf(mirrorOwner)}-$simpleName';
+ }
+ return getDocgenObject(mirrorOwner, owner).docName + '.' +
+ simpleName;
+ }
+
+ bool get isPrivate => mirror.isPrivate;
+
+ String get packageName {
+ var libMirror = _getOwningLibraryFromMirror(mirror);
+ if (libMirror != null) {
+ return getPackageName(libMirror);
+ }
+ return '';
+ }
+
+ String get packagePrefix => packageName == null || packageName.isEmpty ?
+ '' : '$packageName/';
+
+ // This is a known incomplete implementation of Indexable
+ // overriding noSuchMethod to remove static warnings
+ noSuchMethod(Invocation invocation) {
+ throw new UnimplementedError(invocation.memberName.toString());
+ }
+}
+
+LibraryMirror _getOwningLibraryFromMirror(DeclarationMirror mirror) {
+ if (mirror == null) return null;
+ if (mirror is LibraryMirror) return mirror;
+ return _getOwningLibraryFromMirror(mirror.owner);
+}
diff --git a/pkg/docgen/lib/src/models/generic.dart b/pkg/docgen/lib/src/models/generic.dart
new file mode 100644
index 0000000..46a48a1
--- /dev/null
+++ b/pkg/docgen/lib/src/models/generic.dart
@@ -0,0 +1,22 @@
+// 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.
+
+library docgen.models.generic;
+
+import '../exports/source_mirrors.dart';
+import '../exports/mirrors_util.dart' as dart2js_util;
+
+import 'mirror_based.dart';
+
+/// A Docgen wrapper around the dart2js mirror for a generic type.
+class Generic extends MirrorBased<TypeVariableMirror> {
+ final TypeVariableMirror mirror;
+
+ Generic(this.mirror);
+
+ Map toMap() => {
+ 'name': dart2js_util.nameOf(mirror),
+ 'type': dart2js_util.qualifiedNameOf(mirror.upperBound)
+ };
+}
diff --git a/pkg/docgen/lib/src/models/indexable.dart b/pkg/docgen/lib/src/models/indexable.dart
new file mode 100644
index 0000000..53441d0
--- /dev/null
+++ b/pkg/docgen/lib/src/models/indexable.dart
@@ -0,0 +1,216 @@
+// 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.
+
+library docgen.models.indexable;
+
+import 'dart:collection';
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+import 'library.dart';
+import 'mirror_based.dart';
+import 'model_helpers.dart';
+
+/// An item that is categorized in our mirrorToDocgen map, as a distinct,
+/// searchable element.
+///
+/// These are items that refer to concrete entities (a Class, for example,
+/// but not a Type, which is a "pointer" to a class) that we wish to be
+/// globally resolvable. This includes things such as class methods and
+/// variables, but parameters for methods are not "Indexable" as we do not want
+/// the user to be able to search for a method based on its parameter names!
+/// The set of indexable items also includes Typedefs, since the user can refer
+/// to them as concrete entities in a particular scope.
+abstract class Indexable<TMirror extends DeclarationMirror>
+ extends MirrorBased<TMirror> {
+
+ Library get owningLibrary => owner.owningLibrary;
+
+ /// The reference to this element based on where it is printed as a
+ /// documentation file and also the unique URL to refer to this item.
+ ///
+ /// The qualified name (for URL purposes) and the file name are the same,
+ /// of the form packageName/ClassName or packageName/ClassName.methodName.
+ /// This defines both the URL and the directory structure.
+ String get qualifiedName => packagePrefix + ownerPrefix + name;
+
+ final TMirror mirror;
+ final bool isPrivate;
+ /// The comment text pre-resolution. We keep this around because inherited
+ /// methods need to resolve links differently from the superclass.
+ String unresolvedComment = '';
+
+ Indexable(TMirror mirror)
+ : this.mirror = mirror,
+ this.isPrivate = isHidden(mirror) {
+
+ var mirrorQualifiedName = dart2js_util.qualifiedNameOf(this.mirror);
+
+ var map = _mirrorToDocgen.putIfAbsent(mirrorQualifiedName,
+ () => new HashMap<String, Indexable>());
+
+ var added = false;
+ map.putIfAbsent(owner.docName, () {
+ added = true;
+ return this;
+ });
+
+ if (!added) {
+ throw new StateError('An indexable has already been stored for '
+ '${owner.docName}');
+ }
+ }
+
+ /// Returns this object's qualified name, but following the conventions
+ /// we're using in Dartdoc, which is that library names with dots in them
+ /// have them replaced with hyphens.
+ String get docName;
+
+ /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
+ markdown.Node fixReference(String name) {
+ // Attempt the look up the whole name up in the scope.
+ String elementName = findElementInScope(name);
+ if (elementName != null) {
+ return new markdown.Element.text('a', elementName);
+ }
+ return fixComplexReference(name);
+ }
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) =>
+ findElementInScopeWithPrefix(name, packagePrefix);
+
+ /// The full docName of the owner element, appended with a '.' for this
+ /// object's name to be appended.
+ String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
+
+ /// The prefix String to refer to the package that this item is in, for URLs
+ /// and comment resolution.
+ ///
+ /// The prefix can be prepended to a qualified name to get a fully unique
+ /// name among all packages.
+ String get packagePrefix;
+
+ /// Documentation comment with converted markdown and all links resolved.
+ String commentField;
+
+ /// Accessor to documentation comment with markdown converted to html and all
+ /// links resolved.
+ String get comment {
+ if (commentField != null) return commentField;
+
+ commentField = commentToHtml();
+ if (commentField.isEmpty) {
+ commentField = getMdnComment();
+ }
+ return commentField;
+ }
+
+ void set comment(x) {
+ commentField = x;
+ }
+
+ /// The simple name to refer to this item.
+ String get name => dart2js_util.nameOf(mirror);
+
+ /// Accessor to the parent item that owns this item.
+ ///
+ /// "Owning" is defined as the object one scope-level above which this item
+ /// is defined. Ex: The owner for a top level class, would be its enclosing
+ /// library. The owner of a local variable in a method would be the enclosing
+ /// method.
+ Indexable get owner;
+
+ /// Generates MDN comments from database.json.
+ String getMdnComment();
+
+ /// The type of this member to be used in index.txt.
+ String get typeName;
+
+ /// Creates a [Map] with this [Indexable]'s name and a preview comment.
+ Map get previewMap {
+ var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName };
+ var pre = preview;
+ if (pre != null) finalMap['preview'] = pre;
+ return finalMap;
+ }
+
+ String get preview {
+ if (comment != '') {
+ var index = comment.indexOf('</p>');
+ return index > 0 ?
+ '${comment.substring(0, index)}</p>' :
+ '<p><i>Comment preview not available</i></p>';
+ }
+ return null;
+ }
+
+ /// Accessor to obtain the raw comment text for a given item, _without_ any
+ /// of the links resolved.
+ String get _commentText {
+ String commentText;
+ mirror.metadata.forEach((metadata) {
+ if (metadata is CommentInstanceMirror) {
+ CommentInstanceMirror comment = metadata;
+ if (comment.isDocComment) {
+ if (commentText == null) {
+ commentText = comment.trimmedText;
+ } else {
+ commentText = '$commentText\n${comment.trimmedText}';
+ }
+ }
+ }
+ });
+ return commentText;
+ }
+
+ /// Returns any documentation comments associated with a mirror with
+ /// simple markdown converted to html.
+ ///
+ /// By default we resolve any comment references within our own scope.
+ /// However, if a method is inherited, we want the inherited comments, but
+ /// links to the subclasses's version of the methods.
+ String commentToHtml([Indexable resolvingScope]) {
+ if (resolvingScope == null) resolvingScope = this;
+ var commentText = _commentText;
+ unresolvedComment = commentText;
+
+ commentText = commentText == null ? '' :
+ markdown.markdownToHtml(commentText.trim(),
+ linkResolver: resolvingScope.fixReference,
+ inlineSyntaxes: MARKDOWN_SYNTAXES);
+ return commentText;
+ }
+
+ /// Return a map representation of this type.
+ Map toMap();
+
+ /// Accessor to determine if this item and all of its owners are visible.
+ bool get isVisible => isFullChainVisible(this);
+
+ /// Returns true if [mirror] is the correct type of mirror that this Docgen
+ /// object wraps. (Workaround for the fact that Types are not first class.)
+ bool isValidMirror(DeclarationMirror mirror);
+}
+
+/// Index of all the dart2js mirrors examined to corresponding MirrorBased
+/// docgen objects.
+///
+/// Used for lookup because of the dart2js mirrors exports
+/// issue. The second level map is indexed by owner docName for faster lookup.
+/// Why two levels of lookup? Speed, man. Speed.
+final Map<String, Map<String, Indexable>> _mirrorToDocgen =
+ new HashMap<String, Map<String, Indexable>>();
+
+Iterable<Indexable> get allIndexables =>
+ _mirrorToDocgen.values.expand((map) => map.values);
+
+Map<String, Indexable> lookupIndexableMap(DeclarationMirror mirror) {
+ return _mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)];
+}
diff --git a/pkg/docgen/lib/src/models/library.dart b/pkg/docgen/lib/src/models/library.dart
new file mode 100644
index 0000000..826536f
--- /dev/null
+++ b/pkg/docgen/lib/src/models/library.dart
@@ -0,0 +1,190 @@
+// 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.
+
+library docgen.models.library;
+
+import 'dart:io';
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/source_mirrors.dart';
+import '../exports/mirrors_util.dart' as dart2js_util;
+
+import '../library_helpers.dart';
+import '../package_helpers.dart';
+
+import 'class.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'method.dart';
+import 'model_helpers.dart';
+import 'typedef.dart';
+import 'variable.dart';
+
+/// A class containing contents of a Dart library.
+class Library extends Indexable {
+ final Map<String, Class> classes = {};
+ final Map<String, Typedef> typedefs = {};
+ final Map<String, Class> errors = {};
+
+ /// Top-level variables in the library.
+ Map<String, Variable> variables;
+
+ /// Top-level functions in the library.
+ Map<String, Method> functions;
+
+ String packageName = '';
+ bool _hasBeenCheckedForPackage = false;
+ String packageIntro;
+
+ Indexable get owner => const _LibraryOwner();
+
+ Library get owningLibrary => this;
+
+ /// Returns the [Library] for the given [mirror] if it has already been
+ /// created, else creates it.
+ factory Library(LibraryMirror mirror) {
+ var library = getDocgenObject(mirror);
+ if (library is DummyMirror) {
+ library = new Library._(mirror);
+ }
+ return library;
+ }
+
+ Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
+ var exported = calcExportedItems(libraryMirror);
+ var exportedClasses = addAll(exported['classes'],
+ dart2js_util.typesOf(libraryMirror.declarations));
+ updateLibraryPackage(mirror);
+ exportedClasses.forEach((String mirrorName, TypeMirror mirror) {
+ if (mirror is TypedefMirror) {
+ // This is actually a Dart2jsTypedefMirror, and it does define value,
+ // but we don't have visibility to that type.
+ if (includePrivateMembers || !mirror.isPrivate) {
+ typedefs[dart2js_util.nameOf(mirror)] = new Typedef(mirror, this);
+ }
+ } else if (mirror is ClassMirror) {
+ var clazz = new Class(mirror, this);
+
+ if (clazz.isError()) {
+ errors[dart2js_util.nameOf(mirror)] = clazz;
+ } else {
+ classes[dart2js_util.nameOf(mirror)] = clazz;
+ }
+ } else {
+ throw new ArgumentError(
+ '${dart2js_util.nameOf(mirror)} - no class type match. ');
+ }
+ });
+ this.functions = createMethods(addAll(exported['methods'],
+ libraryMirror.declarations.values.where(
+ (mirror) => mirror is MethodMirror)).values, this);
+ this.variables = createVariables(addAll(exported['variables'],
+ dart2js_util.variablesOf(libraryMirror.declarations)).values, this);
+ }
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = determineLookupFunc(name);
+ var libraryScope = lookupFunc(mirror, name);
+ if (libraryScope != null) {
+ var result = getDocgenObject(libraryScope, this);
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
+ return super.findElementInScope(name);
+ }
+
+ String getMdnComment() => '';
+
+ /// For a library's [mirror], determine the name of the package (if any) we
+ /// believe it came from (because of its file URI).
+ ///
+ /// If no package could be determined, we return an empty string.
+ void updateLibraryPackage(LibraryMirror mirror) {
+ if (mirror == null) return;
+ if (_hasBeenCheckedForPackage) return;
+ _hasBeenCheckedForPackage = true;
+ if (mirror.uri.scheme != 'file') return;
+ packageName = getPackageName(mirror);
+ // Associate the package readme with all the libraries. This is a bit
+ // wasteful, but easier than trying to figure out which partial match
+ // is best.
+ packageIntro = _packageIntro(getPackageDirectory(mirror));
+ }
+
+ String _packageIntro(packageDir) {
+ if (packageDir == null) return null;
+ var dir = new Directory(packageDir);
+ var files = dir.listSync();
+ var readmes = files.where((FileSystemEntity each) => (each is File &&
+ each.path.substring(packageDir.length + 1, each.path.length)
+ .startsWith('README'))).toList();
+ if (readmes.isEmpty) return '';
+ // If there are multiples, pick the shortest name.
+ readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
+ var readme = readmes.first;
+ var linkResolver = (name) => globalFixReference(name);
+ var contents = markdown.markdownToHtml(readme
+ .readAsStringSync(), linkResolver: linkResolver,
+ inlineSyntaxes: MARKDOWN_SYNTAXES);
+ return contents;
+ }
+
+ String get packagePrefix => packageName == null || packageName.isEmpty ?
+ '' : '$packageName/';
+
+ Map get previewMap {
+ var map = {'packageName': packageName};
+ map.addAll(super.previewMap);
+ if (packageIntro != null) {
+ map['packageIntro'] = packageIntro;
+ }
+ return map;
+ }
+
+ String get name => docName;
+
+ String get docName => getLibraryDocName(mirror);
+
+ /// Generates a map describing the [Library] object.
+ Map toMap() => {
+ 'name': name,
+ 'qualifiedName': qualifiedName,
+ 'comment': comment,
+ 'variables': recurseMap(variables),
+ 'functions': expandMethodMap(functions),
+ 'classes': {
+ 'class': classes.values.where((c) => c.isVisible)
+ .map((e) => e.previewMap).toList(),
+ 'typedef': recurseMap(typedefs),
+ 'error': errors.values.where((e) => e.isVisible)
+ .map((e) => e.previewMap).toList()
+ },
+ 'packageName': packageName,
+ 'packageIntro': packageIntro
+ };
+
+ String get typeName => 'library';
+
+ bool isValidMirror(DeclarationMirror mirror) => mirror is LibraryMirror;
+}
+
+/// Dummy implementation of Indexable to represent the owner of a Library.
+class _LibraryOwner implements Indexable {
+ const _LibraryOwner();
+
+ String get docName => '';
+
+ bool get isPrivate => false;
+
+ Indexable get owner => null;
+
+ // This is a known incomplete implementation of Indexable
+ // overriding noSuchMethod to remove static warnings
+ noSuchMethod(Invocation invocation) {
+ throw new UnimplementedError(invocation.memberName.toString());
+ }
+}
diff --git a/pkg/docgen/lib/src/models/method.dart b/pkg/docgen/lib/src/models/method.dart
new file mode 100644
index 0000000..9483a37
--- /dev/null
+++ b/pkg/docgen/lib/src/models/method.dart
@@ -0,0 +1,156 @@
+// 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.
+
+library docgen.models.method;
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'class.dart';
+import 'doc_gen_type.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'model_helpers.dart';
+import 'owned_indexable.dart';
+import 'parameter.dart';
+
+
+/// A class containing properties of a Dart method.
+class Method extends OwnedIndexable<MethodMirror> {
+
+ /// Parameters for this method.
+ final Map<String, Parameter> parameters;
+
+ final bool isStatic;
+ final bool isAbstract;
+ final bool isConst;
+ final DocGenType returnType;
+ Method methodInheritedFrom;
+
+ /// Qualified name to state where the comment is inherited from.
+ String commentInheritedFrom = "";
+
+ factory Method(MethodMirror mirror, Indexable owner,
+ [Method methodInheritedFrom]) {
+ var method = getDocgenObject(mirror, owner);
+ if (method is DummyMirror) {
+ method = new Method._(mirror, owner, methodInheritedFrom);
+ }
+ return method;
+ }
+
+ Method._(MethodMirror mirror, Indexable owner, this.methodInheritedFrom)
+ : returnType = new DocGenType(mirror.returnType, owner.owningLibrary),
+ isStatic = mirror.isStatic,
+ isAbstract = mirror.isAbstract,
+ isConst = mirror.isConstConstructor,
+ parameters = createParameters(mirror.parameters, owner),
+ super(mirror, owner);
+
+ Method get originallyInheritedFrom => methodInheritedFrom == null ?
+ this : methodInheritedFrom.originallyInheritedFrom;
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = determineLookupFunc(name);
+
+ var memberScope = lookupFunc(this.mirror, name);
+ if (memberScope != null) {
+ // do we check for a dummy mirror returned here and look up with an owner
+ // higher ooooor in getDocgenObject do we include more things in our
+ // lookup
+ var result = getDocgenObject(memberScope, owner);
+ if (result is DummyMirror && owner.owner != null
+ && owner.owner is! DummyMirror) {
+ var aresult = getDocgenObject(memberScope, owner.owner);
+ if (aresult is! DummyMirror) result = aresult;
+ }
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
+
+ if (owner != null) {
+ var result = owner.findElementInScope(name);
+ if (result != null) return result;
+ }
+ return super.findElementInScope(name);
+ }
+
+ String get docName {
+ if (mirror.isConstructor) {
+ // We name constructors specially -- including the class name again and a
+ // "-" to separate the constructor from its name (if any).
+ return '${owner.docName}.${dart2js_util.nameOf(mirror.owner)}-'
+ '${dart2js_util.nameOf(mirror)}';
+ }
+ return super.docName;
+ }
+
+ String get qualifiedName => packagePrefix + docName;
+
+ /// Makes sure that the method with an inherited equivalent have comments.
+ void ensureCommentFor(Method inheritedMethod) {
+ if (comment.isNotEmpty) return;
+
+ comment = inheritedMethod.commentToHtml(this);
+ unresolvedComment = inheritedMethod.unresolvedComment;
+ commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
+ new DummyMirror(inheritedMethod.mirror).docName :
+ inheritedMethod.commentInheritedFrom;
+ }
+
+ /// Generates a map describing the [Method] object.
+ Map toMap() => {
+ 'name': name,
+ 'qualifiedName': qualifiedName,
+ 'comment': comment,
+ 'commentFrom': (methodInheritedFrom != null &&
+ commentInheritedFrom == methodInheritedFrom.docName ? ''
+ : commentInheritedFrom),
+ 'inheritedFrom': (methodInheritedFrom == null? '' :
+ originallyInheritedFrom.docName),
+ 'static': isStatic,
+ 'abstract': isAbstract,
+ 'constant': isConst,
+ 'return': [returnType.toMap()],
+ 'parameters': recurseMap(parameters),
+ 'annotations': annotations.map((a) => a.toMap()).toList()
+ };
+
+ String get typeName {
+ if (mirror.isConstructor) return 'constructor';
+ if (mirror.isGetter) return 'getter';
+ if (mirror.isSetter) return 'setter';
+ if (mirror.isOperator) return 'operator';
+ return 'method';
+ }
+
+ String get comment {
+ if (commentField != null) return commentField;
+ if (owner is Class) {
+ (owner as Class).ensureComments();
+ }
+ var result = super.comment;
+ if (result == '' && methodInheritedFrom != null) {
+ // This should be NOT from the MIRROR, but from the COMMENT.
+ methodInheritedFrom.comment; // Ensure comment field has been populated.
+ unresolvedComment = methodInheritedFrom.unresolvedComment;
+
+ comment = unresolvedComment == null ? '' :
+ markdown.markdownToHtml(unresolvedComment.trim(),
+ linkResolver: fixReference, inlineSyntaxes: MARKDOWN_SYNTAXES);
+ commentInheritedFrom = comment != '' ?
+ methodInheritedFrom.commentInheritedFrom : '';
+ result = comment;
+ }
+ return result;
+ }
+
+ bool isValidMirror(DeclarationMirror mirror) => mirror is MethodMirror;
+}
diff --git a/pkg/docgen/lib/src/models/mirror_based.dart b/pkg/docgen/lib/src/models/mirror_based.dart
new file mode 100644
index 0000000..eceac68
--- /dev/null
+++ b/pkg/docgen/lib/src/models/mirror_based.dart
@@ -0,0 +1,17 @@
+// 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.
+
+library docgen.models.mirror_based;
+
+import '../exports/source_mirrors.dart';
+
+/// Docgen representation of an item to be documented, that wraps around a
+/// dart2js mirror.
+abstract class MirrorBased<TMirror extends DeclarationMirror> {
+ /// The original dart2js mirror around which this object wraps.
+ TMirror get mirror;
+
+ /// Return an informative [Object.toString] for debugging.
+ String toString() => "${super.toString()} - $mirror";
+}
diff --git a/pkg/docgen/lib/src/model_helpers.dart b/pkg/docgen/lib/src/models/model_helpers.dart
similarity index 77%
rename from pkg/docgen/lib/src/model_helpers.dart
rename to pkg/docgen/lib/src/models/model_helpers.dart
index 1c8a255..9f683d0 100644
--- a/pkg/docgen/lib/src/model_helpers.dart
+++ b/pkg/docgen/lib/src/models/model_helpers.dart
@@ -4,16 +4,76 @@
library docgen.model_helpers;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
- as dart2js_mirrors;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/libraries.dart';
+import 'dart:collection';
-import 'library_helpers.dart' show includePrivateMembers;
-import 'models.dart';
-import 'package_helpers.dart';
+import '../exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+import '../exports/libraries.dart';
+
+import '../library_helpers.dart' show includePrivateMembers;
+import '../package_helpers.dart';
+
+import 'annotation.dart';
+import 'generic.dart';
+import 'indexable.dart';
+import 'library.dart';
+import 'method.dart';
+import 'parameter.dart';
+import 'variable.dart';
+
+String getLibraryDocName(LibraryMirror mirror) =>
+ dart2js_util.qualifiedNameOf(mirror).replaceAll('.', '-');
+
+/// Expand the method map [mapToExpand] into a more detailed map that
+/// separates out setters, getters, constructors, operators, and methods.
+Map expandMethodMap(Map<String, Method> mapToExpand) => {
+ 'setters': recurseMap(filterMap(mapToExpand,
+ (key, val) => val.mirror.isSetter)),
+ 'getters': recurseMap(filterMap(mapToExpand,
+ (key, val) => val.mirror.isGetter)),
+ 'constructors': recurseMap(filterMap(mapToExpand,
+ (key, val) => val.mirror.isConstructor)),
+ 'operators': recurseMap(filterMap(mapToExpand,
+ (key, val) => val.mirror.isOperator)),
+ 'methods': recurseMap(filterMap(mapToExpand,
+ (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
+};
+
+String getDefaultValue(ParameterMirror mirror) {
+ if (!mirror.hasDefaultValue) return null;
+ return getDefaultValueFromConstMirror(mirror.defaultValue);
+}
+
+String getDefaultValueFromConstMirror(
+ dart2js_mirrors.Dart2JsConstantMirror valueMirror) {
+
+ if (valueMirror is dart2js_mirrors.Dart2JsStringConstantMirror) {
+ return '"${valueMirror.reflectee}"';
+ }
+
+ if (valueMirror is dart2js_mirrors.Dart2JsListConstantMirror) {
+ var buffer = new StringBuffer('[');
+
+ var values = new Iterable.generate(valueMirror.length,
+ (i) => valueMirror.getElement(i))
+ .map((e) => getDefaultValueFromConstMirror(e));
+
+ buffer.writeAll(values, ', ');
+
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ if (valueMirror is dart2js_mirrors.Dart2JsMapConstantMirror) {
+ // TODO(kevmoo) Handle non-empty case
+ if (valueMirror.length == 0) return '{}';
+ }
+
+ // TODO(kevmoo) Handle consts of non-core types
+
+ return '${valueMirror}';
+}
/// Returns a list of meta annotations assocated with a mirror.
List<Annotation> createAnnotations(DeclarationMirror mirror,
@@ -47,7 +107,7 @@
/// Transforms the map by calling toMap on each value in it.
Map recurseMap(Map inputMap) {
- var outputMap = {};
+ var outputMap = new SplayTreeMap();
inputMap.forEach((key, value) {
if (value is Map) {
outputMap[key] = recurseMap(value);
diff --git a/pkg/docgen/lib/src/models/owned_indexable.dart b/pkg/docgen/lib/src/models/owned_indexable.dart
new file mode 100644
index 0000000..ea3335d
--- /dev/null
+++ b/pkg/docgen/lib/src/models/owned_indexable.dart
@@ -0,0 +1,70 @@
+// 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.
+
+library docgen.models.owned_indexable;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+import '../mdn.dart';
+import '../package_helpers.dart';
+
+import 'annotation.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'model_helpers.dart';
+
+abstract class OwnedIndexable<TMirror extends DeclarationMirror>
+ extends Indexable<TMirror> {
+ /// List of the meta annotations on this item.
+ final List<Annotation> annotations;
+
+ /// The object one scope-level above which this item is defined.
+ ///
+ /// Ex: The owner for a top level class, would be its enclosing library.
+ /// The owner of a local variable in a method would be the enclosing method.
+ final Indexable owner;
+
+ /// Returns this object's qualified name, but following the conventions
+ /// we're using in Dartdoc, which is that library names with dots in them
+ /// have them replaced with hyphens.
+ String get docName => owner.docName + '.' + dart2js_util.nameOf(mirror);
+
+ OwnedIndexable(DeclarationMirror mirror, Indexable owner)
+ : annotations = createAnnotations(mirror, owner.owningLibrary),
+ this.owner = owner,
+ super(mirror);
+
+ /// Generates MDN comments from database.json.
+ String getMdnComment() {
+ var domAnnotation = this.annotations.firstWhere(
+ (e) => e.mirror.qualifiedName == #metadata.DomName,
+ orElse: () => null);
+ if (domAnnotation == null) return '';
+ var domName = domAnnotation.parameters.single;
+
+ return mdnComment(rootDirectory, logger, domName);
+ }
+
+ String get packagePrefix => owner.packagePrefix;
+
+ String findElementInScope(String name) {
+ var lookupFunc = determineLookupFunc(name);
+ var result = lookupFunc(mirror, name);
+ if (result != null) {
+ result = getDocgenObject(result);
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
+
+ if (owner != null) {
+ var result = owner.findElementInScope(name);
+ if (result != null) {
+ return result;
+ }
+ }
+ return super.findElementInScope(name);
+ }
+}
diff --git a/pkg/docgen/lib/src/models/parameter.dart b/pkg/docgen/lib/src/models/parameter.dart
new file mode 100644
index 0000000..2f8798c
--- /dev/null
+++ b/pkg/docgen/lib/src/models/parameter.dart
@@ -0,0 +1,49 @@
+// 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.
+
+library docgen.models.parameter;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import 'annotation.dart';
+import 'doc_gen_type.dart';
+import 'library.dart';
+import 'mirror_based.dart';
+import 'model_helpers.dart';
+
+/// Docgen wrapper around the dart2js mirror for a Dart
+/// method/function parameter.
+class Parameter extends MirrorBased {
+ final ParameterMirror mirror;
+ final String name;
+ final bool isOptional;
+ final bool isNamed;
+ final bool hasDefaultValue;
+ final DocGenType type;
+ final String defaultValue;
+ /// List of the meta annotations on the parameter.
+ final List<Annotation> annotations;
+
+ Parameter(ParameterMirror mirror, Library owningLibrary)
+ : this.mirror = mirror,
+ name = dart2js_util.nameOf(mirror),
+ isOptional = mirror.isOptional,
+ isNamed = mirror.isNamed,
+ hasDefaultValue = mirror.hasDefaultValue,
+ defaultValue = getDefaultValue(mirror),
+ type = new DocGenType(mirror.type, owningLibrary),
+ annotations = createAnnotations(mirror, owningLibrary);
+
+ /// Generates a map describing the [Parameter] object.
+ Map toMap() => {
+ 'name': name,
+ 'optional': isOptional,
+ 'named': isNamed,
+ 'default': hasDefaultValue,
+ 'type': new List.filled(1, type.toMap()),
+ 'value': defaultValue,
+ 'annotations': annotations.map((a) => a.toMap()).toList()
+ };
+}
diff --git a/pkg/docgen/lib/src/models/typedef.dart b/pkg/docgen/lib/src/models/typedef.dart
new file mode 100644
index 0000000..17e4208
--- /dev/null
+++ b/pkg/docgen/lib/src/models/typedef.dart
@@ -0,0 +1,65 @@
+// 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.
+
+library docgen.models.typedef;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'dummy_mirror.dart';
+import 'library.dart';
+import 'model_helpers.dart';
+import 'generic.dart';
+import 'parameter.dart';
+import 'owned_indexable.dart';
+
+class Typedef extends OwnedIndexable<TypedefMirror> {
+ final String returnType;
+
+ final Map<String, Parameter> parameters;
+
+ /// Generic information about the typedef.
+ final Map<String, Generic> generics;
+
+ /// Returns the [Library] for the given [mirror] if it has already been
+ /// created, else creates it.
+ factory Typedef(TypedefMirror mirror, Library owningLibrary) {
+ var aTypedef = getDocgenObject(mirror, owningLibrary);
+ if (aTypedef is DummyMirror) {
+ aTypedef = new Typedef._(mirror, owningLibrary);
+ }
+ return aTypedef;
+ }
+
+ Typedef._(TypedefMirror mirror, Library owningLibrary)
+ : returnType = getDocgenObject(mirror.referent.returnType).docName,
+ generics = createGenerics(mirror),
+ parameters = createParameters(mirror.referent.parameters,
+ owningLibrary),
+ super(mirror, owningLibrary);
+
+ Map toMap() {
+ var map = {
+ 'name': name,
+ 'qualifiedName': qualifiedName,
+ 'comment': comment,
+ 'return': returnType,
+ 'parameters': recurseMap(parameters),
+ 'annotations': annotations.map((a) => a.toMap()).toList(),
+ 'generics': recurseMap(generics)
+ };
+
+ // Typedef is displayed on the library page as a class, so a preview is
+ // added manually
+ var pre = preview;
+ if (pre != null) map['preview'] = pre;
+
+ return map;
+ }
+
+ String get typeName => 'typedef';
+
+ bool isValidMirror(DeclarationMirror mirror) => mirror is TypedefMirror;
+}
diff --git a/pkg/docgen/lib/src/models/variable.dart b/pkg/docgen/lib/src/models/variable.dart
new file mode 100644
index 0000000..2d8b02a
--- /dev/null
+++ b/pkg/docgen/lib/src/models/variable.dart
@@ -0,0 +1,64 @@
+// 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.
+
+library docgen.models.variable;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'class.dart';
+import 'doc_gen_type.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'owned_indexable.dart';
+
+
+/// A class containing properties of a Dart variable.
+class Variable extends OwnedIndexable<VariableMirror> {
+ final bool isFinal;
+ final bool isStatic;
+ final bool isConst;
+ final DocGenType type;
+ final String name;
+
+ factory Variable(String name, VariableMirror mirror, Indexable owner) {
+ var variable = getDocgenObject(mirror);
+ if (variable is DummyMirror) {
+ return new Variable._(name, mirror, owner);
+ }
+ return variable;
+ }
+
+ Variable._(this.name, VariableMirror mirror, Indexable owner)
+ : isFinal = mirror.isFinal,
+ isStatic = mirror.isStatic,
+ isConst = mirror.isConst,
+ type = new DocGenType(mirror.type, owner.owningLibrary),
+ super(mirror, owner);
+
+ /// Generates a map describing the [Variable] object.
+ Map toMap() => {
+ 'name': name,
+ 'qualifiedName': qualifiedName,
+ 'comment': comment,
+ 'final': isFinal,
+ 'static': isStatic,
+ 'constant': isConst,
+ 'type': new List.filled(1, type.toMap()),
+ 'annotations': annotations.map((a) => a.toMap()).toList()
+ };
+
+ String get typeName => 'property';
+
+ String get comment {
+ if (commentField != null) return commentField;
+ if (owner is Class) {
+ (owner as Class).ensureComments();
+ }
+ return super.comment;
+ }
+
+ bool isValidMirror(DeclarationMirror mirror) => mirror is VariableMirror;
+}
diff --git a/pkg/docgen/lib/src/package_helpers.dart b/pkg/docgen/lib/src/package_helpers.dart
index 599bb80..e8a36fc 100644
--- a/pkg/docgen/lib/src/package_helpers.dart
+++ b/pkg/docgen/lib/src/package_helpers.dart
@@ -4,7 +4,7 @@
library docgen.package_helpers;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
+import 'exports/source_mirrors.dart';
import 'dart:io';
import 'package:path/path.dart' as path;
diff --git a/pkg/docgen/pubspec.yaml b/pkg/docgen/pubspec.yaml
index 605766c..e6913dd 100644
--- a/pkg/docgen/pubspec.yaml
+++ b/pkg/docgen/pubspec.yaml
@@ -1,16 +1,13 @@
name: docgen
-version: 0.9.2-dev
author: Dart Team <misc@dartlang.org>
description: A documentation generator for the Dart repository.
-homepage: https://github.com/dart-lang/dartdoc-viewer
-environment:
- sdk: '>=0.8.10+6 <2.0.0'
+homepage: https://www.dartlang.org/
dependencies:
args: '>=0.9.0 <0.11.0'
logging: '>=0.9.0 <0.10.0'
- markdown: '0.6.0'
+ markdown: 0.7.0
path: '>=0.9.0 <2.0.0'
yaml: '>=0.9.0 <0.10.0'
dev_dependencies:
- scheduled_test: '>=0.10.0 <0.11.0'
+ scheduled_test: '>=0.10.0 <0.12.0'
unittest: '>=0.9.0 <0.11.0'
diff --git a/pkg/docgen/test/constant_argument_test.dart b/pkg/docgen/test/constant_argument_test.dart
new file mode 100644
index 0000000..ca735f4
--- /dev/null
+++ b/pkg/docgen/test/constant_argument_test.dart
@@ -0,0 +1,62 @@
+// 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.
+
+library docgen.test.typedef;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:scheduled_test/descriptor.dart' as d;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import 'util.dart';
+import '../lib/docgen.dart' as dg;
+
+void main() {
+
+ setUp(() {
+ scheduleTempDir();
+ });
+
+ test('argument default values', () {
+ schedule(() {
+ var codeDir = getMultiLibraryCodePath();
+ expect(FileSystemEntity.isDirectorySync(codeDir), isTrue);
+ return dg.docgen([codeDir], out: p.join(d.defaultRoot, 'docs'));
+ });
+
+ schedule(() {
+ var path = p.join(d.defaultRoot, 'docs', 'test_lib.json');
+ var dartCoreJson = new File(path).readAsStringSync();
+
+ var testLibBar = JSON.decode(dartCoreJson) as Map<String, dynamic>;
+
+ //
+ // Validate function doc references
+ //
+ var functionDef =
+ testLibBar['functions']['methods']['positionalDefaultValues']
+ as Map<String, dynamic>;
+
+ var params = functionDef['parameters'] as Map<String, dynamic>;
+
+ var vals = {};
+ params.forEach((paramName, paramHash) {
+ expect(_PARAM_VALUES, contains(paramName));
+ expect(paramHash['value'], _PARAM_VALUES[paramName],
+ reason: 'Value for $paramName should match expected');
+ });
+ });
+ });
+}
+
+final _PARAM_VALUES = {
+ "boolConst": "true",
+ "intConst": "42",
+ "listConst": '[true, 42, "Shanna", null, 3.14, []]',
+ "mapConst": startsWith("Map"),
+ "emptyMap": '{}',
+ "stringConst": "\"Shanna\""
+};
diff --git a/pkg/docgen/test/generate_json_test.dart b/pkg/docgen/test/generate_json_test.dart
index 9149a96..a6ac53d 100644
--- a/pkg/docgen/test/generate_json_test.dart
+++ b/pkg/docgen/test/generate_json_test.dart
@@ -30,6 +30,8 @@
d.matcherFile('index.json', isJsonMap),
d.matcherFile('index.txt', hasSortedLines),
d.matcherFile('library_list.json', isJsonMap),
+ d.matcherFile('library_list.json',
+ startsWith(_LIBRARY_LIST_UNINDENT_START)),
d.matcherFile('test_lib-bar.C.json', isJsonMap),
d.matcherFile('test_lib-bar.json', isJsonMap),
d.matcherFile('test_lib-foo.B.json', isJsonMap),
@@ -39,6 +41,7 @@
d.matcherFile('test_lib.C.json', isJsonMap),
d.matcherFile('test_lib.json', isJsonMap),
]).validate();
-
});
}
+
+const _LIBRARY_LIST_UNINDENT_START = '{"libraries":[{"packageName":""';
diff --git a/pkg/docgen/test/inherited_comments_test.dart b/pkg/docgen/test/inherited_comments_test.dart
new file mode 100644
index 0000000..2f229f94
--- /dev/null
+++ b/pkg/docgen/test/inherited_comments_test.dart
@@ -0,0 +1,49 @@
+// 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.
+
+library docgen.test.inherited_comments;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:scheduled_test/descriptor.dart' as d;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import 'util.dart';
+import '../lib/docgen.dart' as dg;
+
+void main() {
+
+ setUp(() {
+ scheduleTempDir();
+ });
+
+ test('inherited comments', () {
+ schedule(() {
+ var codeDir = getMultiLibraryCodePath();
+ expect(FileSystemEntity.isDirectorySync(codeDir), isTrue);
+ return dg.docgen([codeDir], out: p.join(d.defaultRoot, 'docs'));
+ });
+
+ schedule(() {
+ var path = p.join(d.defaultRoot, 'docs', 'dart-core.Set.json');
+ var dartCoreSetJson = new File(path).readAsStringSync();
+
+ var dartCoreSet = JSON.decode(dartCoreSetJson) as Map<String, dynamic>;
+
+ var toListDetails = dartCoreSet['inheritedMethods']['methods']['toList']
+ as Map<String, dynamic>;
+
+ expect(toListDetails, containsPair('comment', _TO_LIST_COMMENT));
+ expect(toListDetails, containsPair('commentFrom', _TO_LIST_COMMENT_FROM));
+ });
+ });
+}
+
+const _TO_LIST_COMMENT = "<p>Creates a <a>dart-core.List</a> containing the "
+ "elements of this <a>dart-core.Iterable</a>.</p>\n<p>The elements are in "
+ "iteration order. The list is fixed-length\nif "
+ "<a>dart-core.Set.toList.growable</a> is false.</p>";
+const _TO_LIST_COMMENT_FROM = "dart-core.Iterable.toList";
diff --git a/pkg/docgen/test/multi_library_code/lib/root_lib.dart b/pkg/docgen/test/multi_library_code/lib/root_lib.dart
new file mode 100644
index 0000000..74423ab
--- /dev/null
+++ b/pkg/docgen/test/multi_library_code/lib/root_lib.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.
+
+library root_lib;
+
+export 'test_lib.dart';
+export 'test_lib_foo.dart';
+export 'test_lib_bar.dart';
+
+class RootClass {
+ RootClass(int a, int b);
+}
diff --git a/pkg/docgen/test/multi_library_code/lib/test_lib.dart b/pkg/docgen/test/multi_library_code/lib/test_lib.dart
index 06d1ccf..0ecf007c 100644
--- a/pkg/docgen/test/multi_library_code/lib/test_lib.dart
+++ b/pkg/docgen/test/multi_library_code/lib/test_lib.dart
@@ -39,3 +39,22 @@
B sampleMethod(C cInstance) {
throw new UnimplementedError();
}
+
+int positionalDefaultValues([int intConst = INT_CONST,
+ bool boolConst = BOOL_CONST, List listConst = LIST_CONST,
+ String stringConst = STRING_CONST, Map mapConst = MAP_CONST,
+ Map emptyMap = EMPTY_MAP_CONST]) {
+ throw new UnimplementedError();
+}
+
+const int INT_CONST = 42;
+
+const bool BOOL_CONST = true;
+
+const LIST_CONST = const [true, 42, 'Shanna', null, 3.14, const []];
+
+const STRING_CONST = 'Shanna';
+
+const MAP_CONST = const {'a':1, 2: true, 'c': const [1,null,true]};
+
+const EMPTY_MAP_CONST = const {};
diff --git a/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart b/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
index 094d554..6f36d9b 100644
--- a/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
+++ b/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
@@ -5,6 +5,7 @@
library test_lib.bar;
import 'test_lib.dart';
+import 'test_lib_foo.dart';
/*
* Normal comment for class C.
@@ -12,12 +13,18 @@
class C {
}
-/// [input] is of type [C] returns an [A].
-A generateFoo(C input) {
+/// Processes an [input] of type [C] instance for testing.
+///
+/// To eliminate import warnings for [A] and to test typedefs.
+///
+/// It's important that the [List<A>] for param [listOfA] is not empty.
+A testMethod(C input, List<A> listOfA, B aBee) {
throw 'noop';
}
-/// Processes a [C] instance for testing.
+/// Processes an [input] of type [C] instance for testing.
///
/// To eliminate import warnings for [A] and to test typedefs.
-typedef A AnATransformer(C other);
+///
+/// It's important that the [List<A>] for param [listOfA] is not empty.
+typedef A testTypedef(C other, List<A> listOfA, B aBee);
diff --git a/pkg/docgen/test/multi_library_test.dart b/pkg/docgen/test/multi_library_test.dart
index ceee380..3ff966d 100644
--- a/pkg/docgen/test/multi_library_test.dart
+++ b/pkg/docgen/test/multi_library_test.dart
@@ -8,8 +8,7 @@
import 'package:unittest/unittest.dart';
import '../lib/docgen.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
+import '../lib/src/exports/mirrors_util.dart' as dart2js_util;
import 'util.dart';
diff --git a/pkg/docgen/test/single_library_test.dart b/pkg/docgen/test/single_library_test.dart
index 295cede..31f3a5e 100644
--- a/pkg/docgen/test/single_library_test.dart
+++ b/pkg/docgen/test/single_library_test.dart
@@ -9,9 +9,8 @@
import 'package:path/path.dart' as path;
import 'package:unittest/unittest.dart';
+import '../lib/src/exports/mirrors_util.dart' as dart2js_util;
import '../lib/docgen.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
- as dart2js_util;
const String DART_LIBRARY = '''
library test;
diff --git a/pkg/docgen/test/typedef_test.dart b/pkg/docgen/test/typedef_test.dart
index 9911f08..ca1dc6a 100644
--- a/pkg/docgen/test/typedef_test.dart
+++ b/pkg/docgen/test/typedef_test.dart
@@ -28,21 +28,20 @@
});
schedule(() {
- var path = p.join(d.defaultRoot, 'docs', 'test_lib-bar.json');
- var dartCoreJson = new File(path).readAsStringSync();
+ var path = p.join(d.defaultRoot, 'docs', 'root_lib.json');
+ var rootLibJson = new File(path).readAsStringSync();
- var testLibBar = JSON.decode(dartCoreJson) as Map<String, dynamic>;
+ var rootLib = JSON.decode(rootLibJson) as Map<String, dynamic>;
//
// Validate function doc references
//
- var generateFoo = testLibBar['functions']['methods']['generateFoo']
+ var testMethod = rootLib['functions']['methods']['testMethod']
as Map<String, dynamic>;
- expect(generateFoo['comment'], '<p><a>test_lib-bar.generateFoo.input</a> '
- 'is of type <a>test_lib-bar.C</a> returns an <a>test_lib.A</a>.</p>');
+ expect(testMethod['comment'], _TEST_METHOD_COMMENT);
- var classes = testLibBar['classes'] as Map<String, dynamic>;
+ var classes = rootLib['classes'] as Map<String, dynamic>;
expect(classes, hasLength(3));
@@ -50,16 +49,42 @@
expect(classes['error'], isList);
var typeDefs = classes['typedef'] as Map<String, dynamic>;
- var comparator = typeDefs['AnATransformer'] as Map<String, dynamic>;
+ var comparator = typeDefs['testTypedef'] as Map<String, dynamic>;
- var expectedPreview = '<p>Processes a [C] instance for testing.</p>';
+ expect(comparator['preview'], _TEST_TYPEDEF_PREVIEW);
- expect(comparator['preview'], expectedPreview);
+ expect(comparator['comment'], _TEST_TYPEDEF_COMMENT);
+ });
- var expectedComment = expectedPreview + '\n'
- '<p>To eliminate import warnings for [A] and to test typedefs.</p>';
+ schedule(() {
+ var path = p.join(d.defaultRoot, 'docs', 'root_lib.RootClass.json');
+ var rootClassJson = new File(path).readAsStringSync();
- expect(comparator['comment'], expectedComment);
+ var rootClass = JSON.decode(rootClassJson) as Map<String, dynamic>;
+
+ var defaultCtor = rootClass['methods']['constructors'][''] as Map;
+
+ expect(defaultCtor['qualifiedName'], 'root_lib.RootClass.RootClass-');
});
});
}
+
+// TOOD: [List<A>] is not formatted correctly - issue 16771
+const _TEST_METHOD_COMMENT = '<p>Processes an '
+ '<a>root_lib.testMethod.input</a> of type <a>root_lib.C</a> '
+ 'instance for testing.</p>\n<p>To eliminate import warnings for '
+ '<a>root_lib.A</a> and to test typedefs.</p>\n<p>It\'s important that the'
+ ' <a>dart-core</a><A> for param <a>root_lib.testMethod.listOfA</a> '
+ 'is not empty.</p>';
+
+// TODO: [input] is not turned into a param refenece
+const _TEST_TYPEDEF_PREVIEW = '<p>Processes an input of type '
+ '<a>root_lib.C</a> instance for testing.</p>';
+
+// TOOD: [List<A>] is not formatted correctly - issue 16771
+// TODO: [listOfA] is not turned into a param reference
+final _TEST_TYPEDEF_COMMENT = _TEST_TYPEDEF_PREVIEW + '\n<p>To eliminate import'
+ ' warnings for <a>root_lib.A</a> and to test typedefs.</p>\n<p>It\'s '
+ 'important that the <a>dart-core</a><A> for param listOfA is not '
+ 'empty.</p>';
+
diff --git a/pkg/intl/lib/date_symbol_data_file.dart b/pkg/intl/lib/date_symbol_data_file.dart
index a4a67fe..1e641df 100644
--- a/pkg/intl/lib/date_symbol_data_file.dart
+++ b/pkg/intl/lib/date_symbol_data_file.dart
@@ -7,16 +7,19 @@
* locale data from files in the file system.
*/
-library date_symbol_data_json;
+library date_symbol_data_file;
import 'dart:async';
-import "date_symbols.dart";
-import "src/lazy_locale_data.dart";
-import 'src/date_format_internal.dart';
-import 'src/file_data_reader.dart';
+
import 'package:path/path.dart' as path;
-part "src/data/dates/localeList.dart";
+import 'date_symbols.dart';
+import 'src/data/dates/locale_list.dart';
+import 'src/date_format_internal.dart';
+import 'src/file_data_reader.dart';
+import 'src/lazy_locale_data.dart';
+
+export 'src/data/dates/locale_list.dart';
/**
* This should be called for at least one [locale] before any date formatting
diff --git a/pkg/intl/lib/date_symbol_data_http_request.dart b/pkg/intl/lib/date_symbol_data_http_request.dart
index 3fe56c6..4c54147 100644
--- a/pkg/intl/lib/date_symbol_data_http_request.dart
+++ b/pkg/intl/lib/date_symbol_data_http_request.dart
@@ -6,16 +6,18 @@
* This file should be imported, along with date_format.dart in order to read
* locale data via http requests to a web server..
*/
-library date_symbol_data_json;
+library date_symbol_data_http_request;
import 'dart:async';
+
import 'date_symbols.dart';
-import 'src/lazy_locale_data.dart';
+import 'intl.dart';
+import 'src/data/dates/locale_list.dart';
import 'src/date_format_internal.dart';
import 'src/http_request_data_reader.dart';
-import 'intl.dart';
+import 'src/lazy_locale_data.dart';
-part "src/data/dates/localeList.dart";
+export 'src/data/dates/locale_list.dart';
/**
* This should be called for at least one [locale] before any date formatting
diff --git a/pkg/intl/lib/date_symbol_data_local.dart b/pkg/intl/lib/date_symbol_data_local.dart
index a8ce2b8..9082b0a 100644
--- a/pkg/intl/lib/date_symbol_data_local.dart
+++ b/pkg/intl/lib/date_symbol_data_local.dart
@@ -14,13 +14,13 @@
* removed after those changes land to CLDR.
*/
-library date_symbol_data;
+library date_symbol_data_local;
import 'dart:async';
import "date_symbols.dart";
-import "src/date_format_internal.dart";
import "date_time_patterns.dart";
+import "src/date_format_internal.dart";
/**
* This should be called for at least one [locale] before any date
diff --git a/pkg/intl/lib/intl.dart b/pkg/intl/lib/intl.dart
index 59357c5..2e0cab52 100644
--- a/pkg/intl/lib/intl.dart
+++ b/pkg/intl/lib/intl.dart
@@ -235,7 +235,7 @@
// We treat C as a special case, and assume it wants en_ISO for formatting.
// TODO(alanknight): en_ISO is probably not quite right for the C/Posix
// locale for formatting. Consider adding C to the formats database.
- if (aLocale == null) return systemLocale;
+ if (aLocale == null) return getCurrentLocale();
if (aLocale == "C") return "en_ISO";
if ((aLocale.length < 5) || (aLocale.length > 6)) return aLocale;
if (aLocale[2] != '-' && (aLocale[2] != '_')) return aLocale;
diff --git a/pkg/intl/lib/src/data/dates/localeList.dart b/pkg/intl/lib/src/data/dates/locale_list.dart
similarity index 92%
rename from pkg/intl/lib/src/data/dates/localeList.dart
rename to pkg/intl/lib/src/data/dates/locale_list.dart
index 6325924..0e74f64 100644
--- a/pkg/intl/lib/src/data/dates/localeList.dart
+++ b/pkg/intl/lib/src/data/dates/locale_list.dart
@@ -2,10 +2,10 @@
// for details. All rights reserved. Use of this sourcecode is governed by a
// BSD-style license that can be found in the LICENSE file.
-part of date_symbol_data_json;
+library intl.locale_list;
/// Hard-coded list of all available locales for dates.
-final availableLocalesForDateFormatting = const ["en_ISO",
+const availableLocalesForDateFormatting = const ["en_ISO",
"af",
"am",
"ar",
@@ -84,4 +84,4 @@
"zh_CN",
"zh_HK",
"zh_TW",
- "zu"];
\ No newline at end of file
+ "zu"];
diff --git a/pkg/mock/lib/mock.dart b/pkg/mock/lib/mock.dart
index b95f8f3..768cae2 100644
--- a/pkg/mock/lib/mock.dart
+++ b/pkg/mock/lib/mock.dart
@@ -105,1429 +105,21 @@
library mock;
-import 'dart:mirrors';
-import 'dart:collection' show LinkedHashMap;
+export 'src/action.dart';
+export 'src/behavior.dart';
+export 'src/call_matcher.dart';
+export 'src/log_entry.dart';
+export 'src/log_entry_list.dart';
+export 'src/mock.dart';
+export 'src/responder.dart';
+export 'src/result_matcher.dart';
+export 'src/result_set_matcher.dart';
+export 'src/times_matcher.dart';
-import 'package:matcher/matcher.dart';
+import 'src/log_entry_list.dart';
/**
- * The error formatter for mocking is a bit different from the default one
- * for unit testing; instead of the third argument being a 'reason'
- * it is instead a [signature] describing the method signature filter
- * that was used to select the logs that were verified.
+ * [sharedLog] is not used in this library and is deprecated.
*/
-String _mockingErrorFormatter(actual, Matcher matcher, String signature,
- Map matchState, bool verbose) {
- var description = new StringDescription();
- description.add('Expected ${signature} ').addDescriptionOf(matcher).
- add('\n but: ');
- matcher.describeMismatch(actual, description, matchState, verbose).add('.');
- return description.toString();
-}
-
-/**
- * The failure handler for the [expect()] calls that occur in [verify()]
- * methods in the mock objects. This calls the real failure handler used
- * by the unit test library after formatting the error message with
- * the custom formatter.
- */
-class _MockFailureHandler implements FailureHandler {
- FailureHandler proxy;
- _MockFailureHandler(this.proxy);
- void fail(String reason) {
- proxy.fail(reason);
- }
- void failMatch(actual, Matcher matcher, String reason,
- Map matchState, bool verbose) {
- proxy.fail(_mockingErrorFormatter(actual, matcher, reason,
- matchState, verbose));
- }
-}
-
-_MockFailureHandler _mockFailureHandler = null;
-
-/** Sentinel value for representing no argument. */
-class _Sentinel {
- const _Sentinel();
-}
-const _noArg = const _Sentinel();
-
-/** The ways in which a call to a mock method can be handled. */
-class Action {
- /** Do nothing (void method) */
- static const IGNORE = const Action._('IGNORE');
-
- /** Return a supplied value. */
- static const RETURN = const Action._('RETURN');
-
- /** Throw a supplied value. */
- static const THROW = const Action._('THROW');
-
- /** Call a supplied function. */
- static const PROXY = const Action._('PROXY');
-
- const Action._(this.name);
-
- final String name;
-
- String toString() => 'Action: $name';
-}
-
-/**
- * The behavior of a method call in the mock library is specified
- * with [Responder]s. A [Responder] has a [value] to throw
- * or return (depending on the type of [action]),
- * and can either be one-shot, multi-shot, or infinitely repeating,
- * depending on the value of [count (1, greater than 1, or 0 respectively).
- */
-class Responder {
- final Object value;
- final Action action;
- int count;
- Responder(this.value, [this.count = 1, this.action = Action.RETURN]);
-}
-
-/**
- * A [CallMatcher] is a special matcher used to match method calls (i.e.
- * a method name and set of arguments). It is not a [Matcher] like the
- * unit test [Matcher], but instead represents a method name and a
- * collection of [Matcher]s, one per argument, that will be applied
- * to the parameters to decide if the method call is a match.
- */
-class CallMatcher {
- Matcher nameFilter;
- List<Matcher> argMatchers;
-
- /**
- * Constructor for [CallMatcher]. [name] can be null to
- * match anything, or a literal [String], a predicate [Function],
- * or a [Matcher]. The various arguments can be scalar values or
- * [Matcher]s.
- */
- CallMatcher([name,
- arg0 = _noArg,
- arg1 = _noArg,
- arg2 = _noArg,
- arg3 = _noArg,
- arg4 = _noArg,
- arg5 = _noArg,
- arg6 = _noArg,
- arg7 = _noArg,
- arg8 = _noArg,
- arg9 = _noArg]) {
- if (name == null) {
- nameFilter = anything;
- } else {
- nameFilter = wrapMatcher(name);
- }
- argMatchers = new List<Matcher>();
- if (identical(arg0, _noArg)) return;
- argMatchers.add(wrapMatcher(arg0));
- if (identical(arg1, _noArg)) return;
- argMatchers.add(wrapMatcher(arg1));
- if (identical(arg2, _noArg)) return;
- argMatchers.add(wrapMatcher(arg2));
- if (identical(arg3, _noArg)) return;
- argMatchers.add(wrapMatcher(arg3));
- if (identical(arg4, _noArg)) return;
- argMatchers.add(wrapMatcher(arg4));
- if (identical(arg5, _noArg)) return;
- argMatchers.add(wrapMatcher(arg5));
- if (identical(arg6, _noArg)) return;
- argMatchers.add(wrapMatcher(arg6));
- if (identical(arg7, _noArg)) return;
- argMatchers.add(wrapMatcher(arg7));
- if (identical(arg8, _noArg)) return;
- argMatchers.add(wrapMatcher(arg8));
- if (identical(arg9, _noArg)) return;
- argMatchers.add(wrapMatcher(arg9));
- }
-
- /**
- * We keep our behavior specifications in a Map, which is keyed
- * by the [CallMatcher]. To make the keys unique and to get a
- * descriptive value for the [CallMatcher] we have this override
- * of [toString()].
- */
- String toString() {
- Description d = new StringDescription();
- d.addDescriptionOf(nameFilter);
- // If the nameFilter was a simple string - i.e. just a method name -
- // strip the quotes to make this more natural in appearance.
- if (d.toString()[0] == "'") {
- d.replace(d.toString().substring(1, d.toString().length - 1));
- }
- d.add('(');
- for (var i = 0; i < argMatchers.length; i++) {
- if (i > 0) d.add(', ');
- d.addDescriptionOf(argMatchers[i]);
- }
- d.add(')');
- return d.toString();
- }
-
- /**
- * Given a [method] name and list of [arguments], return true
- * if it matches this [CallMatcher.
- */
- bool matches(String method, List arguments) {
- var matchState = {};
- if (!nameFilter.matches(method, matchState)) {
- return false;
- }
- var numArgs = (arguments == null) ? 0 : arguments.length;
- if (numArgs < argMatchers.length) {
- throw new Exception("Less arguments than matchers for $method.");
- }
- for (var i = 0; i < argMatchers.length; i++) {
- if (!argMatchers[i].matches(arguments[i], matchState)) {
- return false;
- }
- }
- return true;
- }
-}
-
-/**
- * Returns a [CallMatcher] for the specified signature. [method] can be
- * null to match anything, or a literal [String], a predicate [Function],
- * or a [Matcher]. The various arguments can be scalar values or [Matcher]s.
- * To match getters and setters, use "get " and "set " prefixes on the names.
- * For example, for a property "foo", you could use "get foo" and "set foo"
- * as literal string arguments to callsTo to match the getter and setter
- * of "foo".
- */
-CallMatcher callsTo([method,
- arg0 = _noArg,
- arg1 = _noArg,
- arg2 = _noArg,
- arg3 = _noArg,
- arg4 = _noArg,
- arg5 = _noArg,
- arg6 = _noArg,
- arg7 = _noArg,
- arg8 = _noArg,
- arg9 = _noArg]) {
- return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8, arg9);
-}
-
-/**
- * A [Behavior] represents how a [Mock] will respond to one particular
- * type of method call.
- */
-class Behavior {
- CallMatcher matcher; // The method call matcher.
- List<Responder> actions; // The values to return/throw or proxies to call.
- bool logging = true;
-
- Behavior (this.matcher) {
- actions = new List<Responder>();
- }
-
- /**
- * Adds a [Responder] that returns a [value] for [count] calls
- * (1 by default).
- */
- Behavior thenReturn(value, [count = 1]) {
- actions.add(new Responder(value, count, Action.RETURN));
- return this; // For chaining calls.
- }
-
- /** Adds a [Responder] that repeatedly returns a [value]. */
- Behavior alwaysReturn(value) {
- return thenReturn(value, 0);
- }
-
- /**
- * Adds a [Responder] that throws [value] [count]
- * times (1 by default).
- */
- Behavior thenThrow(value, [count = 1]) {
- actions.add(new Responder(value, count, Action.THROW));
- return this; // For chaining calls.
- }
-
- /** Adds a [Responder] that throws [value] endlessly. */
- Behavior alwaysThrow(value) {
- return thenThrow(value, 0);
- }
-
- /**
- * [thenCall] creates a proxy Responder, that is called [count]
- * times (1 by default; 0 is used for unlimited calls, and is
- * exposed as [alwaysCall]). [value] is the function that will
- * be called with the same arguments that were passed to the
- * mock. Proxies can be used to wrap real objects or to define
- * more complex return/throw behavior. You could even (if you
- * wanted) use proxies to emulate the behavior of thenReturn;
- * e.g.:
- *
- * m.when(callsTo('foo')).thenReturn(0)
- *
- * is equivalent to:
- *
- * m.when(callsTo('foo')).thenCall(() => 0)
- */
- Behavior thenCall(value, [count = 1]) {
- actions.add(new Responder(value, count, Action.PROXY));
- return this; // For chaining calls.
- }
-
- /** Creates a repeating proxy call. */
- Behavior alwaysCall(value) {
- return thenCall(value, 0);
- }
-
- /** Returns true if a method call matches the [Behavior]. */
- bool matches(String method, List args) => matcher.matches(method, args);
-
- /** Returns the [matcher]'s representation. */
- String toString() => matcher.toString();
-}
-
-/**
- * Every call to a [Mock] object method is logged. The logs are
- * kept in instances of [LogEntry].
- */
-class LogEntry {
- /** The time of the event. */
- DateTime time;
-
- /** The mock object name, if any. */
- final String mockName;
-
- /** The method name. */
- final String methodName;
-
- /** The parameters. */
- final List args;
-
- /** The behavior that resulted. */
- final Action action;
-
- /** The value that was returned (if no throw). */
- final value;
-
- LogEntry(this.mockName, this.methodName,
- this.args, this.action, [this.value]) {
- time = new DateTime.now();
- }
-
- String _pad2(int val) => (val >= 10 ? '$val' : '0$val');
-
- String toString([DateTime baseTime]) {
- Description d = new StringDescription();
- if (baseTime == null) {
- // Show absolute time.
- d.add('${time.hour}:${_pad2(time.minute)}:'
- '${_pad2(time.second)}.${time.millisecond}> ');
- } else {
- // Show relative time.
- int delta = time.millisecondsSinceEpoch - baseTime.millisecondsSinceEpoch;
- int secs = delta ~/ 1000;
- int msecs = delta % 1000;
- d.add('$secs.$msecs> ');
- }
- d.add('${_qualifiedName(mockName, methodName)}(');
- if (args != null) {
- for (var i = 0; i < args.length; i++) {
- if (i != 0) d.add(', ');
- d.addDescriptionOf(args[i]);
- }
- }
- d.add(') ${action == Action.THROW ? "threw" : "returned"} ');
- d.addDescriptionOf(value);
- return d.toString();
- }
-}
-
-/** Utility function for optionally qualified method names */
-String _qualifiedName(owner, String method) {
- if (owner == null || identical(owner, anything)) {
- return method;
- } else if (owner is Matcher) {
- Description d = new StringDescription();
- d.addDescriptionOf(owner);
- d.add('.');
- d.add(method);
- return d.toString();
- } else {
- return '$owner.$method';
- }
-}
-
-/**
-* [StepValidator]s are used by [stepwiseValidate] in [LogEntryList], which
-* iterates through the list and call the [StepValidator] function with the
-* log [List] and position. The [StepValidator] should return the number of
-* positions to advance upon success, or zero upon failure. When zero is
-* returned an error is reported.
-*/
-typedef int StepValidator(List<LogEntry> logs, int pos);
-
-/**
- * We do verification on a list of [LogEntry]s. To allow chaining
- * of calls to verify, we encapsulate such a list in the [LogEntryList]
- * class.
- */
-class LogEntryList {
- String filter;
- List<LogEntry> logs;
- LogEntryList([this.filter]) {
- logs = new List<LogEntry>();
- }
-
- /** Add a [LogEntry] to the log. */
- add(LogEntry entry) => logs.add(entry);
-
- /** Get the first entry, or null if no entries. */
- get first => (logs == null || logs.length == 0) ? null : logs[0];
-
- /** Get the last entry, or null if no entries. */
- get last => (logs == null || logs.length == 0) ? null : logs.last;
-
- /** Creates a LogEntry predicate function from the argument. */
- Function _makePredicate(arg) {
- if (arg == null) {
- return (e) => true;
- } else if (arg is CallMatcher) {
- return (e) => arg.matches(e.methodName, e.args);
- } else if (arg is Function) {
- return arg;
- } else {
- throw new Exception("Invalid argument to _makePredicate.");
- }
- }
-
- /**
- * Create a new [LogEntryList] consisting of [LogEntry]s from
- * this list that match the specified [mockNameFilter] and [logFilter].
- * [mockNameFilter] can be null, a [String], a predicate [Function],
- * or a [Matcher]. If [mockNameFilter] is null, this is the same as
- * [anything].
- * If [logFilter] is null, all entries in the log will be returned.
- * Otherwise [logFilter] should be a [CallMatcher] or predicate function
- * that takes a [LogEntry] and returns a bool.
- * If [destructive] is true, the log entries are removed from the
- * original list.
- */
- LogEntryList getMatches([mockNameFilter,
- logFilter,
- Matcher actionMatcher,
- bool destructive = false]) {
- if (mockNameFilter == null) {
- mockNameFilter = anything;
- } else {
- mockNameFilter = wrapMatcher(mockNameFilter);
- }
- Function entryFilter = _makePredicate(logFilter);
- String filterName = _qualifiedName(mockNameFilter, logFilter.toString());
- LogEntryList rtn = new LogEntryList(filterName);
- var matchState = {};
- for (var i = 0; i < logs.length; i++) {
- LogEntry entry = logs[i];
- if (mockNameFilter.matches(entry.mockName, matchState) &&
- entryFilter(entry)) {
- if (actionMatcher == null ||
- actionMatcher.matches(entry, matchState)) {
- rtn.add(entry);
- if (destructive) {
- int startIndex = i--;
- logs.removeRange(startIndex, startIndex + 1);
- }
- }
- }
- }
- return rtn;
- }
-
- /** Apply a unit test [Matcher] to the [LogEntryList]. */
- LogEntryList verify(Matcher matcher) {
- if (_mockFailureHandler == null) {
- _mockFailureHandler =
- new _MockFailureHandler(getOrCreateExpectFailureHandler());
- }
- expect(logs, matcher, reason:filter, failureHandler: _mockFailureHandler);
- return this;
- }
-
- /**
- * Iterate through the list and call the [validator] function with the
- * log [List] and position. The [validator] should return the number of
- * positions to advance upon success, or zero upon failure. When zero is
- * returned an error is reported. [reason] can be used to provide a
- * more descriptive failure message. If a failure occurred false will be
- * returned (unless the failure handler itself threw an exception);
- * otherwise true is returned.
- * The use case here is to perform more complex validations; for example
- * we may want to assert that the return value from some function is
- * later used as a parameter to a following function. If we filter the logs
- * to include just these two functions we can write a simple validator to
- * do this check.
- */
- bool stepwiseValidate(StepValidator validator, [String reason = '']) {
- if (_mockFailureHandler == null) {
- _mockFailureHandler =
- new _MockFailureHandler(getOrCreateExpectFailureHandler());
- }
- var i = 0;
- while (i < logs.length) {
- var n = validator(logs, i);
- if (n == 0) {
- if (reason.length > 0) {
- reason = ': $reason';
- }
- _mockFailureHandler.fail("Stepwise validation failed at $filter "
- "position $i$reason");
- return false;
- } else {
- i += n;
- }
- }
- return true;
- }
-
- /**
- * Turn the logs into human-readable text. If [baseTime] is specified
- * then each entry is prefixed with the offset from that time in
- * milliseconds; otherwise the time of day is used.
- */
- String toString([DateTime baseTime]) {
- String s = '';
- for (var e in logs) {
- s = '$s${e.toString(baseTime)}\n';
- }
- return s;
- }
-
- /**
- * Find the first log entry that satisfies [logFilter] and
- * return its position. A search [start] position can be provided
- * to allow for repeated searches. [logFilter] can be a [CallMatcher],
- * or a predicate function that takes a [LogEntry] argument and returns
- * a bool. If [logFilter] is null, it will match any [LogEntry].
- * If no entry is found, then [failureReturnValue] is returned.
- * After each check the position is updated by [skip], so using
- * [skip] of -1 allows backward searches, using a [skip] of 2 can
- * be used to check pairs of adjacent entries, and so on.
- */
- int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
- skip = 1]) {
- logFilter = _makePredicate(logFilter);
- int pos = start;
- while (pos >= 0 && pos < logs.length) {
- if (logFilter(logs[pos])) {
- return pos;
- }
- pos += skip;
- }
- return failureReturnValue;
- }
-
- /**
- * Returns log events that happened up to the first one that
- * satisfies [logFilter]. If [inPlace] is true, then returns
- * this LogEntryList after removing the from the first satisfier;
- * onwards otherwise a new list is created. [description]
- * is used to create a new name for the resulting list.
- * [defaultPosition] is used as the index of the matching item in
- * the case that no match is found.
- */
- LogEntryList _head(logFilter, bool inPlace,
- String description, int defaultPosition) {
- if (filter != null) {
- description = '$filter $description';
- }
- int pos = findLogEntry(logFilter, 0, defaultPosition);
- if (inPlace) {
- if (pos < logs.length) {
- logs.removeRange(pos, logs.length);
- }
- filter = description;
- return this;
- } else {
- LogEntryList newList = new LogEntryList(description);
- for (var i = 0; i < pos; i++) {
- newList.logs.add(logs[i]);
- }
- return newList;
- }
- }
-
- /**
- * Returns log events that happened from the first one that
- * satisfies [logFilter]. If [inPlace] is true, then returns
- * this LogEntryList after removing the entries up to the first
- * satisfier; otherwise a new list is created. [description]
- * is used to create a new name for the resulting list.
- * [defaultPosition] is used as the index of the matching item in
- * the case that no match is found.
- */
- LogEntryList _tail(logFilter, bool inPlace,
- String description, int defaultPosition) {
- if (filter != null) {
- description = '$filter $description';
- }
- int pos = findLogEntry(logFilter, 0, defaultPosition);
- if (inPlace) {
- if (pos > 0) {
- logs.removeRange(0, pos);
- }
- filter = description;
- return this;
- } else {
- LogEntryList newList = new LogEntryList(description);
- while (pos < logs.length) {
- newList.logs.add(logs[pos++]);
- }
- return newList;
- }
- }
-
- /**
- * Returns log events that happened after [when]. If [inPlace]
- * is true, then it returns this LogEntryList after removing
- * the entries that happened up to [when]; otherwise a new
- * list is created.
- */
- LogEntryList after(DateTime when, [bool inPlace = false]) =>
- _tail((e) => e.time.isAfter(when), inPlace, 'after $when', logs.length);
-
- /**
- * Returns log events that happened from [when] onwards. If
- * [inPlace] is true, then it returns this LogEntryList after
- * removing the entries that happened before [when]; otherwise
- * a new list is created.
- */
- LogEntryList from(DateTime when, [bool inPlace = false]) =>
- _tail((e) => !e.time.isBefore(when), inPlace, 'from $when', logs.length);
-
- /**
- * Returns log events that happened until [when]. If [inPlace]
- * is true, then it returns this LogEntryList after removing
- * the entries that happened after [when]; otherwise a new
- * list is created.
- */
- LogEntryList until(DateTime when, [bool inPlace = false]) =>
- _head((e) => e.time.isAfter(when), inPlace, 'until $when', logs.length);
-
- /**
- * Returns log events that happened before [when]. If [inPlace]
- * is true, then it returns this LogEntryList after removing
- * the entries that happened from [when] onwards; otherwise a new
- * list is created.
- */
- LogEntryList before(DateTime when, [bool inPlace = false]) =>
- _head((e) => !e.time.isBefore(when),
- inPlace,
- 'before $when',
- logs.length);
-
- /**
- * Returns log events that happened after [logEntry]'s time.
- * If [inPlace] is true, then it returns this LogEntryList after
- * removing the entries that happened up to [when]; otherwise a new
- * list is created. If [logEntry] is null the current time is used.
- */
- LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
- after(logEntry == null ? new DateTime.now() : logEntry.time);
-
- /**
- * Returns log events that happened from [logEntry]'s time onwards.
- * If [inPlace] is true, then it returns this LogEntryList after
- * removing the entries that happened before [when]; otherwise
- * a new list is created. If [logEntry] is null the current time is used.
- */
- LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
- from(logEntry == null ? new DateTime.now() : logEntry.time);
-
- /**
- * Returns log events that happened until [logEntry]'s time. If
- * [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened after [when]; otherwise a new
- * list is created. If [logEntry] is null the epoch time is used.
- */
- LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
- until(logEntry == null ?
- new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
-
- /**
- * Returns log events that happened before [logEntry]'s time. If
- * [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened from [when] onwards; otherwise a new
- * list is created. If [logEntry] is null the epoch time is used.
- */
- LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
- before(logEntry == null ?
- new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
-
- /**
- * Returns log events that happened after the first event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened earlier; otherwise a new list is created.
- */
- LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
- afterEntry(segment.first, inPlace);
-
- /**
- * Returns log events that happened after the last event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened earlier; otherwise a new list is created.
- */
- LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
- afterEntry(segment.last, inPlace);
-
- /**
- * Returns log events that happened from the time of the first event in
- * [segment] onwards. If [inPlace] is true, then it returns this
- * LogEntryList after removing the earlier entries; otherwise a new list
- * is created.
- */
- LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
- fromEntry(segment.first, inPlace);
-
- /**
- * Returns log events that happened from the time of the last event in
- * [segment] onwards. If [inPlace] is true, then it returns this
- * LogEntryList after removing the earlier entries; otherwise a new list
- * is created.
- */
- LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
- fromEntry(segment.last, inPlace);
-
- /**
- * Returns log events that happened until the first event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened later; otherwise a new list is created.
- */
- LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
- untilEntry(segment.first, inPlace);
-
- /**
- * Returns log events that happened until the last event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened later; otherwise a new list is created.
- */
- LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
- untilEntry(segment.last, inPlace);
-
- /**
- * Returns log events that happened before the first event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened later; otherwise a new list is created.
- */
- LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
- beforeEntry(segment.first, inPlace);
-
- /**
- * Returns log events that happened before the last event in [segment].
- * If [inPlace] is true, then it returns this LogEntryList after removing
- * the entries that happened later; otherwise a new list is created.
- */
- LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
- beforeEntry(segment.last, inPlace);
-
- /**
- * Iterate through the LogEntryList looking for matches to the entries
- * in [keys]; for each match found the closest [distance] neighboring log
- * entries that match [mockNameFilter] and [logFilter] will be included in
- * the result. If [isPreceding] is true we use the neighbors that precede
- * the matched entry; else we use the neighbors that followed.
- * If [includeKeys] is true then the entries in [keys] that resulted in
- * entries in the output list are themselves included in the output list. If
- * [distance] is zero then all matches are included.
- */
- LogEntryList _neighboring(bool isPreceding,
- LogEntryList keys,
- mockNameFilter,
- logFilter,
- int distance,
- bool includeKeys) {
- String filterName = 'Calls to '
- '${_qualifiedName(mockNameFilter, logFilter.toString())} '
- '${isPreceding?"preceding":"following"} ${keys.filter}';
-
- LogEntryList rtn = new LogEntryList(filterName);
-
- // Deal with the trivial case.
- if (logs.length == 0 || keys.logs.length == 0) {
- return rtn;
- }
-
- // Normalize the mockNameFilter and logFilter values.
- if (mockNameFilter == null) {
- mockNameFilter = anything;
- } else {
- mockNameFilter = wrapMatcher(mockNameFilter);
- }
- logFilter = _makePredicate(logFilter);
-
- // The scratch list is used to hold matching entries when we
- // are doing preceding neighbors. The remainingCount is used to
- // keep track of how many matching entries we can still add in the
- // current segment (0 if we are doing doing following neighbors, until
- // we get our first key match).
- List scratch = null;
- int remainingCount = 0;
- if (isPreceding) {
- scratch = new List();
- remainingCount = logs.length;
- }
-
- var keyIterator = keys.logs.iterator;
- keyIterator.moveNext();
- LogEntry keyEntry = keyIterator.current;
- Map matchState = {};
-
- for (LogEntry logEntry in logs) {
- // If we have a log entry match, copy the saved matches from the
- // scratch buffer into the return list, as well as the matching entry,
- // if appropriate, and reset the scratch buffer. Continue processing
- // from the next key entry.
- if (keyEntry == logEntry) {
- if (scratch != null) {
- int numToCopy = scratch.length;
- if (distance > 0 && distance < numToCopy) {
- numToCopy = distance;
- }
- for (var i = scratch.length - numToCopy; i < scratch.length; i++) {
- rtn.logs.add(scratch[i]);
- }
- scratch.clear();
- } else {
- remainingCount = distance > 0 ? distance : logs.length;
- }
- if (includeKeys) {
- rtn.logs.add(keyEntry);
- }
- if (keyIterator.moveNext()) {
- keyEntry = keyIterator.current;
- } else if (isPreceding) { // We're done.
- break;
- }
- } else if (remainingCount > 0 &&
- mockNameFilter.matches(logEntry.mockName, matchState) &&
- logFilter(logEntry)) {
- if (scratch != null) {
- scratch.add(logEntry);
- } else {
- rtn.logs.add(logEntry);
- --remainingCount;
- }
- }
- }
- return rtn;
- }
-
- /**
- * Iterate through the LogEntryList looking for matches to the entries
- * in [keys]; for each match found the closest [distance] prior log entries
- * that match [mocknameFilter] and [logFilter] will be included in the result.
- * If [includeKeys] is true then the entries in [keys] that resulted in
- * entries in the output list are themselves included in the output list. If
- * [distance] is zero then all matches are included.
- *
- * The idea here is that you could find log entries that are related to
- * other logs entries in some temporal sense. For example, say we have a
- * method commit() that returns -1 on failure. Before commit() gets called
- * the value being committed is created by process(). We may want to find
- * the calls to process() that preceded calls to commit() that failed.
- * We could do this with:
- *
- * print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
- * logFilter: callsTo('process')).toString());
- *
- * We might want to include the details of the failing calls to commit()
- * to see what parameters were passed in, in which case we would set
- * [includeKeys].
- *
- * As another simple example, say we wanted to know the three method
- * calls that immediately preceded each failing call to commit():
- *
- * print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
- * distance: 3).toString());
- */
- LogEntryList preceding(LogEntryList keys,
- {mockNameFilter: null,
- logFilter: null,
- int distance: 1,
- bool includeKeys: false}) =>
- _neighboring(true, keys, mockNameFilter, logFilter,
- distance, includeKeys);
-
- /**
- * Iterate through the LogEntryList looking for matches to the entries
- * in [keys]; for each match found the closest [distance] subsequent log
- * entries that match [mocknameFilter] and [logFilter] will be included in
- * the result. If [includeKeys] is true then the entries in [keys] that
- * resulted in entries in the output list are themselves included in the
- * output list. If [distance] is zero then all matches are included.
- * See [preceding] for a usage example.
- */
- LogEntryList following(LogEntryList keys,
- {mockNameFilter: null,
- logFilter: null,
- int distance: 1,
- bool includeKeys: false}) =>
- _neighboring(false, keys, mockNameFilter, logFilter,
- distance, includeKeys);
-}
-
-/**
- * [_TimesMatcher]s are used to make assertions about the number of
- * times a method was called.
- */
-class _TimesMatcher extends Matcher {
- final int min, max;
-
- const _TimesMatcher(this.min, [this.max = -1]);
-
- bool matches(logList, Map matchState) => logList.length >= min &&
- (max < 0 || logList.length <= max);
-
- Description describe(Description description) {
- description.add('to be called ');
- if (max < 0) {
- description.add('at least $min');
- } else if (max == min) {
- description.add('$max');
- } else if (min == 0) {
- description.add('at most $max');
- } else {
- description.add('between $min and $max');
- }
- return description.add(' times');
- }
-
- Description describeMismatch(logList, Description mismatchDescription,
- Map matchState, bool verbose) =>
- mismatchDescription.add('was called ${logList.length} times');
-}
-
-/** [happenedExactly] matches an exact number of calls. */
-Matcher happenedExactly(count) {
- return new _TimesMatcher(count, count);
-}
-
-/** [happenedAtLeast] matches a minimum number of calls. */
-Matcher happenedAtLeast(count) {
- return new _TimesMatcher(count);
-}
-
-/** [happenedAtMost] matches a maximum number of calls. */
-Matcher happenedAtMost(count) {
- return new _TimesMatcher(0, count);
-}
-
-/** [neverHappened] matches zero calls. */
-const Matcher neverHappened = const _TimesMatcher(0, 0);
-
-/** [happenedOnce] matches exactly one call. */
-const Matcher happenedOnce = const _TimesMatcher(1, 1);
-
-/** [happenedAtLeastOnce] matches one or more calls. */
-const Matcher happenedAtLeastOnce = const _TimesMatcher(1);
-
-/** [happenedAtMostOnce] matches zero or one call. */
-const Matcher happenedAtMostOnce = const _TimesMatcher(0, 1);
-
-/**
- * [_ResultMatcher]s are used to make assertions about the results
- * of method calls. These can be used as optional parameters to [getLogs].
- */
-class _ResultMatcher extends Matcher {
- final Action action;
- final Matcher value;
-
- const _ResultMatcher(this.action, this.value);
-
- bool matches(item, Map matchState) {
- if (item is! LogEntry) {
- return false;
- }
- // normalize the action; _PROXY is like _RETURN.
- Action eaction = item.action;
- if (eaction == Action.PROXY) {
- eaction = Action.RETURN;
- }
- return (eaction == action && value.matches(item.value, matchState));
- }
-
- Description describe(Description description) {
- description.add(' to ');
- if (action == Action.RETURN || action == Action.PROXY)
- description.add('return ');
- else
- description.add('throw ');
- return description.addDescriptionOf(value);
- }
-
- Description describeMismatch(item, Description mismatchDescription,
- Map matchState, bool verbose) {
- if (item.action == Action.RETURN || item.action == Action.PROXY) {
- mismatchDescription.add('returned ');
- } else {
- mismatchDescription.add('threw ');
- }
- mismatchDescription.add(item.value);
- return mismatchDescription;
- }
-}
-
-/**
- *[returning] matches log entries where the call to a method returned
- * a value that matched [value].
- */
-Matcher returning(value) =>
- new _ResultMatcher(Action.RETURN, wrapMatcher(value));
-
-/**
- *[throwing] matches log entrues where the call to a method threw
- * a value that matched [value].
- */
-Matcher throwing(value) =>
- new _ResultMatcher(Action.THROW, wrapMatcher(value));
-
-/** Special values for use with [_ResultSetMatcher] [frequency]. */
-class _Frequency {
- /** Every call/throw must match */
- static const ALL = const _Frequency._('ALL');
-
- /** At least one call/throw must match. */
- static const SOME = const _Frequency._('SOME');
-
- /** No calls/throws should match. */
- static const NONE = const _Frequency._('NONE');
-
- const _Frequency._(this.name);
-
- final String name;
-}
-
-/**
- * [_ResultSetMatcher]s are used to make assertions about the results
- * of method calls. When filtering an execution log by calling
- * [getLogs], a [LogEntrySet] of matching call logs is returned;
- * [_ResultSetMatcher]s can then assert various things about this
- * (sub)set of logs.
- *
- * We could make this class use _ResultMatcher but it doesn't buy that
- * match and adds some perf hit, so there is some duplication here.
- */
-class _ResultSetMatcher extends Matcher {
- final Action action;
- final Matcher value;
- final _Frequency frequency; // ALL, SOME, or NONE.
-
- const _ResultSetMatcher(this.action, this.value, this.frequency);
-
- bool matches(logList, Map matchState) {
- for (LogEntry entry in logList) {
- // normalize the action; PROXY is like RETURN.
- Action eaction = entry.action;
- if (eaction == Action.PROXY) {
- eaction = Action.RETURN;
- }
- if (eaction == action && value.matches(entry.value, matchState)) {
- if (frequency == _Frequency.NONE) {
- addStateInfo(matchState, {'entry': entry});
- return false;
- } else if (frequency == _Frequency.SOME) {
- return true;
- }
- } else {
- // Mismatch.
- if (frequency == _Frequency.ALL) { // We need just one mismatch to fail.
- addStateInfo(matchState, {'entry': entry});
- return false;
- }
- }
- }
- // If we get here, then if count is _ALL we got all matches and
- // this is success; otherwise we got all mismatched which is
- // success for count == _NONE and failure for count == _SOME.
- return (frequency != _Frequency.SOME);
- }
-
- Description describe(Description description) {
- description.add(' to ');
- description.add(frequency == _Frequency.ALL ? 'alway ' :
- (frequency == _Frequency.NONE ? 'never ' : 'sometimes '));
- if (action == Action.RETURN || action == Action.PROXY)
- description.add('return ');
- else
- description.add('throw ');
- return description.addDescriptionOf(value);
- }
-
- Description describeMismatch(logList, Description mismatchDescription,
- Map matchState, bool verbose) {
- if (frequency != _Frequency.SOME) {
- LogEntry entry = matchState['entry'];
- if (entry.action == Action.RETURN || entry.action == Action.PROXY) {
- mismatchDescription.add('returned');
- } else {
- mismatchDescription.add('threw');
- }
- mismatchDescription.add(' value that ');
- value.describeMismatch(entry.value, mismatchDescription,
- matchState['state'], verbose);
- mismatchDescription.add(' at least once');
- } else {
- mismatchDescription.add('never did');
- }
- return mismatchDescription;
- }
-}
-
-/**
- *[alwaysReturned] asserts that all matching calls to a method returned
- * a value that matched [value].
- */
-Matcher alwaysReturned(value) =>
- new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.ALL);
-
-/**
- *[sometimeReturned] asserts that at least one matching call to a method
- * returned a value that matched [value].
- */
-Matcher sometimeReturned(value) =>
- new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.SOME);
-
-/**
- *[neverReturned] asserts that no matching calls to a method returned
- * a value that matched [value].
- */
-Matcher neverReturned(value) =>
- new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.NONE);
-
-/**
- *[alwaysThrew] asserts that all matching calls to a method threw
- * a value that matched [value].
- */
-Matcher alwaysThrew(value) =>
- new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.ALL);
-
-/**
- *[sometimeThrew] asserts that at least one matching call to a method threw
- * a value that matched [value].
- */
-Matcher sometimeThrew(value) =>
- new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.SOME);
-
-/**
- *[neverThrew] asserts that no matching call to a method threw
- * a value that matched [value].
- */
-Matcher neverThrew(value) =>
- new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE);
-
-/** The shared log used for named mocks. */
+@deprecated
LogEntryList sharedLog = null;
-
-/** The base class for all mocked objects. */
-@proxy
-class Mock {
- /** The mock name. Needed if the log is shared; optional otherwise. */
- final String name;
-
- /** The set of [Behavior]s supported. */
- final LinkedHashMap<String,Behavior> _behaviors;
-
- /** How to handle unknown method calls - swallow or throw. */
- final bool _throwIfNoBehavior;
-
- /** For spys, the real object that we are spying on. */
- final Object _realObject;
-
- /** The [log] of calls made. Only used if [name] is null. */
- LogEntryList log;
-
- /** Whether to create an audit log or not. */
- bool _logging;
-
- bool get logging => _logging;
- set logging(bool value) {
- if (value && log == null) {
- log = new LogEntryList();
- }
- _logging = value;
- }
-
- /**
- * Default constructor. Unknown method calls are allowed and logged,
- * the mock has no name, and has its own log.
- */
- Mock() :
- _throwIfNoBehavior = false, log = null, name = null, _realObject = null,
- _behaviors = new LinkedHashMap<String,Behavior>() {
- logging = true;
- }
-
- /**
- * This constructor makes a mock that has a [name] and possibly uses
- * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods
- * that have no defined behaviors will throw an exception; otherwise they
- * will be allowed and logged (but will not do anything).
- * If [enableLogging] is false, no logging will be done initially (whether
- * or not a [log] is supplied), but [logging] can be set to true later.
- */
- Mock.custom({this.name,
- this.log,
- throwIfNoBehavior: false,
- enableLogging: true})
- : _throwIfNoBehavior = throwIfNoBehavior, _realObject = null,
- _behaviors = new LinkedHashMap<String,Behavior>() {
- if (log != null && name == null) {
- throw new Exception("Mocks with shared logs must have a name.");
- }
- logging = enableLogging;
- }
-
- /**
- * This constructor creates a spy with no user-defined behavior.
- * This is simply a proxy for a real object that passes calls
- * through to that real object but captures an audit trail of
- * calls made to the object that can be queried and validated
- * later.
- */
- Mock.spy(this._realObject, {this.name, this.log})
- : _behaviors = null,
- _throwIfNoBehavior = true {
- logging = true;
- }
-
- /**
- * [when] is used to create a new or extend an existing [Behavior].
- * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for
- * that signature are returned (being created first if needed).
- *
- * Typical use case:
- *
- * mock.when(callsTo(...)).alwaysReturn(...);
- */
- Behavior when(CallMatcher logFilter) {
- String key = logFilter.toString();
- if (!_behaviors.containsKey(key)) {
- Behavior b = new Behavior(logFilter);
- _behaviors[key] = b;
- return b;
- } else {
- return _behaviors[key];
- }
- }
-
- /**
- * This is the handler for method calls. We loop through the list
- * of [Behavior]s, and find the first match that still has return
- * values available, and then do the action specified by that
- * return value. If we find no [Behavior] to apply an exception is
- * thrown.
- */
- noSuchMethod(Invocation invocation) {
- var method = MirrorSystem.getName(invocation.memberName);
- var args = invocation.positionalArguments;
- if (invocation.isGetter) {
- method = 'get $method';
- } else if (invocation.isSetter) {
- method = 'set $method';
- // Remove the trailing '='.
- if (method[method.length-1] == '=') {
- method = method.substring(0, method.length - 1);
- }
- }
- if (_behaviors == null) { // Spy.
- var mirror = reflect(_realObject);
- try {
- var result = mirror.delegate(invocation);
- log.add(new LogEntry(name, method, args, Action.PROXY, result));
- return result;
- } catch (e) {
- log.add(new LogEntry(name, method, args, Action.THROW, e));
- throw e;
- }
- }
- bool matchedMethodName = false;
- Map matchState = {};
- for (String k in _behaviors.keys) {
- Behavior b = _behaviors[k];
- if (b.matcher.nameFilter.matches(method, matchState)) {
- matchedMethodName = true;
- }
- if (b.matches(method, args)) {
- List actions = b.actions;
- if (actions == null || actions.length == 0) {
- continue; // No return values left in this Behavior.
- }
- // Get the first response.
- Responder response = actions[0];
- // If it is exhausted, remove it from the list.
- // Note that for endlessly repeating values, we started the count at
- // 0, so we get a potentially useful value here, which is the
- // (negation of) the number of times we returned the value.
- if (--response.count == 0) {
- actions.removeRange(0, 1);
- }
- // Do the response.
- Action action = response.action;
- var value = response.value;
- if (action == Action.RETURN) {
- if (_logging && b.logging) {
- log.add(new LogEntry(name, method, args, action, value));
- }
- return value;
- } else if (action == Action.THROW) {
- if (_logging && b.logging) {
- log.add(new LogEntry(name, method, args, action, value));
- }
- throw value;
- } else if (action == Action.PROXY) {
- // TODO(gram): Replace all this with:
- // var rtn = reflect(value).apply(invocation.positionalArguments,
- // invocation.namedArguments);
- // once that is supported.
- var rtn;
- switch (args.length) {
- case 0:
- rtn = value();
- break;
- case 1:
- rtn = value(args[0]);
- break;
- case 2:
- rtn = value(args[0], args[1]);
- break;
- case 3:
- rtn = value(args[0], args[1], args[2]);
- break;
- case 4:
- rtn = value(args[0], args[1], args[2], args[3]);
- break;
- case 5:
- rtn = value(args[0], args[1], args[2], args[3], args[4]);
- break;
- case 6:
- rtn = value(args[0], args[1], args[2], args[3],
- args[4], args[5]);
- break;
- case 7:
- rtn = value(args[0], args[1], args[2], args[3],
- args[4], args[5], args[6]);
- break;
- case 8:
- rtn = value(args[0], args[1], args[2], args[3],
- args[4], args[5], args[6], args[7]);
- break;
- case 9:
- rtn = value(args[0], args[1], args[2], args[3],
- args[4], args[5], args[6], args[7], args[8]);
- break;
- case 9:
- rtn = value(args[0], args[1], args[2], args[3],
- args[4], args[5], args[6], args[7], args[8], args[9]);
- break;
- default:
- throw new Exception(
- "Cannot proxy calls with more than 10 parameters.");
- }
- if (_logging && b.logging) {
- log.add(new LogEntry(name, method, args, action, rtn));
- }
- return rtn;
- }
- }
- }
- if (matchedMethodName) {
- // User did specify behavior for this method, but all the
- // actions are exhausted. This is considered an error.
- throw new Exception('No more actions for method '
- '${_qualifiedName(name, method)}.');
- } else if (_throwIfNoBehavior) {
- throw new Exception('No behavior specified for method '
- '${_qualifiedName(name, method)}.');
- }
- // Otherwise user hasn't specified behavior for this method; we don't throw
- // so we can underspecify.
- if (_logging) {
- log.add(new LogEntry(name, method, args, Action.IGNORE));
- }
- }
-
- /** [verifyZeroInteractions] returns true if no calls were made */
- bool verifyZeroInteractions() {
- if (log == null) {
- // This means we created the mock with logging off and have never turned
- // it on, so it doesn't make sense to verify behavior on such a mock.
- throw new
- Exception("Can't verify behavior when logging was never enabled.");
- }
- return log.logs.length == 0;
- }
-
- /**
- * [getLogs] extracts all calls from the call log that match the
- * [logFilter], and returns the matching list of [LogEntry]s. If
- * [destructive] is false (the default) the matching calls are left
- * in the log, else they are removed. Removal allows us to verify a
- * set of interactions and then verify that there are no other
- * interactions left. [actionMatcher] can be used to further
- * restrict the returned logs based on the action the mock performed.
- * [logFilter] can be a [CallMatcher] or a predicate function that
- * takes a [LogEntry] and returns a bool.
- *
- * Typical usage:
- *
- * getLogs(callsTo(...)).verify(...);
- */
- LogEntryList getLogs([CallMatcher logFilter,
- Matcher actionMatcher,
- bool destructive = false]) {
- if (log == null) {
- // This means we created the mock with logging off and have never turned
- // it on, so it doesn't make sense to get logs from such a mock.
- throw new
- Exception("Can't retrieve logs when logging was never enabled.");
- } else {
- return log.getMatches(name, logFilter, actionMatcher, destructive);
- }
- }
-
- /**
- * Useful shorthand method that creates a [CallMatcher] from its arguments
- * and then calls [getLogs].
- */
- LogEntryList calls(method,
- [arg0 = _noArg,
- arg1 = _noArg,
- arg2 = _noArg,
- arg3 = _noArg,
- arg4 = _noArg,
- arg5 = _noArg,
- arg6 = _noArg,
- arg7 = _noArg,
- arg8 = _noArg,
- arg9 = _noArg]) =>
- getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8, arg9));
-
- /** Clear the behaviors for the Mock. */
- void resetBehavior() => _behaviors.clear();
-
- /** Clear the logs for the Mock. */
- void clearLogs() {
- if (log != null) {
- if (name == null) { // This log is not shared.
- log.logs.clear();
- } else { // This log may be shared.
- log.logs = log.logs.where((e) => e.mockName != name).toList();
- }
- }
- }
-
- /** Clear both logs and behavior. */
- void reset() {
- resetBehavior();
- clearLogs();
- }
-}
diff --git a/pkg/mock/lib/src/action.dart b/pkg/mock/lib/src/action.dart
new file mode 100644
index 0000000..1712b2c
--- /dev/null
+++ b/pkg/mock/lib/src/action.dart
@@ -0,0 +1,26 @@
+// 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.
+
+library mock.action;
+
+/** The ways in which a call to a mock method can be handled. */
+class Action {
+ /** Do nothing (void method) */
+ static const IGNORE = const Action._('IGNORE');
+
+ /** Return a supplied value. */
+ static const RETURN = const Action._('RETURN');
+
+ /** Throw a supplied value. */
+ static const THROW = const Action._('THROW');
+
+ /** Call a supplied function. */
+ static const PROXY = const Action._('PROXY');
+
+ const Action._(this.name);
+
+ final String name;
+
+ String toString() => 'Action: $name';
+}
diff --git a/pkg/mock/lib/src/behavior.dart b/pkg/mock/lib/src/behavior.dart
new file mode 100644
index 0000000..b43f699
--- /dev/null
+++ b/pkg/mock/lib/src/behavior.dart
@@ -0,0 +1,83 @@
+// 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.
+
+library mock.behavior;
+
+import 'action.dart';
+import 'call_matcher.dart';
+import 'responder.dart';
+
+/**
+ * A [Behavior] represents how a [Mock] will respond to one particular
+ * type of method call.
+ */
+class Behavior {
+ CallMatcher matcher; // The method call matcher.
+ List<Responder> actions; // The values to return/throw or proxies to call.
+ bool logging = true;
+
+ Behavior (this.matcher) {
+ actions = new List<Responder>();
+ }
+
+ /**
+ * Adds a [Responder] that returns a [value] for [count] calls
+ * (1 by default).
+ */
+ Behavior thenReturn(value, [count = 1]) {
+ actions.add(new Responder(value, count, Action.RETURN));
+ return this; // For chaining calls.
+ }
+
+ /** Adds a [Responder] that repeatedly returns a [value]. */
+ Behavior alwaysReturn(value) {
+ return thenReturn(value, 0);
+ }
+
+ /**
+ * Adds a [Responder] that throws [value] [count]
+ * times (1 by default).
+ */
+ Behavior thenThrow(value, [count = 1]) {
+ actions.add(new Responder(value, count, Action.THROW));
+ return this; // For chaining calls.
+ }
+
+ /** Adds a [Responder] that throws [value] endlessly. */
+ Behavior alwaysThrow(value) {
+ return thenThrow(value, 0);
+ }
+
+ /**
+ * [thenCall] creates a proxy Responder, that is called [count]
+ * times (1 by default; 0 is used for unlimited calls, and is
+ * exposed as [alwaysCall]). [value] is the function that will
+ * be called with the same arguments that were passed to the
+ * mock. Proxies can be used to wrap real objects or to define
+ * more complex return/throw behavior. You could even (if you
+ * wanted) use proxies to emulate the behavior of thenReturn;
+ * e.g.:
+ *
+ * m.when(callsTo('foo')).thenReturn(0)
+ *
+ * is equivalent to:
+ *
+ * m.when(callsTo('foo')).thenCall(() => 0)
+ */
+ Behavior thenCall(value, [count = 1]) {
+ actions.add(new Responder(value, count, Action.PROXY));
+ return this; // For chaining calls.
+ }
+
+ /** Creates a repeating proxy call. */
+ Behavior alwaysCall(value) {
+ return thenCall(value, 0);
+ }
+
+ /** Returns true if a method call matches the [Behavior]. */
+ bool matches(String method, List args) => matcher.matches(method, args);
+
+ /** Returns the [matcher]'s representation. */
+ String toString() => matcher.toString();
+}
diff --git a/pkg/mock/lib/src/call_matcher.dart b/pkg/mock/lib/src/call_matcher.dart
new file mode 100644
index 0000000..ff6e5e5
--- /dev/null
+++ b/pkg/mock/lib/src/call_matcher.dart
@@ -0,0 +1,134 @@
+// 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.
+
+library mock.call_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'util.dart';
+
+/**
+ * A [CallMatcher] is a special matcher used to match method calls (i.e.
+ * a method name and set of arguments). It is not a [Matcher] like the
+ * unit test [Matcher], but instead represents a method name and a
+ * collection of [Matcher]s, one per argument, that will be applied
+ * to the parameters to decide if the method call is a match.
+ */
+class CallMatcher {
+ Matcher nameFilter;
+ List<Matcher> argMatchers;
+
+ /**
+ * Constructor for [CallMatcher]. [name] can be null to
+ * match anything, or a literal [String], a predicate [Function],
+ * or a [Matcher]. The various arguments can be scalar values or
+ * [Matcher]s.
+ */
+ CallMatcher([name,
+ arg0 = NO_ARG,
+ arg1 = NO_ARG,
+ arg2 = NO_ARG,
+ arg3 = NO_ARG,
+ arg4 = NO_ARG,
+ arg5 = NO_ARG,
+ arg6 = NO_ARG,
+ arg7 = NO_ARG,
+ arg8 = NO_ARG,
+ arg9 = NO_ARG]) {
+ if (name == null) {
+ nameFilter = anything;
+ } else {
+ nameFilter = wrapMatcher(name);
+ }
+ argMatchers = new List<Matcher>();
+ if (identical(arg0, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg0));
+ if (identical(arg1, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg1));
+ if (identical(arg2, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg2));
+ if (identical(arg3, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg3));
+ if (identical(arg4, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg4));
+ if (identical(arg5, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg5));
+ if (identical(arg6, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg6));
+ if (identical(arg7, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg7));
+ if (identical(arg8, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg8));
+ if (identical(arg9, NO_ARG)) return;
+ argMatchers.add(wrapMatcher(arg9));
+ }
+
+ /**
+ * We keep our behavior specifications in a Map, which is keyed
+ * by the [CallMatcher]. To make the keys unique and to get a
+ * descriptive value for the [CallMatcher] we have this override
+ * of [toString()].
+ */
+ String toString() {
+ Description d = new StringDescription();
+ d.addDescriptionOf(nameFilter);
+ // If the nameFilter was a simple string - i.e. just a method name -
+ // strip the quotes to make this more natural in appearance.
+ if (d.toString()[0] == "'") {
+ d.replace(d.toString().substring(1, d.toString().length - 1));
+ }
+ d.add('(');
+ for (var i = 0; i < argMatchers.length; i++) {
+ if (i > 0) d.add(', ');
+ d.addDescriptionOf(argMatchers[i]);
+ }
+ d.add(')');
+ return d.toString();
+ }
+
+ /**
+ * Given a [method] name and list of [arguments], return true
+ * if it matches this [CallMatcher.
+ */
+ bool matches(String method, List arguments) {
+ var matchState = {};
+ if (!nameFilter.matches(method, matchState)) {
+ return false;
+ }
+ var numArgs = (arguments == null) ? 0 : arguments.length;
+ if (numArgs < argMatchers.length) {
+ throw new Exception("Less arguments than matchers for $method.");
+ }
+ for (var i = 0; i < argMatchers.length; i++) {
+ if (!argMatchers[i].matches(arguments[i], matchState)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+/**
+ * Returns a [CallMatcher] for the specified signature. [method] can be
+ * null to match anything, or a literal [String], a predicate [Function],
+ * or a [Matcher]. The various arguments can be scalar values or [Matcher]s.
+ * To match getters and setters, use "get " and "set " prefixes on the names.
+ * For example, for a property "foo", you could use "get foo" and "set foo"
+ * as literal string arguments to callsTo to match the getter and setter
+ * of "foo".
+ */
+CallMatcher callsTo([method,
+ arg0 = NO_ARG,
+ arg1 = NO_ARG,
+ arg2 = NO_ARG,
+ arg3 = NO_ARG,
+ arg4 = NO_ARG,
+ arg5 = NO_ARG,
+ arg6 = NO_ARG,
+ arg7 = NO_ARG,
+ arg8 = NO_ARG,
+ arg9 = NO_ARG]) {
+ return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8, arg9);
+}
diff --git a/pkg/mock/lib/src/log_entry.dart b/pkg/mock/lib/src/log_entry.dart
new file mode 100644
index 0000000..85f9061
--- /dev/null
+++ b/pkg/mock/lib/src/log_entry.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library mock.log_entry;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'util.dart';
+
+/**
+ * Every call to a [Mock] object method is logged. The logs are
+ * kept in instances of [LogEntry].
+ */
+class LogEntry {
+ /** The time of the event. */
+ DateTime time;
+
+ /** The mock object name, if any. */
+ final String mockName;
+
+ /** The method name. */
+ final String methodName;
+
+ /** The parameters. */
+ final List args;
+
+ /** The behavior that resulted. */
+ final Action action;
+
+ /** The value that was returned (if no throw). */
+ final value;
+
+ LogEntry(this.mockName, this.methodName,
+ this.args, this.action, [this.value]) {
+ time = new DateTime.now();
+ }
+
+ String _pad2(int val) => (val >= 10 ? '$val' : '0$val');
+
+ String toString([DateTime baseTime]) {
+ Description d = new StringDescription();
+ if (baseTime == null) {
+ // Show absolute time.
+ d.add('${time.hour}:${_pad2(time.minute)}:'
+ '${_pad2(time.second)}.${time.millisecond}> ');
+ } else {
+ // Show relative time.
+ int delta = time.millisecondsSinceEpoch - baseTime.millisecondsSinceEpoch;
+ int secs = delta ~/ 1000;
+ int msecs = delta % 1000;
+ d.add('$secs.$msecs> ');
+ }
+ d.add('${qualifiedName(mockName, methodName)}(');
+ if (args != null) {
+ for (var i = 0; i < args.length; i++) {
+ if (i != 0) d.add(', ');
+ d.addDescriptionOf(args[i]);
+ }
+ }
+ d.add(') ${action == Action.THROW ? "threw" : "returned"} ');
+ d.addDescriptionOf(value);
+ return d.toString();
+ }
+}
diff --git a/pkg/mock/lib/src/log_entry_list.dart b/pkg/mock/lib/src/log_entry_list.dart
new file mode 100644
index 0000000..6c612ba
--- /dev/null
+++ b/pkg/mock/lib/src/log_entry_list.dart
@@ -0,0 +1,564 @@
+// 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.
+
+library mock.log_entry_list;
+
+import 'package:matcher/matcher.dart';
+
+import 'call_matcher.dart';
+import 'log_entry.dart';
+import 'util.dart';
+
+/**
+* [StepValidator]s are used by [stepwiseValidate] in [LogEntryList], which
+* iterates through the list and call the [StepValidator] function with the
+* log [List] and position. The [StepValidator] should return the number of
+* positions to advance upon success, or zero upon failure. When zero is
+* returned an error is reported.
+*/
+typedef int StepValidator(List<LogEntry> logs, int pos);
+
+/**
+ * We do verification on a list of [LogEntry]s. To allow chaining
+ * of calls to verify, we encapsulate such a list in the [LogEntryList]
+ * class.
+ */
+class LogEntryList {
+ String filter;
+ List<LogEntry> logs;
+ LogEntryList([this.filter]) {
+ logs = new List<LogEntry>();
+ }
+
+ /** Add a [LogEntry] to the log. */
+ add(LogEntry entry) => logs.add(entry);
+
+ /** Get the first entry, or null if no entries. */
+ get first => (logs == null || logs.length == 0) ? null : logs[0];
+
+ /** Get the last entry, or null if no entries. */
+ get last => (logs == null || logs.length == 0) ? null : logs.last;
+
+ /** Creates a LogEntry predicate function from the argument. */
+ Function _makePredicate(arg) {
+ if (arg == null) {
+ return (e) => true;
+ } else if (arg is CallMatcher) {
+ return (e) => arg.matches(e.methodName, e.args);
+ } else if (arg is Function) {
+ return arg;
+ } else {
+ throw new Exception("Invalid argument to _makePredicate.");
+ }
+ }
+
+ /**
+ * Create a new [LogEntryList] consisting of [LogEntry]s from
+ * this list that match the specified [mockNameFilter] and [logFilter].
+ * [mockNameFilter] can be null, a [String], a predicate [Function],
+ * or a [Matcher]. If [mockNameFilter] is null, this is the same as
+ * [anything].
+ * If [logFilter] is null, all entries in the log will be returned.
+ * Otherwise [logFilter] should be a [CallMatcher] or predicate function
+ * that takes a [LogEntry] and returns a bool.
+ * If [destructive] is true, the log entries are removed from the
+ * original list.
+ */
+ LogEntryList getMatches([mockNameFilter,
+ logFilter,
+ Matcher actionMatcher,
+ bool destructive = false]) {
+ if (mockNameFilter == null) {
+ mockNameFilter = anything;
+ } else {
+ mockNameFilter = wrapMatcher(mockNameFilter);
+ }
+ Function entryFilter = _makePredicate(logFilter);
+ String filterName = qualifiedName(mockNameFilter, logFilter.toString());
+ LogEntryList rtn = new LogEntryList(filterName);
+ var matchState = {};
+ for (var i = 0; i < logs.length; i++) {
+ LogEntry entry = logs[i];
+ if (mockNameFilter.matches(entry.mockName, matchState) &&
+ entryFilter(entry)) {
+ if (actionMatcher == null ||
+ actionMatcher.matches(entry, matchState)) {
+ rtn.add(entry);
+ if (destructive) {
+ int startIndex = i--;
+ logs.removeRange(startIndex, startIndex + 1);
+ }
+ }
+ }
+ }
+ return rtn;
+ }
+
+ /** Apply a unit test [Matcher] to the [LogEntryList]. */
+ LogEntryList verify(Matcher matcher) {
+ if (_mockFailureHandler == null) {
+ _mockFailureHandler =
+ new _MockFailureHandler(getOrCreateExpectFailureHandler());
+ }
+ expect(logs, matcher, reason:filter, failureHandler: _mockFailureHandler);
+ return this;
+ }
+
+ /**
+ * Iterate through the list and call the [validator] function with the
+ * log [List] and position. The [validator] should return the number of
+ * positions to advance upon success, or zero upon failure. When zero is
+ * returned an error is reported. [reason] can be used to provide a
+ * more descriptive failure message. If a failure occurred false will be
+ * returned (unless the failure handler itself threw an exception);
+ * otherwise true is returned.
+ * The use case here is to perform more complex validations; for example
+ * we may want to assert that the return value from some function is
+ * later used as a parameter to a following function. If we filter the logs
+ * to include just these two functions we can write a simple validator to
+ * do this check.
+ */
+ bool stepwiseValidate(StepValidator validator, [String reason = '']) {
+ if (_mockFailureHandler == null) {
+ _mockFailureHandler =
+ new _MockFailureHandler(getOrCreateExpectFailureHandler());
+ }
+ var i = 0;
+ while (i < logs.length) {
+ var n = validator(logs, i);
+ if (n == 0) {
+ if (reason.length > 0) {
+ reason = ': $reason';
+ }
+ _mockFailureHandler.fail("Stepwise validation failed at $filter "
+ "position $i$reason");
+ return false;
+ } else {
+ i += n;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Turn the logs into human-readable text. If [baseTime] is specified
+ * then each entry is prefixed with the offset from that time in
+ * milliseconds; otherwise the time of day is used.
+ */
+ String toString([DateTime baseTime]) {
+ String s = '';
+ for (var e in logs) {
+ s = '$s${e.toString(baseTime)}\n';
+ }
+ return s;
+ }
+
+ /**
+ * Find the first log entry that satisfies [logFilter] and
+ * return its position. A search [start] position can be provided
+ * to allow for repeated searches. [logFilter] can be a [CallMatcher],
+ * or a predicate function that takes a [LogEntry] argument and returns
+ * a bool. If [logFilter] is null, it will match any [LogEntry].
+ * If no entry is found, then [failureReturnValue] is returned.
+ * After each check the position is updated by [skip], so using
+ * [skip] of -1 allows backward searches, using a [skip] of 2 can
+ * be used to check pairs of adjacent entries, and so on.
+ */
+ int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
+ skip = 1]) {
+ logFilter = _makePredicate(logFilter);
+ int pos = start;
+ while (pos >= 0 && pos < logs.length) {
+ if (logFilter(logs[pos])) {
+ return pos;
+ }
+ pos += skip;
+ }
+ return failureReturnValue;
+ }
+
+ /**
+ * Returns log events that happened up to the first one that
+ * satisfies [logFilter]. If [inPlace] is true, then returns
+ * this LogEntryList after removing the from the first satisfier;
+ * onwards otherwise a new list is created. [description]
+ * is used to create a new name for the resulting list.
+ * [defaultPosition] is used as the index of the matching item in
+ * the case that no match is found.
+ */
+ LogEntryList _head(logFilter, bool inPlace,
+ String description, int defaultPosition) {
+ if (filter != null) {
+ description = '$filter $description';
+ }
+ int pos = findLogEntry(logFilter, 0, defaultPosition);
+ if (inPlace) {
+ if (pos < logs.length) {
+ logs.removeRange(pos, logs.length);
+ }
+ filter = description;
+ return this;
+ } else {
+ LogEntryList newList = new LogEntryList(description);
+ for (var i = 0; i < pos; i++) {
+ newList.logs.add(logs[i]);
+ }
+ return newList;
+ }
+ }
+
+ /**
+ * Returns log events that happened from the first one that
+ * satisfies [logFilter]. If [inPlace] is true, then returns
+ * this LogEntryList after removing the entries up to the first
+ * satisfier; otherwise a new list is created. [description]
+ * is used to create a new name for the resulting list.
+ * [defaultPosition] is used as the index of the matching item in
+ * the case that no match is found.
+ */
+ LogEntryList _tail(logFilter, bool inPlace,
+ String description, int defaultPosition) {
+ if (filter != null) {
+ description = '$filter $description';
+ }
+ int pos = findLogEntry(logFilter, 0, defaultPosition);
+ if (inPlace) {
+ if (pos > 0) {
+ logs.removeRange(0, pos);
+ }
+ filter = description;
+ return this;
+ } else {
+ LogEntryList newList = new LogEntryList(description);
+ while (pos < logs.length) {
+ newList.logs.add(logs[pos++]);
+ }
+ return newList;
+ }
+ }
+
+ /**
+ * Returns log events that happened after [when]. If [inPlace]
+ * is true, then it returns this LogEntryList after removing
+ * the entries that happened up to [when]; otherwise a new
+ * list is created.
+ */
+ LogEntryList after(DateTime when, [bool inPlace = false]) =>
+ _tail((e) => e.time.isAfter(when), inPlace, 'after $when', logs.length);
+
+ /**
+ * Returns log events that happened from [when] onwards. If
+ * [inPlace] is true, then it returns this LogEntryList after
+ * removing the entries that happened before [when]; otherwise
+ * a new list is created.
+ */
+ LogEntryList from(DateTime when, [bool inPlace = false]) =>
+ _tail((e) => !e.time.isBefore(when), inPlace, 'from $when', logs.length);
+
+ /**
+ * Returns log events that happened until [when]. If [inPlace]
+ * is true, then it returns this LogEntryList after removing
+ * the entries that happened after [when]; otherwise a new
+ * list is created.
+ */
+ LogEntryList until(DateTime when, [bool inPlace = false]) =>
+ _head((e) => e.time.isAfter(when), inPlace, 'until $when', logs.length);
+
+ /**
+ * Returns log events that happened before [when]. If [inPlace]
+ * is true, then it returns this LogEntryList after removing
+ * the entries that happened from [when] onwards; otherwise a new
+ * list is created.
+ */
+ LogEntryList before(DateTime when, [bool inPlace = false]) =>
+ _head((e) => !e.time.isBefore(when),
+ inPlace,
+ 'before $when',
+ logs.length);
+
+ /**
+ * Returns log events that happened after [logEntry]'s time.
+ * If [inPlace] is true, then it returns this LogEntryList after
+ * removing the entries that happened up to [when]; otherwise a new
+ * list is created. If [logEntry] is null the current time is used.
+ */
+ LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
+ after(logEntry == null ? new DateTime.now() : logEntry.time);
+
+ /**
+ * Returns log events that happened from [logEntry]'s time onwards.
+ * If [inPlace] is true, then it returns this LogEntryList after
+ * removing the entries that happened before [when]; otherwise
+ * a new list is created. If [logEntry] is null the current time is used.
+ */
+ LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
+ from(logEntry == null ? new DateTime.now() : logEntry.time);
+
+ /**
+ * Returns log events that happened until [logEntry]'s time. If
+ * [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened after [when]; otherwise a new
+ * list is created. If [logEntry] is null the epoch time is used.
+ */
+ LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
+ until(logEntry == null ?
+ new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
+
+ /**
+ * Returns log events that happened before [logEntry]'s time. If
+ * [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened from [when] onwards; otherwise a new
+ * list is created. If [logEntry] is null the epoch time is used.
+ */
+ LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
+ before(logEntry == null ?
+ new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
+
+ /**
+ * Returns log events that happened after the first event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened earlier; otherwise a new list is created.
+ */
+ LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
+ afterEntry(segment.first, inPlace);
+
+ /**
+ * Returns log events that happened after the last event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened earlier; otherwise a new list is created.
+ */
+ LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
+ afterEntry(segment.last, inPlace);
+
+ /**
+ * Returns log events that happened from the time of the first event in
+ * [segment] onwards. If [inPlace] is true, then it returns this
+ * LogEntryList after removing the earlier entries; otherwise a new list
+ * is created.
+ */
+ LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
+ fromEntry(segment.first, inPlace);
+
+ /**
+ * Returns log events that happened from the time of the last event in
+ * [segment] onwards. If [inPlace] is true, then it returns this
+ * LogEntryList after removing the earlier entries; otherwise a new list
+ * is created.
+ */
+ LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
+ fromEntry(segment.last, inPlace);
+
+ /**
+ * Returns log events that happened until the first event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened later; otherwise a new list is created.
+ */
+ LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
+ untilEntry(segment.first, inPlace);
+
+ /**
+ * Returns log events that happened until the last event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened later; otherwise a new list is created.
+ */
+ LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
+ untilEntry(segment.last, inPlace);
+
+ /**
+ * Returns log events that happened before the first event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened later; otherwise a new list is created.
+ */
+ LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
+ beforeEntry(segment.first, inPlace);
+
+ /**
+ * Returns log events that happened before the last event in [segment].
+ * If [inPlace] is true, then it returns this LogEntryList after removing
+ * the entries that happened later; otherwise a new list is created.
+ */
+ LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
+ beforeEntry(segment.last, inPlace);
+
+ /**
+ * Iterate through the LogEntryList looking for matches to the entries
+ * in [keys]; for each match found the closest [distance] neighboring log
+ * entries that match [mockNameFilter] and [logFilter] will be included in
+ * the result. If [isPreceding] is true we use the neighbors that precede
+ * the matched entry; else we use the neighbors that followed.
+ * If [includeKeys] is true then the entries in [keys] that resulted in
+ * entries in the output list are themselves included in the output list. If
+ * [distance] is zero then all matches are included.
+ */
+ LogEntryList _neighboring(bool isPreceding,
+ LogEntryList keys,
+ mockNameFilter,
+ logFilter,
+ int distance,
+ bool includeKeys) {
+ String filterName = 'Calls to '
+ '${qualifiedName(mockNameFilter, logFilter.toString())} '
+ '${isPreceding?"preceding":"following"} ${keys.filter}';
+
+ LogEntryList rtn = new LogEntryList(filterName);
+
+ // Deal with the trivial case.
+ if (logs.length == 0 || keys.logs.length == 0) {
+ return rtn;
+ }
+
+ // Normalize the mockNameFilter and logFilter values.
+ if (mockNameFilter == null) {
+ mockNameFilter = anything;
+ } else {
+ mockNameFilter = wrapMatcher(mockNameFilter);
+ }
+ logFilter = _makePredicate(logFilter);
+
+ // The scratch list is used to hold matching entries when we
+ // are doing preceding neighbors. The remainingCount is used to
+ // keep track of how many matching entries we can still add in the
+ // current segment (0 if we are doing doing following neighbors, until
+ // we get our first key match).
+ List scratch = null;
+ int remainingCount = 0;
+ if (isPreceding) {
+ scratch = new List();
+ remainingCount = logs.length;
+ }
+
+ var keyIterator = keys.logs.iterator;
+ keyIterator.moveNext();
+ LogEntry keyEntry = keyIterator.current;
+ Map matchState = {};
+
+ for (LogEntry logEntry in logs) {
+ // If we have a log entry match, copy the saved matches from the
+ // scratch buffer into the return list, as well as the matching entry,
+ // if appropriate, and reset the scratch buffer. Continue processing
+ // from the next key entry.
+ if (keyEntry == logEntry) {
+ if (scratch != null) {
+ int numToCopy = scratch.length;
+ if (distance > 0 && distance < numToCopy) {
+ numToCopy = distance;
+ }
+ for (var i = scratch.length - numToCopy; i < scratch.length; i++) {
+ rtn.logs.add(scratch[i]);
+ }
+ scratch.clear();
+ } else {
+ remainingCount = distance > 0 ? distance : logs.length;
+ }
+ if (includeKeys) {
+ rtn.logs.add(keyEntry);
+ }
+ if (keyIterator.moveNext()) {
+ keyEntry = keyIterator.current;
+ } else if (isPreceding) { // We're done.
+ break;
+ }
+ } else if (remainingCount > 0 &&
+ mockNameFilter.matches(logEntry.mockName, matchState) &&
+ logFilter(logEntry)) {
+ if (scratch != null) {
+ scratch.add(logEntry);
+ } else {
+ rtn.logs.add(logEntry);
+ --remainingCount;
+ }
+ }
+ }
+ return rtn;
+ }
+
+ /**
+ * Iterate through the LogEntryList looking for matches to the entries
+ * in [keys]; for each match found the closest [distance] prior log entries
+ * that match [mocknameFilter] and [logFilter] will be included in the result.
+ * If [includeKeys] is true then the entries in [keys] that resulted in
+ * entries in the output list are themselves included in the output list. If
+ * [distance] is zero then all matches are included.
+ *
+ * The idea here is that you could find log entries that are related to
+ * other logs entries in some temporal sense. For example, say we have a
+ * method commit() that returns -1 on failure. Before commit() gets called
+ * the value being committed is created by process(). We may want to find
+ * the calls to process() that preceded calls to commit() that failed.
+ * We could do this with:
+ *
+ * print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
+ * logFilter: callsTo('process')).toString());
+ *
+ * We might want to include the details of the failing calls to commit()
+ * to see what parameters were passed in, in which case we would set
+ * [includeKeys].
+ *
+ * As another simple example, say we wanted to know the three method
+ * calls that immediately preceded each failing call to commit():
+ *
+ * print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
+ * distance: 3).toString());
+ */
+ LogEntryList preceding(LogEntryList keys,
+ {mockNameFilter: null,
+ logFilter: null,
+ int distance: 1,
+ bool includeKeys: false}) =>
+ _neighboring(true, keys, mockNameFilter, logFilter,
+ distance, includeKeys);
+
+ /**
+ * Iterate through the LogEntryList looking for matches to the entries
+ * in [keys]; for each match found the closest [distance] subsequent log
+ * entries that match [mocknameFilter] and [logFilter] will be included in
+ * the result. If [includeKeys] is true then the entries in [keys] that
+ * resulted in entries in the output list are themselves included in the
+ * output list. If [distance] is zero then all matches are included.
+ * See [preceding] for a usage example.
+ */
+ LogEntryList following(LogEntryList keys,
+ {mockNameFilter: null,
+ logFilter: null,
+ int distance: 1,
+ bool includeKeys: false}) =>
+ _neighboring(false, keys, mockNameFilter, logFilter,
+ distance, includeKeys);
+}
+
+_MockFailureHandler _mockFailureHandler = null;
+
+/**
+ * The failure handler for the [expect()] calls that occur in [verify()]
+ * methods in the mock objects. This calls the real failure handler used
+ * by the unit test library after formatting the error message with
+ * the custom formatter.
+ */
+class _MockFailureHandler implements FailureHandler {
+ FailureHandler proxy;
+ _MockFailureHandler(this.proxy);
+ void fail(String reason) {
+ proxy.fail(reason);
+ }
+ void failMatch(actual, Matcher matcher, String reason,
+ Map matchState, bool verbose) {
+ proxy.fail(_mockingErrorFormatter(actual, matcher, reason,
+ matchState, verbose));
+ }
+}
+
+/**
+ * The error formatter for mocking is a bit different from the default one
+ * for unit testing; instead of the third argument being a 'reason'
+ * it is instead a [signature] describing the method signature filter
+ * that was used to select the logs that were verified.
+ */
+String _mockingErrorFormatter(actual, Matcher matcher, String signature,
+ Map matchState, bool verbose) {
+ var description = new StringDescription();
+ description.add('Expected ${signature} ').addDescriptionOf(matcher).
+ add('\n but: ');
+ matcher.describeMismatch(actual, description, matchState, verbose).add('.');
+ return description.toString();
+}
diff --git a/pkg/mock/lib/src/mock.dart b/pkg/mock/lib/src/mock.dart
new file mode 100644
index 0000000..29598b8
--- /dev/null
+++ b/pkg/mock/lib/src/mock.dart
@@ -0,0 +1,325 @@
+// 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.
+
+library mock.mock;
+
+// TOOD(kevmoo): just use `Map`
+import 'dart:collection' show LinkedHashMap;
+import 'dart:mirrors';
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'behavior.dart';
+import 'call_matcher.dart';
+import 'log_entry.dart';
+import 'log_entry_list.dart';
+import 'responder.dart';
+import 'util.dart';
+
+/** The base class for all mocked objects. */
+@proxy
+class Mock {
+ /** The mock name. Needed if the log is shared; optional otherwise. */
+ final String name;
+
+ /** The set of [Behavior]s supported. */
+ final LinkedHashMap<String,Behavior> _behaviors;
+
+ /** How to handle unknown method calls - swallow or throw. */
+ final bool _throwIfNoBehavior;
+
+ /** For spys, the real object that we are spying on. */
+ final Object _realObject;
+
+ /** The [log] of calls made. Only used if [name] is null. */
+ LogEntryList log;
+
+ /** Whether to create an audit log or not. */
+ bool _logging;
+
+ bool get logging => _logging;
+ set logging(bool value) {
+ if (value && log == null) {
+ log = new LogEntryList();
+ }
+ _logging = value;
+ }
+
+ /**
+ * Default constructor. Unknown method calls are allowed and logged,
+ * the mock has no name, and has its own log.
+ */
+ Mock() :
+ _throwIfNoBehavior = false, log = null, name = null, _realObject = null,
+ _behaviors = new LinkedHashMap<String,Behavior>() {
+ logging = true;
+ }
+
+ /**
+ * This constructor makes a mock that has a [name] and possibly uses
+ * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods
+ * that have no defined behaviors will throw an exception; otherwise they
+ * will be allowed and logged (but will not do anything).
+ * If [enableLogging] is false, no logging will be done initially (whether
+ * or not a [log] is supplied), but [logging] can be set to true later.
+ */
+ Mock.custom({this.name,
+ this.log,
+ throwIfNoBehavior: false,
+ enableLogging: true})
+ : _throwIfNoBehavior = throwIfNoBehavior, _realObject = null,
+ _behaviors = new LinkedHashMap<String,Behavior>() {
+ if (log != null && name == null) {
+ throw new Exception("Mocks with shared logs must have a name.");
+ }
+ logging = enableLogging;
+ }
+
+ /**
+ * This constructor creates a spy with no user-defined behavior.
+ * This is simply a proxy for a real object that passes calls
+ * through to that real object but captures an audit trail of
+ * calls made to the object that can be queried and validated
+ * later.
+ */
+ Mock.spy(this._realObject, {this.name, this.log})
+ : _behaviors = null,
+ _throwIfNoBehavior = true {
+ logging = true;
+ }
+
+ /**
+ * [when] is used to create a new or extend an existing [Behavior].
+ * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for
+ * that signature are returned (being created first if needed).
+ *
+ * Typical use case:
+ *
+ * mock.when(callsTo(...)).alwaysReturn(...);
+ */
+ Behavior when(CallMatcher logFilter) {
+ String key = logFilter.toString();
+ if (!_behaviors.containsKey(key)) {
+ Behavior b = new Behavior(logFilter);
+ _behaviors[key] = b;
+ return b;
+ } else {
+ return _behaviors[key];
+ }
+ }
+
+ /**
+ * This is the handler for method calls. We loop through the list
+ * of [Behavior]s, and find the first match that still has return
+ * values available, and then do the action specified by that
+ * return value. If we find no [Behavior] to apply an exception is
+ * thrown.
+ */
+ noSuchMethod(Invocation invocation) {
+ var method = MirrorSystem.getName(invocation.memberName);
+ var args = invocation.positionalArguments;
+ if (invocation.isGetter) {
+ method = 'get $method';
+ } else if (invocation.isSetter) {
+ method = 'set $method';
+ // Remove the trailing '='.
+ if (method[method.length-1] == '=') {
+ method = method.substring(0, method.length - 1);
+ }
+ }
+ if (_behaviors == null) { // Spy.
+ var mirror = reflect(_realObject);
+ try {
+ var result = mirror.delegate(invocation);
+ log.add(new LogEntry(name, method, args, Action.PROXY, result));
+ return result;
+ } catch (e) {
+ log.add(new LogEntry(name, method, args, Action.THROW, e));
+ throw e;
+ }
+ }
+ bool matchedMethodName = false;
+ Map matchState = {};
+ for (String k in _behaviors.keys) {
+ Behavior b = _behaviors[k];
+ if (b.matcher.nameFilter.matches(method, matchState)) {
+ matchedMethodName = true;
+ }
+ if (b.matches(method, args)) {
+ List actions = b.actions;
+ if (actions == null || actions.length == 0) {
+ continue; // No return values left in this Behavior.
+ }
+ // Get the first response.
+ Responder response = actions[0];
+ // If it is exhausted, remove it from the list.
+ // Note that for endlessly repeating values, we started the count at
+ // 0, so we get a potentially useful value here, which is the
+ // (negation of) the number of times we returned the value.
+ if (--response.count == 0) {
+ actions.removeRange(0, 1);
+ }
+ // Do the response.
+ Action action = response.action;
+ var value = response.value;
+ if (action == Action.RETURN) {
+ if (_logging && b.logging) {
+ log.add(new LogEntry(name, method, args, action, value));
+ }
+ return value;
+ } else if (action == Action.THROW) {
+ if (_logging && b.logging) {
+ log.add(new LogEntry(name, method, args, action, value));
+ }
+ throw value;
+ } else if (action == Action.PROXY) {
+ // TODO(gram): Replace all this with:
+ // var rtn = reflect(value).apply(invocation.positionalArguments,
+ // invocation.namedArguments);
+ // once that is supported.
+ var rtn;
+ switch (args.length) {
+ case 0:
+ rtn = value();
+ break;
+ case 1:
+ rtn = value(args[0]);
+ break;
+ case 2:
+ rtn = value(args[0], args[1]);
+ break;
+ case 3:
+ rtn = value(args[0], args[1], args[2]);
+ break;
+ case 4:
+ rtn = value(args[0], args[1], args[2], args[3]);
+ break;
+ case 5:
+ rtn = value(args[0], args[1], args[2], args[3], args[4]);
+ break;
+ case 6:
+ rtn = value(args[0], args[1], args[2], args[3],
+ args[4], args[5]);
+ break;
+ case 7:
+ rtn = value(args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6]);
+ break;
+ case 8:
+ rtn = value(args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7]);
+ break;
+ case 9:
+ rtn = value(args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7], args[8]);
+ break;
+ case 9:
+ rtn = value(args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7], args[8], args[9]);
+ break;
+ default:
+ throw new Exception(
+ "Cannot proxy calls with more than 10 parameters.");
+ }
+ if (_logging && b.logging) {
+ log.add(new LogEntry(name, method, args, action, rtn));
+ }
+ return rtn;
+ }
+ }
+ }
+ if (matchedMethodName) {
+ // User did specify behavior for this method, but all the
+ // actions are exhausted. This is considered an error.
+ throw new Exception('No more actions for method '
+ '${qualifiedName(name, method)}.');
+ } else if (_throwIfNoBehavior) {
+ throw new Exception('No behavior specified for method '
+ '${qualifiedName(name, method)}.');
+ }
+ // Otherwise user hasn't specified behavior for this method; we don't throw
+ // so we can underspecify.
+ if (_logging) {
+ log.add(new LogEntry(name, method, args, Action.IGNORE));
+ }
+ }
+
+ /** [verifyZeroInteractions] returns true if no calls were made */
+ bool verifyZeroInteractions() {
+ if (log == null) {
+ // This means we created the mock with logging off and have never turned
+ // it on, so it doesn't make sense to verify behavior on such a mock.
+ throw new
+ Exception("Can't verify behavior when logging was never enabled.");
+ }
+ return log.logs.length == 0;
+ }
+
+ /**
+ * [getLogs] extracts all calls from the call log that match the
+ * [logFilter], and returns the matching list of [LogEntry]s. If
+ * [destructive] is false (the default) the matching calls are left
+ * in the log, else they are removed. Removal allows us to verify a
+ * set of interactions and then verify that there are no other
+ * interactions left. [actionMatcher] can be used to further
+ * restrict the returned logs based on the action the mock performed.
+ * [logFilter] can be a [CallMatcher] or a predicate function that
+ * takes a [LogEntry] and returns a bool.
+ *
+ * Typical usage:
+ *
+ * getLogs(callsTo(...)).verify(...);
+ */
+ LogEntryList getLogs([CallMatcher logFilter,
+ Matcher actionMatcher,
+ bool destructive = false]) {
+ if (log == null) {
+ // This means we created the mock with logging off and have never turned
+ // it on, so it doesn't make sense to get logs from such a mock.
+ throw new
+ Exception("Can't retrieve logs when logging was never enabled.");
+ } else {
+ return log.getMatches(name, logFilter, actionMatcher, destructive);
+ }
+ }
+
+ /**
+ * Useful shorthand method that creates a [CallMatcher] from its arguments
+ * and then calls [getLogs].
+ */
+ LogEntryList calls(method,
+ [arg0 = NO_ARG,
+ arg1 = NO_ARG,
+ arg2 = NO_ARG,
+ arg3 = NO_ARG,
+ arg4 = NO_ARG,
+ arg5 = NO_ARG,
+ arg6 = NO_ARG,
+ arg7 = NO_ARG,
+ arg8 = NO_ARG,
+ arg9 = NO_ARG]) =>
+ getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8, arg9));
+
+ /** Clear the behaviors for the Mock. */
+ void resetBehavior() => _behaviors.clear();
+
+ /** Clear the logs for the Mock. */
+ void clearLogs() {
+ if (log != null) {
+ if (name == null) { // This log is not shared.
+ log.logs.clear();
+ } else { // This log may be shared.
+ log.logs = log.logs.where((e) => e.mockName != name).toList();
+ }
+ }
+ }
+
+ /** Clear both logs and behavior. */
+ void reset() {
+ resetBehavior();
+ clearLogs();
+ }
+}
diff --git a/pkg/mock/lib/src/responder.dart b/pkg/mock/lib/src/responder.dart
new file mode 100644
index 0000000..ef0dd71
--- /dev/null
+++ b/pkg/mock/lib/src/responder.dart
@@ -0,0 +1,21 @@
+// 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.
+
+library mock.responder;
+
+import 'action.dart';
+
+/**
+ * The behavior of a method call in the mock library is specified
+ * with [Responder]s. A [Responder] has a [value] to throw
+ * or return (depending on the type of [action]),
+ * and can either be one-shot, multi-shot, or infinitely repeating,
+ * depending on the value of [count (1, greater than 1, or 0 respectively).
+ */
+class Responder {
+ final Object value;
+ final Action action;
+ int count;
+ Responder(this.value, [this.count = 1, this.action = Action.RETURN]);
+}
diff --git a/pkg/mock/lib/src/result_matcher.dart b/pkg/mock/lib/src/result_matcher.dart
new file mode 100644
index 0000000..08f89d6
--- /dev/null
+++ b/pkg/mock/lib/src/result_matcher.dart
@@ -0,0 +1,67 @@
+// 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.
+
+library mock.result_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'log_entry.dart';
+
+/**
+ * [_ResultMatcher]s are used to make assertions about the results
+ * of method calls. These can be used as optional parameters to [getLogs].
+ */
+class _ResultMatcher extends Matcher {
+ final Action action;
+ final Matcher value;
+
+ const _ResultMatcher(this.action, this.value);
+
+ bool matches(item, Map matchState) {
+ if (item is! LogEntry) {
+ return false;
+ }
+ // normalize the action; _PROXY is like _RETURN.
+ Action eaction = item.action;
+ if (eaction == Action.PROXY) {
+ eaction = Action.RETURN;
+ }
+ return (eaction == action && value.matches(item.value, matchState));
+ }
+
+ Description describe(Description description) {
+ description.add(' to ');
+ if (action == Action.RETURN || action == Action.PROXY)
+ description.add('return ');
+ else
+ description.add('throw ');
+ return description.addDescriptionOf(value);
+ }
+
+ Description describeMismatch(item, Description mismatchDescription,
+ Map matchState, bool verbose) {
+ if (item.action == Action.RETURN || item.action == Action.PROXY) {
+ mismatchDescription.add('returned ');
+ } else {
+ mismatchDescription.add('threw ');
+ }
+ mismatchDescription.add(item.value);
+ return mismatchDescription;
+ }
+}
+
+/**
+ *[returning] matches log entries where the call to a method returned
+ * a value that matched [value].
+ */
+Matcher returning(value) =>
+ new _ResultMatcher(Action.RETURN, wrapMatcher(value));
+
+/**
+ *[throwing] matches log entrues where the call to a method threw
+ * a value that matched [value].
+ */
+Matcher throwing(value) =>
+ new _ResultMatcher(Action.THROW, wrapMatcher(value));
diff --git a/pkg/mock/lib/src/result_set_matcher.dart b/pkg/mock/lib/src/result_set_matcher.dart
new file mode 100644
index 0000000..103ce78
--- /dev/null
+++ b/pkg/mock/lib/src/result_set_matcher.dart
@@ -0,0 +1,144 @@
+// 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.
+
+library mock.result_set_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'log_entry.dart';
+
+/** Special values for use with [_ResultSetMatcher] [frequency]. */
+class _Frequency {
+ /** Every call/throw must match */
+ static const ALL = const _Frequency._('ALL');
+
+ /** At least one call/throw must match. */
+ static const SOME = const _Frequency._('SOME');
+
+ /** No calls/throws should match. */
+ static const NONE = const _Frequency._('NONE');
+
+ const _Frequency._(this.name);
+
+ final String name;
+}
+
+/**
+ * [_ResultSetMatcher]s are used to make assertions about the results
+ * of method calls. When filtering an execution log by calling
+ * [getLogs], a [LogEntrySet] of matching call logs is returned;
+ * [_ResultSetMatcher]s can then assert various things about this
+ * (sub)set of logs.
+ *
+ * We could make this class use _ResultMatcher but it doesn't buy that
+ * match and adds some perf hit, so there is some duplication here.
+ */
+class _ResultSetMatcher extends Matcher {
+ final Action action;
+ final Matcher value;
+ final _Frequency frequency; // ALL, SOME, or NONE.
+
+ const _ResultSetMatcher(this.action, this.value, this.frequency);
+
+ bool matches(logList, Map matchState) {
+ for (LogEntry entry in logList) {
+ // normalize the action; PROXY is like RETURN.
+ Action eaction = entry.action;
+ if (eaction == Action.PROXY) {
+ eaction = Action.RETURN;
+ }
+ if (eaction == action && value.matches(entry.value, matchState)) {
+ if (frequency == _Frequency.NONE) {
+ addStateInfo(matchState, {'entry': entry});
+ return false;
+ } else if (frequency == _Frequency.SOME) {
+ return true;
+ }
+ } else {
+ // Mismatch.
+ if (frequency == _Frequency.ALL) { // We need just one mismatch to fail.
+ addStateInfo(matchState, {'entry': entry});
+ return false;
+ }
+ }
+ }
+ // If we get here, then if count is _ALL we got all matches and
+ // this is success; otherwise we got all mismatched which is
+ // success for count == _NONE and failure for count == _SOME.
+ return (frequency != _Frequency.SOME);
+ }
+
+ Description describe(Description description) {
+ description.add(' to ');
+ description.add(frequency == _Frequency.ALL ? 'alway ' :
+ (frequency == _Frequency.NONE ? 'never ' : 'sometimes '));
+ if (action == Action.RETURN || action == Action.PROXY)
+ description.add('return ');
+ else
+ description.add('throw ');
+ return description.addDescriptionOf(value);
+ }
+
+ Description describeMismatch(logList, Description mismatchDescription,
+ Map matchState, bool verbose) {
+ if (frequency != _Frequency.SOME) {
+ LogEntry entry = matchState['entry'];
+ if (entry.action == Action.RETURN || entry.action == Action.PROXY) {
+ mismatchDescription.add('returned');
+ } else {
+ mismatchDescription.add('threw');
+ }
+ mismatchDescription.add(' value that ');
+ value.describeMismatch(entry.value, mismatchDescription,
+ matchState['state'], verbose);
+ mismatchDescription.add(' at least once');
+ } else {
+ mismatchDescription.add('never did');
+ }
+ return mismatchDescription;
+ }
+}
+
+/**
+ *[alwaysReturned] asserts that all matching calls to a method returned
+ * a value that matched [value].
+ */
+Matcher alwaysReturned(value) =>
+ new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.ALL);
+
+/**
+ *[sometimeReturned] asserts that at least one matching call to a method
+ * returned a value that matched [value].
+ */
+Matcher sometimeReturned(value) =>
+ new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.SOME);
+
+/**
+ *[neverReturned] asserts that no matching calls to a method returned
+ * a value that matched [value].
+ */
+Matcher neverReturned(value) =>
+ new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.NONE);
+
+/**
+ *[alwaysThrew] asserts that all matching calls to a method threw
+ * a value that matched [value].
+ */
+Matcher alwaysThrew(value) =>
+ new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.ALL);
+
+/**
+ *[sometimeThrew] asserts that at least one matching call to a method threw
+ * a value that matched [value].
+ */
+Matcher sometimeThrew(value) =>
+ new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.SOME);
+
+/**
+ *[neverThrew] asserts that no matching call to a method threw
+ * a value that matched [value].
+ */
+Matcher neverThrew(value) =>
+ new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE);
diff --git a/pkg/mock/lib/src/times_matcher.dart b/pkg/mock/lib/src/times_matcher.dart
new file mode 100644
index 0000000..225fc94
--- /dev/null
+++ b/pkg/mock/lib/src/times_matcher.dart
@@ -0,0 +1,65 @@
+// 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.
+
+library mock.times_matcher;
+
+import 'package:matcher/matcher.dart';
+
+/**
+ * [_TimesMatcher]s are used to make assertions about the number of
+ * times a method was called.
+ */
+class _TimesMatcher extends Matcher {
+ final int min, max;
+
+ const _TimesMatcher(this.min, [this.max = -1]);
+
+ bool matches(logList, Map matchState) => logList.length >= min &&
+ (max < 0 || logList.length <= max);
+
+ Description describe(Description description) {
+ description.add('to be called ');
+ if (max < 0) {
+ description.add('at least $min');
+ } else if (max == min) {
+ description.add('$max');
+ } else if (min == 0) {
+ description.add('at most $max');
+ } else {
+ description.add('between $min and $max');
+ }
+ return description.add(' times');
+ }
+
+ Description describeMismatch(logList, Description mismatchDescription,
+ Map matchState, bool verbose) =>
+ mismatchDescription.add('was called ${logList.length} times');
+}
+
+/** [happenedExactly] matches an exact number of calls. */
+Matcher happenedExactly(count) {
+ return new _TimesMatcher(count, count);
+}
+
+/** [happenedAtLeast] matches a minimum number of calls. */
+Matcher happenedAtLeast(count) {
+ return new _TimesMatcher(count);
+}
+
+/** [happenedAtMost] matches a maximum number of calls. */
+Matcher happenedAtMost(count) {
+ return new _TimesMatcher(0, count);
+}
+
+/** [neverHappened] matches zero calls. */
+const Matcher neverHappened = const _TimesMatcher(0, 0);
+
+/** [happenedOnce] matches exactly one call. */
+const Matcher happenedOnce = const _TimesMatcher(1, 1);
+
+/** [happenedAtLeastOnce] matches one or more calls. */
+const Matcher happenedAtLeastOnce = const _TimesMatcher(1);
+
+/** [happenedAtMostOnce] matches zero or one call. */
+const Matcher happenedAtMostOnce = const _TimesMatcher(0, 1);
diff --git a/pkg/mock/lib/src/util.dart b/pkg/mock/lib/src/util.dart
new file mode 100644
index 0000000..5341b88
--- /dev/null
+++ b/pkg/mock/lib/src/util.dart
@@ -0,0 +1,28 @@
+// 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.
+
+library mock.util;
+
+import 'package:matcher/matcher.dart';
+
+/** Utility function for optionally qualified method names */
+String qualifiedName(owner, String method) {
+ if (owner == null || identical(owner, anything)) {
+ return method;
+ } else if (owner is Matcher) {
+ Description d = new StringDescription();
+ d.addDescriptionOf(owner);
+ d.add('.');
+ d.add(method);
+ return d.toString();
+ } else {
+ return '$owner.$method';
+ }
+}
+
+/** Sentinel value for representing no argument. */
+class _Sentinel {
+ const _Sentinel();
+}
+const NO_ARG = const _Sentinel();
diff --git a/pkg/mock/pubspec.yaml b/pkg/mock/pubspec.yaml
index 33acbdb..2220370 100644
--- a/pkg/mock/pubspec.yaml
+++ b/pkg/mock/pubspec.yaml
@@ -1,5 +1,5 @@
name: mock
-version: 0.10.0+1
+version: 0.10.1-dev
author: Dart Team <misc@dartlang.org>
description: A library for mocking classes
homepage: http://www.dartlang.org
diff --git a/pkg/observe/pubspec.yaml b/pkg/observe/pubspec.yaml
index bf2bd93..83b924c 100644
--- a/pkg/observe/pubspec.yaml
+++ b/pkg/observe/pubspec.yaml
@@ -1,5 +1,5 @@
name: observe
-version: 0.10.0-pre.4.dev
+version: 0.10.0-pre.4
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
description: >
Observable properties and objects for use in template_binding.
diff --git a/pkg/pkg.gyp b/pkg/pkg.gyp
index 9e37d4d..df21e10 100644
--- a/pkg/pkg.gyp
+++ b/pkg/pkg.gyp
@@ -18,7 +18,7 @@
'<!@(["python", "../tools/list_pkg_directories.py", '
'"../third_party/pkg"])',
'<!@(["python", "../tools/list_pkg_directories.py", '
- '"../pkg/polymer/example/"])',
+ '"polymer/example/"])',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/packages.stamp',
@@ -31,6 +31,6 @@
],
},
],
- }
+ },
],
}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index b0bd22e..13de742 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -17,39 +17,32 @@
scheduled_test/test/scheduled_server_test: Pass, Fail # 13524
scheduled_test/test/scheduled_process_test: Pass, Slow # Issue 9231
-[ $compiler == dart2js && $mode == debug ]
-docgen/test/generate_json_test: Skip # Way too slow
-docgen/test/multi_library_test: Skip # Way too slow
-docgen/test/single_library_test: Skip # Way too slow
-docgen/test/typedef_test: Skip # Way too slow
-
[ $runtime == vm && $mode == debug]
-docgen/test/only_lib_content_in_pkg_test: Skip # Slow
-polymer/test/build/all_phases_test: Skip # Slow
analysis_server/test/analysis_server_test: Pass, Timeout
analysis_server/test/domain_context_test: Pass, Timeout
analysis_server/test/domain_server_test: Pass, Timeout
analyzer/test/generated/element_test: Pass, Timeout
analyzer/test/generated/parser_test: Pass, Timeout
code_transformers/test/resolver_test: Pass, Timeout
-docgen/test/only_lib_content_in_pkg_test: Pass, Timeout
-polymer_expressions/test/globals_test: Pass, Timeout
-polymer/test/build/all_phases_test: Pass, Timeout
+docgen/test/*: Skip # Slow
+polymer/test/build/all_phases_test: Skip # Slow
polymer/test/build/script_compactor_test: Pass, Timeout
+polymer_expressions/test/globals_test: Pass, Timeout
smoke/test/codegen/end_to_end_test: Pass, Timeout
smoke/test/codegen/recorder_test: Pass, Timeout
template_binding/test/template_binding_test: Pass, Timeout
third_party/html5lib/test/tokenizer_test: Pass, Timeout
[ $runtime == vm && ( $arch == simarm || $arch == simmips ) ]
-docgen/test/only_lib_content_in_pkg_test: Skip # slow
-docgen/test/typedef_test: Skip # slow
barback/test/too_many_open_files_test: Skip # 14220
-third_party/html5lib/test/tokenizer_test: Pass, Slow
-docgen/test/generate_json_test: Skip # Issue 17003
-polymer/test/build/script_compactor_test: Skip # Slow
-polymer/test/build/all_phases_test: Skip # Slow
code_transformers/test/resolver_test: Skip # Issue 17908
+docgen/test/*: Skip # Issue 17003
+polymer/test/build/all_phases_test: Skip # Slow
+polymer/test/build/script_compactor_test: Skip # Slow
+third_party/html5lib/test/tokenizer_test: Pass, Slow
+
+[ $runtime == vm ]
+docgen/test/typedef_test: RuntimeError # Issue 18352
[ $compiler == dart2js ]
collection/test/equality_test/01: Fail # Issue 1533
@@ -58,7 +51,7 @@
collection/test/equality_test/04: Fail # Issue 1533
collection/test/equality_test/05: Fail # Issue 1533
collection/test/equality_test/none: Pass, Fail # Issue 14348
-docgen/test/multi_library_test: Slow, Pass # issue 17060
+docgen/test/*: Skip # Far too slow
third_party/angular_tests/browser_test: Pass, Slow # Large dart2js compile time
typed_data/test/typed_buffers_test/01: Fail # Not supporting Int64List, Uint64List.
@@ -72,14 +65,15 @@
[ $runtime == jsshell ]
analyzer/test/generated/element_test: Pass, Slow # Issue 16473
-docgen/test/typedef_test: Slow, Pass # issue 17060
[ $runtime == d8 || $runtime == jsshell ]
-async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
-scheduled_test/test/unittest_compatibility_test: RuntimeError # Issue 7728
stack_trace/test/chain_test: Fail # Issues 15171 and 15105
stack_trace/test/vm_test: RuntimeError, OK # VM-specific traces
unittest/test/missing_tick_test: Fail # Timer interface not supported: dartbug.com/7728.
+
+[ $runtime == jsshell ]
+async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
+scheduled_test/test/unittest_compatibility_test: RuntimeError # Issue 7728
unittest/test/unittest_nested_groups_setup_teardown_test: RuntimeError # http://dartbug.com/10109
[ $runtime == vm || $runtime == d8 || $runtime == jsshell ]
@@ -167,7 +161,6 @@
polymer/example/canonicalization3: Skip
third_party/angular_tests/browser_test: StaticWarning # Issue 15890
-docgen/test/*: StaticWarning # Issue 17896
[ $compiler == dart2js && $runtime == none]
polymer/example/canonicalization: Skip
@@ -191,14 +184,13 @@
[ $compiler == dart2js && $browser ]
stack_trace/test/vm_test: Fail, OK # VM-specific traces
-stack_trace/test/chain_test: Fail, Timeout # Issues 15171, 15105, and 18142
+stack_trace/test/chain_test: Fail # Issues 15171 and 15105
crypto/test/sha256_test: Slow, Pass
crypto/test/sha1_test: Slow, Pass
polymer/example/component: Fail # Issue 13198
polymer/test/mirror_loader_test: Skip # tests development only behavior
[ $compiler == dart2js && $runtime == chromeOnAndroid ]
-docgen/test/single_library_test: Fail # TODO(kasperl): Please triage.
intl/test/date_time_format_http_request_test: Fail # TODO(kasperl): Please triage.
[ $browser ]
diff --git a/pkg/pkg_files.gyp b/pkg/pkg_files.gyp
new file mode 100644
index 0000000..df3a986
--- /dev/null
+++ b/pkg/pkg_files.gyp
@@ -0,0 +1,34 @@
+# 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.
+
+{
+ 'targets': [
+ # Other targets depend on pkg files, but have to many inputs, which causes
+ # issues on some platforms.
+ # This target lists all the files in pkg and third_party/pkg,
+ # and creates a single pkg_files.stamp
+ {
+ 'target_name': 'pkg_files_stamp',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'make_pkg_files_stamp',
+ 'inputs': [
+ '../tools/create_timestamp_file.py',
+ '<!@(["python", "../tools/list_files.py", "\\.dart$", "."])',
+ '<!@(["python", "../tools/list_files.py", "\\.dart$",'
+ '"../third_party/pkg"])',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/pkg_files.stamp',
+ ],
+ 'action': [
+ 'python', '../tools/create_timestamp_file.py',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }
+ ],
+}
diff --git a/pkg/pkgbuild.status b/pkg/pkgbuild.status
index ea8f0de..7e856fd 100644
--- a/pkg/pkgbuild.status
+++ b/pkg/pkgbuild.status
@@ -2,8 +2,6 @@
# 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.
-third_party/pkg/route_hierarchical: Fail
-
samples/third_party/angular_todo: Pass, Slow
samples/third_party/dromaeo: Pass, Slow
samples/third_party/todomvc_performance: Pass, Slow
@@ -28,15 +26,3 @@
[ $system == windows ]
samples/third_party/todomvc_performance: Fail # Issue 18086
-
-[ $use_public_packages ]
-pkg/observe: PubGetError # Issue 18115
-pkg/polymer: PubGetError # Issue 18115
-pkg/polymer_expressions: PubGetError # Issue 18115
-pkg/template_binding: PubGetError # Issue 18115
-samples/polymer_intl: PubGetError # Issue 18115
-samples/searchable_list: PubGetError # Issue 18115
-samples/third_party/angular_todo: PubGetError # Issue 18115
-samples/third_party/todomvc: PubGetError # Issue 18115
-samples/third_party/todomvc_performance: PubGetError # Issue 18115
-samples/tracker: PubGetError # Issue 18115
diff --git a/pkg/polymer/lib/src/build/import_inliner.dart b/pkg/polymer/lib/src/build/import_inliner.dart
index ee2aee0..e785315 100644
--- a/pkg/polymer/lib/src/build/import_inliner.dart
+++ b/pkg/polymer/lib/src/build/import_inliner.dart
@@ -28,6 +28,7 @@
final AssetId docId;
final seen = new Set<AssetId>();
final scriptIds = <AssetId>[];
+ final extractedFiles = new Set<AssetId>();
/// The number of extracted inline Dart scripts. Used as a counter to give
/// unique-ish filenames.
@@ -51,8 +52,10 @@
changed = _extractScripts(document, docId);
return _visitImports(document);
}).then((importsFound) {
- bool scriptsRemoved = _removeScripts(document);
- changed = changed || importsFound || scriptsRemoved;
+ changed = changed || importsFound;
+ return _removeScripts(document);
+ }).then((scriptsRemoved) {
+ changed = changed || scriptsRemoved;
var output = transform.primaryInput;
if (changed) output = new Asset.fromString(docId, document.outerHtml);
@@ -160,17 +163,32 @@
///
/// Dartium only allows a single script tag per page, so we can't inline
/// the script tags. Instead we remove them entirely.
- bool _removeScripts(Document doc) {
+ Future<bool> _removeScripts(Document doc) {
bool changed = false;
- for (var script in doc.querySelectorAll('script')) {
+ return Future.forEach(doc.querySelectorAll('script'), (script) {
if (script.attributes['type'] == TYPE_DART_COMPONENT) {
changed = true;
script.remove();
var src = script.attributes['src'];
- scriptIds.add(uriToAssetId(docId, src, logger, script.sourceSpan));
+ var srcId = uriToAssetId(docId, src, logger, script.sourceSpan);
+
+ // We check for extractedFiles because 'hasInput' below is only true for
+ // assets that existed before this transformer runs (hasInput is false
+ // for files created by [_extractScripts]).
+ if (extractedFiles.contains(srcId)) {
+ scriptIds.add(srcId);
+ return true;
+ }
+ return transform.hasInput(srcId).then((exists) {
+ if (!exists) {
+ logger.warning('Script file at "$src" not found.',
+ span: script.sourceSpan);
+ } else {
+ scriptIds.add(srcId);
+ }
+ });
}
- }
- return changed;
+ }).then((_) => changed);
}
/// Split inline scripts into their own files. We need to do this for dart2js
@@ -219,6 +237,7 @@
code = "library $libName;\n$code";
}
+ extractedFiles.add(newId);
transform.addOutput(new Asset.fromString(newId, code));
}
return changed;
diff --git a/pkg/polymer/lib/src/build/script_compactor.dart b/pkg/polymer/lib/src/build/script_compactor.dart
index 4690889..b90dfe1 100644
--- a/pkg/polymer/lib/src/build/script_compactor.dart
+++ b/pkg/polymer/lib/src/build/script_compactor.dart
@@ -139,26 +139,32 @@
/// Emits the main HTML and Dart bootstrap code for the application. If there
/// were not Dart entry point files, then this simply emits the original HTML.
Future _emitNewEntrypoint(_) {
- if (entryLibraries.isEmpty) {
- // We didn't find code, nothing to do.
- transform.addOutput(transform.primaryInput);
- return null;
- }
-
+ // If we don't find code, there is nothing to do.
+ if (entryLibraries.isEmpty) return null;
return _initResolver()
.then(_extractUsesOfMirrors)
.then(_emitFiles)
- .whenComplete(() => resolver.release());
+ .whenComplete(() {
+ if (resolver != null) resolver.release();
+ });
}
/// Load a resolver that computes information for every library in
/// [entryLibraries], then use it to initialize the [recorder] (for import
/// resolution) and to resolve specific elements (for analyzing the user's
/// code).
- Future _initResolver() => resolvers.get(transform, entryLibraries).then((r) {
- resolver = r;
- types = new _ResolvedTypes(resolver);
- });
+ Future _initResolver() {
+ // We include 'polymer.dart' to simplify how we do resolution below. This
+ // way we can assume polymer is there, even if the user didn't include an
+ // import to it. If not, the polymer build will fail with an error when
+ // trying to create _ResolvedTypes below.
+ var libsToLoad = [new AssetId('polymer', 'lib/polymer.dart')]
+ ..addAll(entryLibraries);
+ return resolvers.get(transform, libsToLoad).then((r) {
+ resolver = r;
+ types = new _ResolvedTypes(resolver);
+ });
+ }
/// Inspects the entire program to find out anything that polymer accesses
/// using mirrors and produces static information that can be used to replace
@@ -343,15 +349,21 @@
generator.writeTopLevelDeclarations(code);
code.writeln('\nvoid main() {');
generator.writeInitCall(code);
- code.writeln(' startPolymer([');
+ code.write(' startPolymer([');
// Include initializers to switch from mirrors_loader to static_loader.
- for (var init in initializers) {
- var initCode = init.asCode(prefixes[init.assetId]);
- code.write(" $initCode,\n");
+ if (!initializers.isEmpty) {
+ code.writeln();
+ for (var init in initializers) {
+ var initCode = init.asCode(prefixes[init.assetId]);
+ code.write(" $initCode,\n");
+ }
+ code.writeln(' ]);');
+ } else {
+ logger.warning(NO_INITIALIZERS_ERROR);
+ code.writeln(']);');
}
- code..writeln(' ]);')
- ..writeln('}');
+ code.writeln('}');
transform.addOutput(new Asset.fromString(bootstrapId, code.toString()));
@@ -402,6 +414,13 @@
import 'package:polymer/polymer.dart';
""";
+const NO_INITIALIZERS_ERROR =
+ 'No polymer initializers were found. Make sure to either '
+ 'annotate your polymer elements with @CustomTag or include a '
+ 'top level method annotated with @initMethod that registers your '
+ 'elements. Both annotations are defined in the polymer library ('
+ 'package:polymer/polymer.dart).';
+
/// An html visitor that:
/// * finds all polymer expressions and records the getters and setters that
/// will be needed to evaluate them at runtime.
@@ -538,7 +557,7 @@
visitInvoke(pe.Invoke e) {
_includeSetter = false; // Invoke is only valid as an r-value.
- _add(e.method);
+ if (e.method != null) _add(e.method);
super.visitInvoke(e);
}
}
diff --git a/pkg/polymer/pubspec.yaml b/pkg/polymer/pubspec.yaml
index aca5df5..05badf4 100644
--- a/pkg/polymer/pubspec.yaml
+++ b/pkg/polymer/pubspec.yaml
@@ -1,5 +1,5 @@
name: polymer
-version: 0.10.0-pre.9.dev
+version: 0.10.0-pre.9
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
description: >
Polymer.dart is a new type of library for the web, built on top of Web
diff --git a/pkg/polymer/test/bind_mdv_test.html b/pkg/polymer/test/bind_mdv_test.html
index 2539932..1f23b31 100644
--- a/pkg/polymer/test/bind_mdv_test.html
+++ b/pkg/polymer/test/bind_mdv_test.html
@@ -17,8 +17,6 @@
<h1> Running bind_mdv_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/pkg/polymer/test/build/code_extractor.dart b/pkg/polymer/test/build/code_extractor.dart
index beeafcb..813d13d 100644
--- a/pkg/polymer/test/build/code_extractor.dart
+++ b/pkg/polymer/test/build/code_extractor.dart
@@ -164,6 +164,7 @@
});
group('fixes import/export/part URIs', dartUriTests);
+ group('validates script-tag URIs', validateUriTests);
}
dartUriTests() {
@@ -312,3 +313,55 @@
'</body></html>',
});
}
+
+validateUriTests() {
+
+ testPhases('script is inline', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><body>'
+ '<script type="application/dart;component=1">'
+ 'main(){}'
+ '</script>'
+ '</body></html>',
+ }, {
+ 'a|web/test.html.scriptUrls': '[["a","web/test.html.0.dart"]]',
+ }, []);
+
+ testPhases('script src is valid', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><body>'
+ '<script type="application/dart;component=1" src="a.dart"></script>'
+ '</body></html>',
+ 'a|web/a.dart': 'main() {}',
+ }, {
+ 'a|web/test.html.scriptUrls': '[["a","web/a.dart"]]',
+ }, []);
+
+ testPhases('script src is invalid', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><body>\n'
+ '<script type="application/dart;component=1" src="a.dart"></script>'
+ '</body></html>',
+ }, {
+ 'a|web/test.html.scriptUrls': '[]',
+ }, [
+ 'warning: Script file at "a.dart" not found. (web/test.html 1 0)',
+ ]);
+
+ testPhases('many script src around, valid and invalid', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><body>'
+ '<script type="application/dart;component=1" src="a.dart"></script>'
+ '\n<script type="application/dart;component=1" src="b.dart"></script>'
+ '\n<script type="application/dart;component=1" src="c.dart"></script>'
+ '\n<script type="application/dart;component=1" src="d.dart"></script>'
+ '</body></html>',
+ 'a|web/a.dart': 'main() {}',
+ 'a|web/c.dart': 'main() {}',
+ }, {
+ 'a|web/test.html.scriptUrls': '[["a","web/a.dart"],["a","web/c.dart"]]',
+ }, [
+ 'warning: Script file at "b.dart" not found. (web/test.html 1 0)',
+ 'warning: Script file at "d.dart" not found. (web/test.html 3 0)',
+ ]);
+}
diff --git a/pkg/polymer/test/build/import_inliner_test.dart b/pkg/polymer/test/build/import_inliner_test.dart
index 8741ac1..6684c5b 100644
--- a/pkg/polymer/test/build/import_inliner_test.dart
+++ b/pkg/polymer/test/build/import_inliner_test.dart
@@ -436,6 +436,8 @@
'<link rel="import" href="test_1.html">'
'</head><body><polymer-element>2</polymer-element>'
'<script type="application/dart" src="s2.dart"></script></html>',
+ 'a|web/s1.dart': '',
+ 'a|web/s2.dart': '',
}, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
@@ -484,6 +486,8 @@
'<script type="application/dart;component=1" src="s2.dart"></script>'
'</polymer-element>'
'</html>',
+ 'a|web/s1.dart': '',
+ 'a|web/s2.dart': '',
}, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
@@ -525,6 +529,7 @@
'<script type="application/dart;component=1" src="s1.dart"></script>'
'</polymer-element>'
'FOO</body></html>',
+ 'a|web/s1.dart': '',
}, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
@@ -813,6 +818,4 @@
'a|web/test2.css':
'h1 { font-size: 70px; }',
});
-
}
-
diff --git a/pkg/polymer/test/build/script_compactor_test.dart b/pkg/polymer/test/build/script_compactor_test.dart
index 862ceb3..6c3119e 100644
--- a/pkg/polymer/test/build/script_compactor_test.dart
+++ b/pkg/polymer/test/build/script_compactor_test.dart
@@ -108,7 +108,7 @@
}
'''.replaceAll('\n ', '\n'),
});
-
+
testPhases('use const expressions', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>',
@@ -163,9 +163,59 @@
'class XFoo extends PolymerElement {\n'
'}\n'
'main(){}',
- }, {}, [
+ }, {
+ 'a|web/test.html_bootstrap.dart':
+ '''$MAIN_HEADER
+ import 'a.dart' as i0;
+ ${DEFAULT_IMPORTS.join('\n')}
+ import 'a.dart' as smoke_0;
+ import 'package:polymer/polymer.dart' as smoke_1;
+
+ void main() {
+ useGeneratedCode(new StaticConfiguration(
+ checkedMode: false,
+ parents: {
+ smoke_0.XFoo: smoke_1.PolymerElement,
+ },
+ declarations: {
+ smoke_0.XFoo: const {},
+ }));
+ startPolymer([]);
+ }
+ '''.replaceAll('\n ', '\n'),
+
+ }, [
'warning: The parameter to @CustomTag seems to be invalid. '
'(web/a.dart 2 11)',
+ 'warning: $NO_INITIALIZERS_ERROR',
+ ]);
+
+ testPhases('no polymer import (no warning, but no crash either)', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head>',
+ 'a|web/test.html.scriptUrls': '[["a","web/a.dart"]]',
+ 'a|web/a.dart':
+ 'library a;\n'
+ 'import "package:polymer/polymer.broken.import.dart";\n'
+ '@CustomTag("x-foo")\n'
+ 'class XFoo extends PolymerElement {\n'
+ '}\n'
+ 'main(){}',
+ }, {
+ 'a|web/test.html_bootstrap.dart':
+ '''$MAIN_HEADER
+ import 'a.dart' as i0;
+ ${DEFAULT_IMPORTS.join('\n')}
+
+ void main() {
+ useGeneratedCode(new StaticConfiguration(
+ checkedMode: false));
+ startPolymer([]);
+ }
+ '''.replaceAll('\n ', '\n'),
+
+ }, [
+ 'warning: $NO_INITIALIZERS_ERROR',
]);
testPhases('several scripts', phases, {
@@ -256,7 +306,7 @@
smoke_2.XF1: smoke_1.PolymerElement,
smoke_3.XG2: smoke_1.PolymerElement,
smoke_4.XH1: smoke_1.PolymerElement,
- },
+ },
declarations: {
smoke_5.XC1: const {},
smoke_5.XC2: const {},
@@ -292,6 +342,8 @@
'<polymer-element name="foo-bar"><template>'
'<div>{{a.node}}</div>'
'<div>{{anotherNode}}</div>'
+ '<div>{{a.call1(a)}}</div>'
+ '<div>{{call2(a)}}</div>'
'<div class="{{an.attribute}}"></div>'
'<a href="path/{{within.an.attribute}}/foo/bar"></a>'
'<div data-attribute="{{anotherAttribute}}"></div>'
@@ -322,6 +374,8 @@
#anotherAttribute: (o) => o.anotherAttribute,
#anotherNode: (o) => o.anotherNode,
#attribute: (o) => o.attribute,
+ #call1: (o) => o.call1,
+ #call2: (o) => o.call2,
#here: (o) => o.here,
#intToStringTransformer: (o) => o.intToStringTransformer,
#is: (o) => o.is,
@@ -345,6 +399,8 @@
#anotherAttribute: r'anotherAttribute',
#anotherNode: r'anotherNode',
#attribute: r'attribute',
+ #call1: r'call1',
+ #call2: r'call2',
#here: r'here',
#intToStringTransformer: r'intToStringTransformer',
#is: r'is',
diff --git a/pkg/scheduled_test/CHANGELOG.md b/pkg/scheduled_test/CHANGELOG.md
index 86d33dd..7f095a1 100644
--- a/pkg/scheduled_test/CHANGELOG.md
+++ b/pkg/scheduled_test/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.11.0+1
+
+* Support `v0.5.0` of `shelf`
+
## 0.11.0
* `ScheduledServer.handle` now takes a `shelf.Handler` rather than a custom
diff --git a/pkg/scheduled_test/pubspec.yaml b/pkg/scheduled_test/pubspec.yaml
index 12316a9..4066bfa 100644
--- a/pkg/scheduled_test/pubspec.yaml
+++ b/pkg/scheduled_test/pubspec.yaml
@@ -1,5 +1,5 @@
name: scheduled_test
-version: 0.11.0
+version: 0.11.0+1
author: Dart Team <misc@dartlang.org>
homepage: http://www.dartlang.org
description: >
@@ -12,7 +12,7 @@
dependencies:
http: '>=0.9.0 <0.11.0'
path: '>=0.9.0 <2.0.0'
- shelf: '>=0.4.0 <0.5.0'
+ shelf: '>=0.4.0 <0.6.0'
stack_trace: '>=0.9.1 <0.10.0'
unittest: '>=0.9.0 <0.11.0'
environment:
diff --git a/pkg/scheduled_test/test/scheduled_server_test.dart b/pkg/scheduled_test/test/scheduled_server_test.dart
index 9b92820..53e4ac1 100644
--- a/pkg/scheduled_test/test/scheduled_server_test.dart
+++ b/pkg/scheduled_test/test/scheduled_server_test.dart
@@ -191,7 +191,7 @@
/// Creates a metatest that runs [testBody], captures its schedule errors, and
/// asserts that it throws an error with the given [errorMessage].
-void expectServerError(String description, void testBody(),
+void expectServerError(String description, testBody(),
String errorMessage) {
expectTestFails(description, testBody, (errors) {
// There can be between one and three errors here. The first is the
diff --git a/pkg/scheduled_test/test/utils.dart b/pkg/scheduled_test/test/utils.dart
index 2d27b2d..11d04fe 100644
--- a/pkg/scheduled_test/test/utils.dart
+++ b/pkg/scheduled_test/test/utils.dart
@@ -62,7 +62,7 @@
/// Creates a metatest with [body] and asserts that it passes.
///
/// This is like [expectTestsPass], but the [test] is set up automatically.
-void expectTestPasses(String description, Future body()) =>
+void expectTestPasses(String description, body()) =>
expectTestsPass(description, () => test('test', body));
/// Creates a metatest that runs [testBody], captures its schedule errors, and
diff --git a/pkg/shelf/CHANGELOG.md b/pkg/shelf/CHANGELOG.md
index caefe02..a8ec616 100644
--- a/pkg/shelf/CHANGELOG.md
+++ b/pkg/shelf/CHANGELOG.md
@@ -1,3 +1,16 @@
+## 0.5.1
+
+* Add a `context` map to `Request` and `Response` for passing data among
+ handlers and middleware.
+
+## 0.5.0+1
+
+* Allow `scheduled_test` development dependency up to v0.12.0
+
+## 0.5.0
+
+* Renamed `Stack` to `Pipeline`.
+
## 0.4.0
* Access to headers for `Request` and `Response` is now case-insensitive.
diff --git a/pkg/shelf/README.md b/pkg/shelf/README.md
index 9456481..7f527d0 100644
--- a/pkg/shelf/README.md
+++ b/pkg/shelf/README.md
@@ -20,7 +20,7 @@
import 'package:shelf/shelf_io.dart' as io;
void main() {
- var handler = const shelf.Stack().addMiddleware(shelf.logRequests())
+ var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080).then((server) {
diff --git a/pkg/shelf/example/example_server.dart b/pkg/shelf/example/example_server.dart
index 49fda9e..ca0210b 100644
--- a/pkg/shelf/example/example_server.dart
+++ b/pkg/shelf/example/example_server.dart
@@ -6,7 +6,7 @@
import 'package:shelf/shelf_io.dart' as io;
void main() {
- var handler = const shelf.Stack().addMiddleware(shelf.logRequests())
+ var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080).then((server) {
diff --git a/pkg/shelf/lib/shelf.dart b/pkg/shelf/lib/shelf.dart
index eb339c0..09ed47a 100644
--- a/pkg/shelf/lib/shelf.dart
+++ b/pkg/shelf/lib/shelf.dart
@@ -4,9 +4,9 @@
library shelf;
+export 'src/handler.dart';
export 'src/handlers/logger.dart';
export 'src/middleware.dart';
+export 'src/pipeline.dart';
export 'src/request.dart';
export 'src/response.dart';
-export 'src/stack.dart';
-export 'src/handler.dart';
diff --git a/pkg/shelf/lib/src/message.dart b/pkg/shelf/lib/src/message.dart
index ad64d10..1b2e966 100644
--- a/pkg/shelf/lib/src/message.dart
+++ b/pkg/shelf/lib/src/message.dart
@@ -20,6 +20,19 @@
/// The value is immutable.
final Map<String, String> headers;
+ /// Extra context that can be used by for middleware and handlers.
+ ///
+ /// For requests, this is used to pass data to inner middleware and handlers;
+ /// for responses, it's used to pass data to outer middleware and handlers.
+ ///
+ /// Context properties that are used by a particular package should begin with
+ /// that package's name followed by a period. For example, if [logRequests]
+ /// wanted to take a prefix, its property name would be `"shelf.prefix"`,
+ /// since it's in the `shelf` package.
+ ///
+ /// The value is immutable.
+ final Map<String, Object> context;
+
/// The streaming body of the message.
///
/// This can be read via [read] or [readAsString].
@@ -28,8 +41,11 @@
/// Creates a new [Message].
///
/// If [headers] is `null`, it is treated as empty.
- Message(this._body, {Map<String, String> headers})
- : this.headers = _getIgnoreCaseMapView(headers);
+ Message(this._body, {Map<String, String> headers,
+ Map<String, Object> context})
+ : this.headers = _getIgnoreCaseMapView(headers),
+ this.context = new pc.UnmodifiableMapView(
+ context == null ? const {} : new Map.from(context));
/// The contents of the content-length field in [headers].
///
diff --git a/pkg/shelf/lib/src/stack.dart b/pkg/shelf/lib/src/pipeline.dart
similarity index 66%
rename from pkg/shelf/lib/src/stack.dart
rename to pkg/shelf/lib/src/pipeline.dart
index 3aacf72..016e5fc 100644
--- a/pkg/shelf/lib/src/stack.dart
+++ b/pkg/shelf/lib/src/pipeline.dart
@@ -2,7 +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.
-library shelf.stack;
+library shelf.pipeline;
import 'handler.dart';
import 'middleware.dart';
@@ -10,36 +10,36 @@
/// A helper that makes it easy to compose a set of [Middleware] and a
/// [Handler].
///
-/// var handler = const Stack()
+/// var handler = const Pipeline()
/// .addMiddleware(loggingMiddleware)
/// .addMiddleware(cachingMiddleware)
/// .addHandler(application);
-class Stack {
- final Stack _parent;
+class Pipeline {
+ final Pipeline _parent;
final Middleware _middleware;
- const Stack()
+ const Pipeline()
: _middleware = null,
_parent = null;
- Stack._(this._middleware, this._parent);
+ Pipeline._(this._middleware, this._parent);
- /// Returns a new [Stack] with [middleware] added to the existing set of
+ /// Returns a new [Pipeline] with [middleware] added to the existing set of
/// [Middleware].
///
/// [middleware] will be the last [Middleware] to process a request and
/// the first to process a response.
- Stack addMiddleware(Middleware middleware) =>
- new Stack._(middleware, this);
+ Pipeline addMiddleware(Middleware middleware) =>
+ new Pipeline._(middleware, this);
/// Returns a new [Handler] with [handler] as the final processor of a
- /// [Request] if all of the middleware in the stack have passed the request
+ /// [Request] if all of the middleware in the pipeline have passed the request
/// through.
Handler addHandler(Handler handler) {
if (_middleware == null) return handler;
return _parent.addHandler(_middleware(handler));
}
- /// Exposes this stack of [Middleware] as a single middleware instance.
+ /// Exposes this pipeline of [Middleware] as a single middleware instance.
Middleware get middleware => addHandler;
}
diff --git a/pkg/shelf/lib/src/request.dart b/pkg/shelf/lib/src/request.dart
index 71c4e98..f7e011d 100644
--- a/pkg/shelf/lib/src/request.dart
+++ b/pkg/shelf/lib/src/request.dart
@@ -71,14 +71,14 @@
// TODO(kevmoo) finish documenting the rest of the arguments.
Request(this.method, Uri requestedUri, {String protocolVersion,
Map<String, String> headers, Uri url, String scriptName,
- Stream<List<int>> body})
+ Stream<List<int>> body, Map<String, Object> context})
: this.requestedUri = requestedUri,
this.protocolVersion = protocolVersion == null ?
'1.1' : protocolVersion,
this.url = _computeUrl(requestedUri, url, scriptName),
this.scriptName = _computeScriptName(requestedUri, url, scriptName),
super(body == null ? new Stream.fromIterable([]) : body,
- headers: headers) {
+ headers: headers, context: context) {
if (method.isEmpty) throw new ArgumentError('method cannot be empty.');
// TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed
diff --git a/pkg/shelf/lib/src/response.dart b/pkg/shelf/lib/src/response.dart
index bbc268b..e59b0c5 100644
--- a/pkg/shelf/lib/src/response.dart
+++ b/pkg/shelf/lib/src/response.dart
@@ -53,8 +53,10 @@
/// If [encoding] is passed, the "encoding" field of the Content-Type header
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
- Response.ok(body, {Map<String, String> headers, Encoding encoding})
- : this(200, body: body, headers: headers, encoding: encoding);
+ Response.ok(body, {Map<String, String> headers, Encoding encoding,
+ Map<String, Object> context})
+ : this(200, body: body, headers: headers, encoding: encoding,
+ context: context);
/// Constructs a 301 Moved Permanently response.
///
@@ -71,8 +73,9 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response.movedPermanently(location, {body, Map<String, String> headers,
- Encoding encoding})
- : this._redirect(301, location, body, headers, encoding);
+ Encoding encoding, Map<String, Object> context})
+ : this._redirect(301, location, body, headers, encoding,
+ context: context);
/// Constructs a 302 Found response.
///
@@ -89,8 +92,9 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response.found(location, {body, Map<String, String> headers,
- Encoding encoding})
- : this._redirect(302, location, body, headers, encoding);
+ Encoding encoding, Map<String, Object> context})
+ : this._redirect(302, location, body, headers, encoding,
+ context: context);
/// Constructs a 303 See Other response.
///
@@ -108,17 +112,20 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response.seeOther(location, {body, Map<String, String> headers,
- Encoding encoding})
- : this._redirect(303, location, body, headers, encoding);
+ Encoding encoding, Map<String, Object> context})
+ : this._redirect(303, location, body, headers, encoding,
+ context: context);
/// Constructs a helper constructor for redirect responses.
Response._redirect(int statusCode, location, body,
- Map<String, String> headers, Encoding encoding)
+ Map<String, String> headers, Encoding encoding,
+ { Map<String, Object> context })
: this(statusCode,
body: body,
encoding: encoding,
headers: _addHeader(
- headers, 'location', _locationToString(location)));
+ headers, 'location', _locationToString(location)),
+ context: context);
/// Constructs a 304 Not Modified response.
///
@@ -126,9 +133,11 @@
/// information used to determine whether the requested resource has changed
/// since the last request. It indicates that the resource has not changed and
/// the old value should be used.
- Response.notModified({Map<String, String> headers})
+ Response.notModified({Map<String, String> headers,
+ Map<String, Object> context})
: this(304, headers: _addHeader(
- headers, 'date', formatHttpDate(new DateTime.now())));
+ headers, 'date', formatHttpDate(new DateTime.now())),
+ context: context);
/// Constructs a 403 Forbidden response.
///
@@ -143,8 +152,9 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response.forbidden(body, {Map<String, String> headers,
- Encoding encoding})
- : this(403, body: body, headers: headers);
+ Encoding encoding, Map<String, Object> context})
+ : this(403, body: body, headers: headers,
+ context: context);
/// Constructs a 404 Not Found response.
///
@@ -159,8 +169,10 @@
/// If [encoding] is passed, the "encoding" field of the Content-Type header
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
- Response.notFound(body, {Map<String, String> headers, Encoding encoding})
- : this(404, body: body, headers: headers);
+ Response.notFound(body, {Map<String, String> headers, Encoding encoding,
+ Map<String, Object> context})
+ : this(404, body: body, headers: headers,
+ context: context);
/// Constructs a 500 Internal Server Error response.
///
@@ -176,10 +188,11 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response.internalServerError({body, Map<String, String> headers,
- Encoding encoding})
+ Encoding encoding, Map<String, Object> context})
: this(500,
headers: body == null ? _adjust500Headers(headers) : headers,
- body: body == null ? 'Internal Server Error' : body);
+ body: body == null ? 'Internal Server Error' : body,
+ context: context);
/// Constructs an HTTP response with the given [statusCode].
///
@@ -194,9 +207,10 @@
/// in [headers] will be set appropriately. If there is no existing
/// Content-Type header, it will be set to "application/octet-stream".
Response(this.statusCode, {body, Map<String, String> headers,
- Encoding encoding})
+ Encoding encoding, Map<String, Object> context})
: super(_bodyToStream(body, encoding),
- headers: _adjustHeaders(headers, encoding)) {
+ headers: _adjustHeaders(headers, encoding),
+ context: context) {
if (statusCode < 100) {
throw new ArgumentError("Invalid status code: $statusCode.");
}
diff --git a/pkg/shelf/pubspec.yaml b/pkg/shelf/pubspec.yaml
index fcf337b..3c103d9 100644
--- a/pkg/shelf/pubspec.yaml
+++ b/pkg/shelf/pubspec.yaml
@@ -1,5 +1,5 @@
name: shelf
-version: 0.4.0
+version: 0.5.1
author: Dart Team <misc@dartlang.org>
description: Web Server Middleware for Dart
homepage: http://www.dartlang.org
@@ -13,5 +13,5 @@
stack_trace: '>=0.9.2 <0.10.0'
dev_dependencies:
http: '>=0.9.2 <0.11.0'
- scheduled_test: '>=0.10.0 <0.11.0'
+ scheduled_test: '>=0.10.0 <0.12.0'
unittest: '>=0.10.0 <0.11.0'
diff --git a/pkg/shelf/test/create_middleware_test.dart b/pkg/shelf/test/create_middleware_test.dart
index 7ce99eb..5c35f4d 100644
--- a/pkg/shelf/test/create_middleware_test.dart
+++ b/pkg/shelf/test/create_middleware_test.dart
@@ -13,7 +13,7 @@
void main() {
test('forwards the request and response if both handlers are null', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware())
.addHandler((request) {
return syncHandler(request, headers: {'from' : 'innerHandler'});
@@ -26,7 +26,7 @@
group('requestHandler', () {
test('sync null response forwards to inner handler', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(requestHandler: (request) => null))
.addHandler(syncHandler);
@@ -36,7 +36,7 @@
});
test('async null response forwards to inner handler', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(
requestHandler: (request) => new Future.value(null)))
.addHandler(syncHandler);
@@ -47,7 +47,7 @@
});
test('sync response is returned', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(
requestHandler: (request) => _middlewareResponse))
.addHandler(_failHandler);
@@ -58,7 +58,7 @@
});
test('async response is returned', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(requestHandler: (request) =>
new Future.value(_middlewareResponse)))
.addHandler(_failHandler);
@@ -74,7 +74,7 @@
requestHandler: (request) => _middlewareResponse,
responseHandler: (response) => fail('should not be called'));
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(middleware)
.addHandler(syncHandler);
@@ -87,7 +87,7 @@
var middleware = createMiddleware(
requestHandler: (request) => new Future.value(_middlewareResponse),
responseHandler: (response) => fail('should not be called'));
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(middleware)
.addHandler(syncHandler);
@@ -100,7 +100,7 @@
group('responseHandler', () {
test('innerHandler sync response is seen, replaced value continues', () {
- var handler = const Stack().addMiddleware(createMiddleware(
+ var handler = const Pipeline().addMiddleware(createMiddleware(
responseHandler: (response) {
expect(response.headers['from'], 'handler');
return _middlewareResponse;
@@ -114,7 +114,7 @@
});
test('innerHandler async response is seen, async value continues', () {
- var handler = const Stack().addMiddleware(
+ var handler = const Pipeline().addMiddleware(
createMiddleware(responseHandler: (response) {
expect(response.headers['from'], 'handler');
return new Future.value(_middlewareResponse);
@@ -131,7 +131,7 @@
group('error handling', () {
test('sync error thrown by requestHandler bubbles down', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(
requestHandler: (request) => throw 'middleware error'))
.addHandler(_failHandler);
@@ -140,7 +140,7 @@
});
test('async error thrown by requestHandler bubbles down', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(createMiddleware(requestHandler: (request) =>
new Future.error('middleware error')))
.addHandler(_failHandler);
@@ -153,7 +153,7 @@
throw 'middleware error';
}, errorHandler: (e, s) => fail('should never get here'));
- var handler = const Stack().addMiddleware(middleware)
+ var handler = const Pipeline().addMiddleware(middleware)
.addHandler(syncHandler);
expect(makeSimpleRequest(handler), throwsA('middleware error'));
@@ -166,7 +166,7 @@
},
errorHandler: (e, s) => fail('should never get here'));
- var handler = const Stack().addMiddleware(middleware)
+ var handler = const Pipeline().addMiddleware(middleware)
.addHandler(syncHandler);
expect(makeSimpleRequest(handler), throwsA('middleware error'));
@@ -179,7 +179,7 @@
return _middlewareResponse;
});
- var handler = const Stack().addMiddleware(middleware)
+ var handler = const Pipeline().addMiddleware(middleware)
.addHandler((request) {
throw 'bad handler';
});
@@ -195,7 +195,7 @@
throw error;
});
- var handler = const Stack().addMiddleware(middleware)
+ var handler = const Pipeline().addMiddleware(middleware)
.addHandler((request) {
throw 'bad handler';
});
diff --git a/pkg/shelf/test/log_middleware_test.dart b/pkg/shelf/test/log_middleware_test.dart
index d8da8fa..b9a8304 100644
--- a/pkg/shelf/test/log_middleware_test.dart
+++ b/pkg/shelf/test/log_middleware_test.dart
@@ -25,7 +25,7 @@
};
test('logs a request with a synchronous response', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(logRequests(logger: logger))
.addHandler(syncHandler);
@@ -35,7 +35,7 @@
});
test('logs a request with an asynchronous response', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(logRequests(logger: logger))
.addHandler(asyncHandler);
@@ -45,7 +45,7 @@
});
test('logs a request with an asynchronous response', () {
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(logRequests(logger: (msg, isError) {
expect(gotLog, isFalse);
gotLog = true;
diff --git a/pkg/shelf/test/message_test.dart b/pkg/shelf/test/message_test.dart
index 57f32c4..1db286ba 100644
--- a/pkg/shelf/test/message_test.dart
+++ b/pkg/shelf/test/message_test.dart
@@ -11,13 +11,15 @@
import 'package:unittest/unittest.dart';
class _TestMessage extends Message {
- _TestMessage(Map<String, String> headers, Stream<List<int>> body)
- : super(body, headers: headers);
+ _TestMessage(Map<String, String> headers, Map<String, Object> context,
+ Stream<List<int>> body)
+ : super(body, headers: headers, context: context);
}
-Message _createMessage({Map<String, String> headers, Stream<List<int>> body}) {
+Message _createMessage({Map<String, String> headers,
+ Map<String, Object> context, Stream<List<int>> body}) {
if (body == null) body = new Stream.fromIterable([]);
- return new _TestMessage(headers, body);
+ return new _TestMessage(headers, context, body);
}
void main() {
@@ -43,6 +45,26 @@
expect(() => message.headers['h2'] = 'value2', throwsUnsupportedError);
});
});
+
+ group('context', () {
+ test('is accessible', () {
+ var message = _createMessage(context: {'foo': 'bar'});
+ expect(message.context, containsPair('foo', 'bar'));
+ });
+
+ test('null context value becomes empty and immutable', () {
+ var message = _createMessage();
+ expect(message.context, isEmpty);
+ expect(() => message.context['key'] = 'value', throwsUnsupportedError);
+ });
+
+ test('is immutable', () {
+ var message = _createMessage(context: {'key': 'value'});
+ expect(() => message.context['key'] = 'value', throwsUnsupportedError);
+ expect(() => message.context['key2'] = 'value', throwsUnsupportedError);
+ });
+ });
+
group("readAsString", () {
test("supports a null body", () {
var request = _createMessage();
diff --git a/pkg/shelf/test/stack_test.dart b/pkg/shelf/test/pipeline_test.dart
similarity index 88%
rename from pkg/shelf/test/stack_test.dart
rename to pkg/shelf/test/pipeline_test.dart
index 8ff29d6..eda4a12 100644
--- a/pkg/shelf/test/stack_test.dart
+++ b/pkg/shelf/test/pipeline_test.dart
@@ -2,7 +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.
-library shelf.shelf_stack_test;
+library shelf.pipeline_test;
import 'package:shelf/shelf.dart';
import 'package:unittest/unittest.dart';
@@ -10,7 +10,7 @@
import 'test_util.dart';
void main() {
- test('compose middleware with Stack', () {
+ test('compose middleware with Pipeline', () {
int accessLocation = 0;
var middlewareA = createMiddleware(requestHandler: (request) {
@@ -33,7 +33,7 @@
return response;
});
- var handler = const Stack()
+ var handler = const Pipeline()
.addMiddleware(middlewareA)
.addMiddleware(middlewareB)
.addHandler((request) {
@@ -48,7 +48,7 @@
});
});
- test('Stack can be used as middleware', () {
+ test('Pipeline can be used as middleware', () {
int accessLocation = 0;
var middlewareA = createMiddleware(requestHandler: (request) {
@@ -71,12 +71,12 @@
return response;
});
- var innerStack = const Stack()
+ var innerPipeline = const Pipeline()
.addMiddleware(middlewareA)
.addMiddleware(middlewareB);
- var handler = const Stack()
- .addMiddleware(innerStack.middleware)
+ var handler = const Pipeline()
+ .addMiddleware(innerPipeline.middleware)
.addHandler((request) {
expect(accessLocation, 2);
accessLocation = 3;
diff --git a/pkg/smoke/pubspec.yaml b/pkg/smoke/pubspec.yaml
index 96c398d..435eee5 100644
--- a/pkg/smoke/pubspec.yaml
+++ b/pkg/smoke/pubspec.yaml
@@ -1,5 +1,5 @@
name: smoke
-version: 0.1.0-pre.3.dev
+version: 0.1.0-pre.3
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
homepage: "https://api.dartlang.org/apidocs/channels/be/#smoke"
description: >
diff --git a/pkg/stack_trace/test/chain_test.dart b/pkg/stack_trace/test/chain_test.dart
index 683544f..3347deb 100644
--- a/pkg/stack_trace/test/chain_test.dart
+++ b/pkg/stack_trace/test/chain_test.dart
@@ -92,16 +92,20 @@
inMicrotask(() => throw 'first error');
inPeriodicTimer(() => throw 'second error');
}, onError: (error, chain) {
- if (first) {
- expect(error, equals('first error'));
- expect(chain.traces[1].frames,
- contains(frameMember(startsWith('inMicrotask'))));
- first = false;
- } else {
- expect(error, equals('second error'));
- expect(chain.traces[1].frames,
- contains(frameMember(startsWith('inPeriodicTimer'))));
- completer.complete();
+ try {
+ if (first) {
+ expect(error, equals('first error'));
+ expect(chain.traces[1].frames,
+ contains(frameMember(startsWith('inMicrotask'))));
+ first = false;
+ } else {
+ expect(error, equals('second error'));
+ expect(chain.traces[1].frames,
+ contains(frameMember(startsWith('inPeriodicTimer'))));
+ completer.complete();
+ }
+ } catch (error, stackTrace) {
+ completer.completeError(error, stackTrace);
}
});
@@ -121,11 +125,15 @@
throw error;
});
}, onError: (error, chain) {
- expect(error, equals('error'));
- expect(chain, new isInstanceOf<Chain>());
- expect(chain.traces[1].frames,
- contains(frameMember(startsWith('inMicrotask'))));
- completer.complete();
+ try {
+ expect(error, equals('error'));
+ expect(chain, new isInstanceOf<Chain>());
+ expect(chain.traces[1].frames,
+ contains(frameMember(startsWith('inMicrotask'))));
+ completer.complete();
+ } catch (error, stackTrace) {
+ completer.completeError(error, stackTrace);
+ }
});
return completer.future;
@@ -138,11 +146,15 @@
runZoned(() {
Chain.capture(() => inMicrotask(() => throw 'error'));
}, onError: (error, chain) {
- expect(error, equals('error'));
- expect(chain, new isInstanceOf<Chain>());
- expect(chain.traces[1].frames,
- contains(frameMember(startsWith('inMicrotask'))));
- completer.complete();
+ try {
+ expect(error, equals('error'));
+ expect(chain, new isInstanceOf<Chain>());
+ expect(chain.traces[1].frames,
+ contains(frameMember(startsWith('inMicrotask'))));
+ completer.complete();
+ } catch (error, stackTrace) {
+ completer.completeError(error, stackTrace);
+ }
});
return completer.future;
diff --git a/pkg/template_binding/test/custom_element_bindings_test.html b/pkg/template_binding/test/custom_element_bindings_test.html
index 45bdb99..cbaa772 100644
--- a/pkg/template_binding/test/custom_element_bindings_test.html
+++ b/pkg/template_binding/test/custom_element_bindings_test.html
@@ -17,8 +17,6 @@
<h1> Running custom_element_bindings_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc
index 2486c5b..9927ee8 100644
--- a/runtime/bin/builtin.cc
+++ b/runtime/bin/builtin.cc
@@ -96,7 +96,8 @@
library = Dart_LoadLibrary(url, Source(id));
if (!Dart_IsError(library) && (builtin_libraries_[id].has_natives_)) {
// Setup the native resolver for built in library functions.
- DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
+ DART_CHECK_VALID(
+ Dart_SetNativeResolver(library, NativeLookup, NativeSymbol));
}
if (builtin_libraries_[id].patch_url_ != NULL) {
ASSERT(builtin_libraries_[id].patch_paths_ != NULL);
diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h
index a4d8a13..6e03854 100644
--- a/runtime/bin/builtin.h
+++ b/runtime/bin/builtin.h
@@ -57,6 +57,8 @@
int argument_count,
bool* auto_setup_scope);
+ static const uint8_t* NativeSymbol(Dart_NativeFunction nf);
+
static const char* builtin_source_paths_[];
static const char* io_source_paths_[];
static const char* io_patch_paths_[];
diff --git a/runtime/bin/builtin_gen_snapshot.cc b/runtime/bin/builtin_gen_snapshot.cc
index 965a862..3e25004 100644
--- a/runtime/bin/builtin_gen_snapshot.cc
+++ b/runtime/bin/builtin_gen_snapshot.cc
@@ -49,6 +49,18 @@
}
+const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
+ int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
+ for (int i = 0; i < num_entries; i++) {
+ struct NativeEntries* entry = &(BuiltinEntries[i]);
+ if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+ return reinterpret_cast<const uint8_t*>(entry->name_);
+ }
+ }
+ return NULL;
+}
+
+
// Implementation of native functions which are used for some
// test/debug functionality in standalone dart mode.
void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc
index 9201f5c..858aa27 100644
--- a/runtime/bin/builtin_natives.cc
+++ b/runtime/bin/builtin_natives.cc
@@ -99,6 +99,18 @@
}
+const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
+ int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
+ for (int i = 0; i < num_entries; i++) {
+ struct NativeEntries* entry = &(BuiltinEntries[i]);
+ if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+ return reinterpret_cast<const uint8_t*>(entry->name_);
+ }
+ }
+ return IONativeSymbol(nf);
+}
+
+
// Implementation of native functions which are used for some
// test/debug functionality in standalone dart mode.
void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
diff --git a/runtime/bin/builtin_nolib.cc b/runtime/bin/builtin_nolib.cc
index b19dfd3..e4ad4ce7 100644
--- a/runtime/bin/builtin_nolib.cc
+++ b/runtime/bin/builtin_nolib.cc
@@ -47,7 +47,8 @@
Dart_Handle library = Dart_LookupLibrary(url);
ASSERT(!Dart_IsError(library));
// Setup the native resolver for built in library functions.
- DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
+ DART_CHECK_VALID(
+ Dart_SetNativeResolver(library, NativeLookup, NativeSymbol));
}
}
diff --git a/runtime/bin/dbg_message.cc b/runtime/bin/dbg_message.cc
index d47474f..34f4271 100644
--- a/runtime/bin/dbg_message.cc
+++ b/runtime/bin/dbg_message.cc
@@ -473,8 +473,9 @@
res = Dart_GetActivationFrame(trace, i, &frame);
ASSERT_NOT_ERROR(res);
Dart_Handle func_name;
+ Dart_Handle func;
Dart_CodeLocation location;
- res = Dart_ActivationFrameGetLocation(frame, &func_name, NULL, &location);
+ res = Dart_ActivationFrameGetLocation(frame, &func_name, &func, &location);
ASSERT_NOT_ERROR(res);
ASSERT(Dart_IsString(func_name));
msg->Printf("%s{\"functionName\":", (i > 0) ? "," : "");
@@ -486,6 +487,13 @@
msg->Printf(",\"libraryId\":%d,", location.library_id);
msg->Printf("\"tokenOffset\":%d}", location.token_pos);
}
+ ASSERT_NOT_ERROR(func);
+ Dart_Handle origin = Dart_GetFunctionOrigin(func);
+ ASSERT_NOT_ERROR(origin);
+ if (Dart_IsInteger(origin)) {
+ int64_t class_id = GetIntValue(origin);
+ msg->Printf(",\"classId\":%" Pd64 "", class_id);
+ }
Dart_Handle locals = Dart_GetLocalVariables(frame);
ASSERT_NOT_ERROR(locals);
msg->Printf(",\"locals\":");
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index eefdd58..ecfa7e9 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -310,12 +310,15 @@
return false;
}
- bool success = DeleteEntry(&find_file_data, path);
-
- while ((FindNextFileW(find_handle, &find_file_data) != 0) && success) {
+ do {
+ if (!DeleteEntry(&find_file_data, path)) {
+ DWORD last_error = GetLastError();
+ FindClose(find_handle);
+ SetLastError(last_error);
+ return false;
+ }
path->Reset(path_length); // DeleteEntry adds to the path.
- success = success && DeleteEntry(&find_file_data, path);
- }
+ } while (FindNextFileW(find_handle, &find_file_data) != 0);
path->Reset(path_length - 1); // Drop the "\" from the end of the path.
if ((GetLastError() != ERROR_NO_MORE_FILES) ||
@@ -324,7 +327,7 @@
return false;
}
- return success;
+ return true;
}
diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc
index fb3a616..0b099ba 100644
--- a/runtime/bin/file_android.cc
+++ b/runtime/bin/file_android.cc
@@ -266,10 +266,10 @@
}
}
}
+ int e = errno;
+ VOID_TEMP_FAILURE_RETRY(close(old_fd));
+ VOID_TEMP_FAILURE_RETRY(close(new_fd));
if (result < 0) {
- int e = errno;
- VOID_TEMP_FAILURE_RETRY(close(old_fd));
- VOID_TEMP_FAILURE_RETRY(close(new_fd));
VOID_NO_RETRY_EXPECTED(unlink(new_path));
errno = e;
return false;
diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc
index 92c5637..4753a8b 100644
--- a/runtime/bin/file_linux.cc
+++ b/runtime/bin/file_linux.cc
@@ -265,10 +265,10 @@
}
}
}
+ int e = errno;
+ VOID_TEMP_FAILURE_RETRY(close(old_fd));
+ VOID_TEMP_FAILURE_RETRY(close(new_fd));
if (result < 0) {
- int e = errno;
- VOID_TEMP_FAILURE_RETRY(close(old_fd));
- VOID_TEMP_FAILURE_RETRY(close(new_fd));
VOID_NO_RETRY_EXPECTED(unlink(new_path));
errno = e;
return false;
diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc
index 86d7c2d..bd8d498 100644
--- a/runtime/bin/io_natives.cc
+++ b/runtime/bin/io_natives.cc
@@ -124,5 +124,17 @@
return NULL;
}
+
+const uint8_t* IONativeSymbol(Dart_NativeFunction nf) {
+ int num_entries = sizeof(IOEntries) / sizeof(struct NativeEntries);
+ for (int i = 0; i < num_entries; i++) {
+ struct NativeEntries* entry = &(IOEntries[i]);
+ if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+ return reinterpret_cast<const uint8_t*>(entry->name_);
+ }
+ }
+ return NULL;
+}
+
} // namespace bin
} // namespace dart
diff --git a/runtime/bin/io_natives.h b/runtime/bin/io_natives.h
index 7039234..d44f839 100644
--- a/runtime/bin/io_natives.h
+++ b/runtime/bin/io_natives.h
@@ -15,6 +15,8 @@
int argument_count,
bool* auto_setup_scope);
+const uint8_t* IONativeSymbol(Dart_NativeFunction nf);
+
} // namespace bin
} // namespace dart
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 62fea29..d1eea46 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -941,6 +941,7 @@
// Start event handler.
EventHandler::Start();
+ ASSERT(Dart_CurrentIsolate() == NULL);
// Start the VM service isolate, if necessary.
if (start_vm_service) {
ASSERT(vm_service_server_port >= 0);
@@ -952,6 +953,7 @@
Dart_RegisterIsolateServiceRequestCallback(
"io", &ServiceRequestHandler, NULL);
}
+ ASSERT(Dart_CurrentIsolate() == NULL);
// Call CreateIsolateAndSetup which creates an isolate and loads up
// the specified application script.
diff --git a/runtime/bin/process_patch.dart b/runtime/bin/process_patch.dart
index a68b7e5..0f5f8ac 100644
--- a/runtime/bin/process_patch.dart
+++ b/runtime/bin/process_patch.dart
@@ -181,7 +181,7 @@
throw new ArgumentError("Non-string argument: $arg");
}
_arguments[i] = arguments[i];
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
_arguments[i] = _windowsArgumentEscape(_arguments[i]);
}
}
@@ -192,21 +192,27 @@
}
_environment = [];
- var environmentEntryHandler = (key, value) {
+ // Ensure that we have a non-null environment.
+ environment = (environment == null) ? (const {}) : environment;
+ if (environment is !Map) {
+ throw new ArgumentError("Environment is not a map: $environment");
+ }
+ environment.forEach((key, value) {
if (key is !String || value is !String) {
throw new ArgumentError(
"Environment key or value is not a string: ($key, $value)");
}
_environment.add('$key=$value');
- };
- if (environment != null) {
- if (environment is !Map) {
- throw new ArgumentError("Environment is not a map: $environment");
- }
- environment.forEach(environmentEntryHandler);
- }
+ });
if (includeParentEnvironment) {
- Platform.environment.forEach(environmentEntryHandler);
+ Platform.environment.forEach((key, value) {
+ assert(key is String);
+ assert(value is String);
+ // Do not override keys already set as part of environment.
+ if (!environment.containsKey(key)) {
+ _environment.add('$key=$value');
+ }
+ });
}
// stdin going to process.
@@ -221,7 +227,7 @@
}
static String _getShellCommand() {
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
return 'cmd.exe';
}
return '/bin/sh';
@@ -230,7 +236,7 @@
static List<String> _getShellArguments(String executable,
List<String> arguments) {
List<String> shellArguments = [];
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
shellArguments.add('/c');
shellArguments.add(executable);
for (var arg in arguments) {
@@ -320,7 +326,6 @@
_stderr._stream._nativeSocket,
_exitHandler._nativeSocket,
status);
- _environment = null; // The environment will not be needed going forward.
if (!success) {
completer.completeError(
new ProcessException(_path,
@@ -329,6 +334,12 @@
status._errorCode));
return;
}
+ // Reset values which are no longer needed.
+ _path = null;
+ _arguments = null;
+ _workingDirectory = null;
+ _environment = null;
+
_started = true;
// Setup an exit handler to handle internal cleanup and possible
@@ -376,13 +387,17 @@
_stderr._stream._nativeSocket,
_exitHandler._nativeSocket,
status);
- _environment = null; // The environment will not be needed going forward.
if (!success) {
throw new ProcessException(_path,
_arguments,
status._errorMessage,
status._errorCode);
}
+ // Reset values which are no longer needed.
+ _path = null;
+ _arguments = null;
+ _workingDirectory = null;
+ _environment = null;
var result = _wait(
_stdin._sink._nativeSocket,
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index b46bf5e..f31975e 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -1068,7 +1068,15 @@
.then((socket) => new _RawServerSocket(socket));
}
- _RawServerSocket(this._socket) {
+ _RawServerSocket(this._socket);
+
+ StreamSubscription<RawSocket> listen(void onData(RawSocket event),
+ {Function onError,
+ void onDone(),
+ bool cancelOnError}) {
+ if (_controller != null) {
+ throw new StateError("Stream was already listened to");
+ }
var zone = Zone.current;
_controller = new StreamController(sync: true,
onListen: _onSubscriptionStateChange,
@@ -1087,14 +1095,7 @@
_controller.addError(e);
_controller.close();
}),
- destroyed: _controller.close
- );
- }
-
- StreamSubscription<RawSocket> listen(void onData(RawSocket event),
- {Function onError,
- void onDone(),
- bool cancelOnError}) {
+ destroyed: _controller.close);
return _controller.stream.listen(
onData,
onError: onError,
diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc
index da82b27..7f1de2d 100644
--- a/runtime/bin/socket_win.cc
+++ b/runtime/bin/socket_win.cc
@@ -233,11 +233,17 @@
memset(&hints, 0, sizeof(hints));
hints.ai_family = SocketAddress::FromType(type);
hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = 0;
+ hints.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG);
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* info = NULL;
int status = getaddrinfo(host, 0, &hints, &info);
if (status != 0) {
+ // We failed, try without AI_ADDRCONFIG. This can happen when looking up
+ // e.g. '::1', when there are no global IPv6 addresses.
+ hints.ai_flags = AI_V4MAPPED;
+ status = getaddrinfo(host, 0, &hints, &info);
+ }
+ if (status != 0) {
ASSERT(*os_error == NULL);
DWORD error_code = WSAGetLastError();
SetLastError(error_code);
diff --git a/runtime/bin/test_extension.c b/runtime/bin/test_extension.c
index 82387a2..d0c50c5 100644
--- a/runtime/bin/test_extension.c
+++ b/runtime/bin/test_extension.c
@@ -64,7 +64,7 @@
return parent_library;
}
- result_code = Dart_SetNativeResolver(parent_library, ResolveName);
+ result_code = Dart_SetNativeResolver(parent_library, ResolveName, NULL);
if (Dart_IsError(result_code)) {
return result_code;
}
diff --git a/runtime/bin/vmservice/client/lib/src/elements/vm_view.html b/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
index bfd4965..253f434 100644
--- a/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
+++ b/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
@@ -52,6 +52,14 @@
<div class="memberName">uptime</div>
<div class="memberValue">{{ vm.uptime | formatTime }}</div>
</div>
+ <div class="memberItem">
+ <div class="memberName">type checks enabled</div>
+ <div class="memberValue">{{ vm.typeChecksEnabled }}</div>
+ </div>
+ <div class="memberItem">
+ <div class="memberName">asserts enabled</div>
+ <div class="memberValue">{{ vm.assertsEnabled }}</div>
+ </div>
</div>
</div>
diff --git a/runtime/bin/vmservice/client/lib/src/service/object.dart b/runtime/bin/vmservice/client/lib/src/service/object.dart
index 9024d49..d7d595b 100644
--- a/runtime/bin/vmservice/client/lib/src/service/object.dart
+++ b/runtime/bin/vmservice/client/lib/src/service/object.dart
@@ -177,6 +177,8 @@
@observable String version = 'unknown';
@observable String architecture = 'unknown';
@observable double uptime = 0.0;
+ @observable bool assertsEnabled = false;
+ @observable bool typeChecksEnabled = false;
VM() : super._empty(null) {
name = 'vm';
@@ -339,6 +341,8 @@
version = map['version'];
architecture = map['architecture'];
uptime = map['uptime'];
+ assertsEnabled = map['assertsEnabled'];
+ typeChecksEnabled = map['typeChecksEnabled'];
_updateIsolates(map['isolates']);
}
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 9046ec8..a91a88c 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -527,26 +527,85 @@
DART_EXPORT bool Dart_IsPrologueWeakPersistentHandle(
Dart_WeakPersistentHandle object);
+typedef struct _Dart_WeakReferenceSetBuilder* Dart_WeakReferenceSetBuilder;
+typedef struct _Dart_WeakReferenceSet* Dart_WeakReferenceSet;
+
+/**
+ * Constructs a weak references set builder.
+ *
+ * \returns a pointer to the weak reference set builder if successful.
+ * Otherwise, returns NULL.
+ */
+DART_EXPORT Dart_WeakReferenceSetBuilder Dart_NewWeakReferenceSetBuilder();
+
/**
* Constructs a set of weak references from the Cartesian product of
* the objects in the key set and the objects in values set.
*
- * \param keys A set of object references. These references will be
+ * \param set_builder The weak references set builder which was created
+ * using Dart_NewWeakReferenceSetBuilder().
+ * \param key An object reference. This references will be
* considered weak by the garbage collector.
- * \param num_keys the number of objects in the keys set.
- * \param values A set of object references. These references will be
+ * \param value An object reference. This reference will be
* considered weak by garbage collector unless any object reference
* in 'keys' is found to be strong.
- * \param num_values the size of the values set
*
- * \return Success if the weak reference set could be created.
- * Otherwise, returns an error handle.
+ * \return a pointer to the weak reference set if successful.
+ * Otherwise, returns NULL.
*/
-DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(
- Dart_WeakPersistentHandle* keys,
- intptr_t num_keys,
- Dart_WeakPersistentHandle* values,
- intptr_t num_values);
+DART_EXPORT Dart_WeakReferenceSet Dart_NewWeakReferenceSet(
+ Dart_WeakReferenceSetBuilder set_builder,
+ Dart_WeakPersistentHandle key,
+ Dart_WeakPersistentHandle value);
+
+/**
+ * Append the pair of key/value object references to the weak references set.
+ *
+ * \param reference_set A weak references set into which the pair of key/value
+ * needs to be added.
+ * \param key An object reference. This references will be
+ * considered weak by the garbage collector.
+ * \param value An object reference. This reference will be
+ * considered weak by garbage collector unless any object reference
+ * in 'keys' is found to be strong.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ * Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle key,
+ Dart_WeakPersistentHandle value);
+
+/**
+ * Append the key object reference to the weak references set.
+ *
+ * \param reference_set A weak references set into which the key
+ * needs to be added.
+ * \param key An object reference. This references will be
+ * considered weak by the garbage collector.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ * Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendKeyToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle key);
+
+/**
+ * Append the value object reference to the weak references set.
+ *
+ * \param reference_set A weak references set into which the key
+ * needs to be added.
+ * \param value An object reference. This references will be
+ * considered weak by the garbage collector.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ * Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendValueToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle value);
/*
@@ -592,6 +651,14 @@
Dart_GcPrologueCallback prologue_callback,
Dart_GcEpilogueCallback epilogue_callback);
+typedef void (*Dart_GcPrologueWeakHandleCallback)(void* isolate_callback_data,
+ Dart_WeakPersistentHandle obj,
+ intptr_t num_native_fields,
+ intptr_t* native_fields);
+
+DART_EXPORT Dart_Handle Dart_VisitPrologueWeakHandles(
+ Dart_GcPrologueWeakHandleCallback callback);
+
/*
* ==========================
* Initialization and Globals
@@ -2212,6 +2279,24 @@
/* TODO(turnidge): Consider renaming to NativeFunctionResolver or
* NativeResolver. */
+/**
+ * Native entry symbol lookup callback.
+ *
+ * For libraries and scripts which have native functions, the embedder
+ * can provide a callback for mapping a native entry to a symbol. This callback
+ * maps a native function entry PC to the native function name. If no native
+ * entry symbol can be found, the callback should return NULL.
+ *
+ * The parameters to the native reverse resolver function are:
+ * \param nf A Dart_NativeFunction.
+ *
+ * \return A const UTF-8 string containing the symbol name or NULL.
+ *
+ * See Dart_SetNativeResolver.
+ */
+typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf);
+
+
/*
* ===========
* Environment
@@ -2248,7 +2333,8 @@
*/
DART_EXPORT Dart_Handle Dart_SetNativeResolver(
Dart_Handle library,
- Dart_NativeEntryResolver resolver);
+ Dart_NativeEntryResolver resolver,
+ Dart_NativeEntrySymbol symbol);
/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */
diff --git a/runtime/include/dart_debugger_api.h b/runtime/include/dart_debugger_api.h
index e694174..5e83862 100755
--- a/runtime/include/dart_debugger_api.h
+++ b/runtime/include/dart_debugger_api.h
@@ -479,6 +479,18 @@
/**
+ * Returns origin class of a function.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \return Returns the class id (a handle to an integer) of the class in
+ * which \function is defined. Returns a null handle if \function is defined
+ * at the top level. Returns an error object otherwise.
+ */
+DART_EXPORT Dart_Handle Dart_GetFunctionOrigin(Dart_Handle function);
+
+
+/**
* Returns an array containing all the global variable names and values of
* the library with given \library_id.
*
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index 5c9f7d8..3ed280b 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -294,13 +294,13 @@
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39
];
- // Powers of 10 above 1000000 are indistinguishable.
+ // Powers of 10 above 1000000 are indistinguishable by eye.
static const int _POW_10_7 = 10000000;
static const int _POW_10_8 = 100000000;
static const int _POW_10_9 = 1000000000;
- static const int _POW_10_10 = 10000000000;
// Find the number of decimal digits in a positive smi.
+ // Never called with numbers < 100. These are handled before calling.
static int _positiveBase10Length(var smi) {
// A positive smi has length <= 19 if 63-bit, <=10 if 31-bit.
// Avoid comparing a 31-bit smi to a non-smi.
@@ -311,15 +311,13 @@
if (smi < 1000000) return 6;
return 7;
}
- if (smi < _POW_10_10) {
- if (smi < _POW_10_8) return 8;
- if (smi < _POW_10_9) return 9;
- return 10;
- }
- smi = smi ~/ _POW_10_10;
- if (smi < 10) return 11;
- if (smi < 100) return 12;
- return 10 + _positiveBase10Length(smi);
+ if (smi < _POW_10_8) return 8;
+ if (smi < _POW_10_9) return 9;
+ smi = smi ~/ _POW_10_9;
+ // Handle numbers < 100 before calling recursively.
+ if (smi < 10) return 10;
+ if (smi < 100) return 11;
+ return 9 + _positiveBase10Length(smi);
}
String toString() {
@@ -363,6 +361,7 @@
}
// Find the number of decimal digits in a negative smi.
+ // Never called with numbers > -100. These are handled before calling.
static int _negativeBase10Length(var negSmi) {
// A negative smi has length <= 19 if 63-bit, <=10 if 31-bit.
// Avoid comparing a 31-bit smi to a non-smi.
@@ -373,15 +372,13 @@
if (negSmi > -1000000) return 6;
return 7;
}
- if (negSmi > -_POW_10_10) {
- if (negSmi > -_POW_10_8) return 8;
- if (negSmi > -_POW_10_9) return 9;
- return 10;
- }
- negSmi = negSmi ~/ _POW_10_10;
- if (negSmi > -10) return 11;
- if (negSmi > -100) return 12;
- return 10 + _negativeBase10Length(negSmi);
+ if (negSmi > -_POW_10_8) return 8;
+ if (negSmi > -_POW_10_9) return 9;
+ negSmi = negSmi ~/ _POW_10_9;
+ // Handle numbers > -100 before calling recursively.
+ if (negSmi > -10) return 10;
+ if (negSmi > -100) return 11;
+ return 9 + _negativeBase10Length(negSmi);
}
// Convert a negative smi to a string.
diff --git a/runtime/lib/math_patch.dart b/runtime/lib/math_patch.dart
index 279a549..29e8014 100644
--- a/runtime/lib/math_patch.dart
+++ b/runtime/lib/math_patch.dart
@@ -20,10 +20,17 @@
return 1.0; // ECMA-262 15.8.2.13
}
if (base == 1.0) return 1.0;
+
if (base.isNaN || exponent.isNaN) {
return double.NAN;
}
- return _pow(base, exponent);
+ if ((base != -double.INFINITY) && (exponent == 0.5)) {
+ if (base == 0.0) {
+ return 0.0;
+ }
+ return sqrt(base);
+ }
+ return _pow(base.toDouble(), exponent.toDouble());
}
double _pow(double base, double exponent) native "Math_doublePow";
diff --git a/runtime/lib/profiler.cc b/runtime/lib/profiler.cc
new file mode 100644
index 0000000..75bf2d1
--- /dev/null
+++ b/runtime/lib/profiler.cc
@@ -0,0 +1,63 @@
+// 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.
+
+#include "vm/bootstrap_natives.h"
+
+#include "include/dart_api.h"
+
+#include "vm/bigint_operations.h"
+#include "vm/exceptions.h"
+#include "vm/native_entry.h"
+#include "vm/object.h"
+#include "vm/object_store.h"
+
+namespace dart {
+
+DECLARE_FLAG(bool, trace_intrinsified_natives);
+
+// dart:profiler.
+
+DEFINE_NATIVE_ENTRY(UserTag_new, 2) {
+ ASSERT(TypeArguments::CheckedHandle(arguments->NativeArgAt(0)).IsNull());
+ GET_NON_NULL_NATIVE_ARGUMENT(String, tag_label, arguments->NativeArgAt(1));
+ return UserTag::New(tag_label);
+}
+
+
+DEFINE_NATIVE_ENTRY(UserTag_label, 1) {
+ const UserTag& self = UserTag::CheckedHandle(arguments->NativeArgAt(0));
+ return self.label();
+}
+
+
+DEFINE_NATIVE_ENTRY(UserTag_makeCurrent, 1) {
+ const UserTag& self = UserTag::CheckedHandle(arguments->NativeArgAt(0));
+ if (FLAG_trace_intrinsified_natives) {
+ OS::Print("UserTag_makeCurrent: %s\n", self.ToCString());
+ }
+ self.MakeActive();
+ return Object::null();
+}
+
+
+DEFINE_NATIVE_ENTRY(Profiler_getCurrentTag, 0) {
+ if (FLAG_trace_intrinsified_natives) {
+ OS::Print("Profiler_getCurrentTag\n");
+ }
+ return isolate->current_tag();
+}
+
+
+DEFINE_NATIVE_ENTRY(Profiler_clearCurrentTag, 0) {
+ // Use a NoGCScope to avoid creating a handle for the old current.
+ NoGCScope no_gc;
+ RawUserTag* old_current = isolate->current_tag();
+ UserTag::ClearActive();
+ if (FLAG_trace_intrinsified_natives) {
+ OS::Print("Profiler_clearCurrentTag\n");
+ }
+ return old_current;
+}
+
+} // namespace dart
diff --git a/runtime/lib/profiler.dart b/runtime/lib/profiler.dart
new file mode 100644
index 0000000..5ed8df5
--- /dev/null
+++ b/runtime/lib/profiler.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import "dart:_internal";
+
+patch class UserTag {
+ /* patch */ factory UserTag(String label) {
+ return new _UserTag(label);
+ }
+}
+
+
+class _UserTag implements UserTag {
+ factory _UserTag(String label) native "UserTag_new";
+ String get label native "UserTag_label";
+ void makeCurrent() native "UserTag_makeCurrent";
+}
+
+patch UserTag getCurrentTag() => _getCurrentTag();
+UserTag _getCurrentTag() native "Profiler_getCurrentTag";
+
+patch UserTag clearCurrentTag() => _clearCurrentTag();
+UserTag _clearCurrentTag() native "Profiler_clearCurrentTag";
diff --git a/runtime/lib/profiler_sources.gypi b/runtime/lib/profiler_sources.gypi
new file mode 100644
index 0000000..4d5ee05
--- /dev/null
+++ b/runtime/lib/profiler_sources.gypi
@@ -0,0 +1,13 @@
+# Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Sources visible via dart:profiler library.
+
+{
+ 'sources': [
+ 'profiler.cc',
+ 'profiler.dart',
+ ],
+}
+
diff --git a/runtime/vm/assembler_arm64.cc b/runtime/vm/assembler_arm64.cc
index 2da262e..02adb89 100644
--- a/runtime/vm/assembler_arm64.cc
+++ b/runtime/vm/assembler_arm64.cc
@@ -20,10 +20,38 @@
namespace dart {
-DEFINE_FLAG(bool, print_stop_message, true, "Print stop message.");
+DEFINE_FLAG(bool, print_stop_message, false, "Print stop message.");
DECLARE_FLAG(bool, inline_alloc);
+Assembler::Assembler(bool use_far_branches)
+ : buffer_(),
+ object_pool_(GrowableObjectArray::Handle()),
+ patchable_pool_entries_(),
+ prologue_offset_(-1),
+ use_far_branches_(use_far_branches),
+ comments_() {
+ if (Isolate::Current() != Dart::vm_isolate()) {
+ object_pool_ = GrowableObjectArray::New(Heap::kOld);
+
+ // These objects and labels need to be accessible through every pool-pointer
+ // at the same index.
+ object_pool_.Add(Object::null_object(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+ // Not adding Object::null() to the index table. It is at index 0 in the
+ // object pool, but the HashMap uses 0 to indicate not found.
+
+ object_pool_.Add(Bool::True(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+ object_pool_index_table_.Insert(ObjIndexPair(Bool::True().raw(), 1));
+
+ object_pool_.Add(Bool::False(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+ object_pool_index_table_.Insert(ObjIndexPair(Bool::False().raw(), 2));
+ }
+}
+
+
void Assembler::InitializeMemoryWithBreakpoints(uword data, intptr_t length) {
ASSERT(Utils::IsAligned(data, 4));
ASSERT(Utils::IsAligned(length, 4));
@@ -35,11 +63,6 @@
}
-void Assembler::Stop(const char* message) {
- UNIMPLEMENTED();
-}
-
-
void Assembler::Emit(int32_t value) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
buffer_.Emit<int32_t>(value);
@@ -74,6 +97,36 @@
}
+// TODO(zra): Support for far branches. Requires loading large immediates.
+void Assembler::Bind(Label* label) {
+ ASSERT(!label->IsBound());
+ intptr_t bound_pc = buffer_.Size();
+
+ while (label->IsLinked()) {
+ const int64_t position = label->Position();
+ const int64_t dest = bound_pc - position;
+ const int32_t next = buffer_.Load<int32_t>(position);
+ const int32_t encoded = EncodeImm19BranchOffset(dest, next);
+ buffer_.Store<int32_t>(position, encoded);
+ label->position_ = DecodeImm19BranchOffset(next);
+ }
+ label->BindTo(bound_pc);
+}
+
+
+void Assembler::Stop(const char* message) {
+ if (FLAG_print_stop_message) {
+ UNIMPLEMENTED();
+ }
+ Label stop;
+ b(&stop);
+ Emit(Utils::Low32Bits(reinterpret_cast<int64_t>(message)));
+ Emit(Utils::High32Bits(reinterpret_cast<int64_t>(message)));
+ Bind(&stop);
+ hlt(kImmExceptionIsDebug);
+}
+
+
static int CountLeadingZeros(uint64_t value, int width) {
ASSERT((width == 32) || (width == 64));
if (value == 0) {
@@ -109,7 +162,7 @@
// by the corresponding fields in the logical instruction.
// If it can't be encoded, the function returns false, and the operand is
// undefined.
-bool Assembler::IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op) {
+bool Operand::IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op) {
ASSERT(imm_op != NULL);
ASSERT((width == kWRegSizeInBits) || (width == kXRegSizeInBits));
ASSERT((width == kXRegSizeInBits) || (value <= 0xffffffffUL));
@@ -203,6 +256,393 @@
}
}
+
+void Assembler::LoadPoolPointer(Register pp) {
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ CodeSize();
+ // PP <- Read(PC - object_pool_pc_dist).
+ ldr(pp, Address::PC(-object_pool_pc_dist));
+
+ // When in the PP register, the pool pointer is untagged. When we
+ // push it on the stack with PushPP it is tagged again. PopPP then untags
+ // when restoring from the stack. This will make loading from the object
+ // pool only one instruction for the first 4096 entries. Otherwise, because
+ // the offset wouldn't be aligned, it would be only one instruction for the
+ // first 64 entries.
+ sub(pp, pp, Operand(kHeapObjectTag));
+}
+
+void Assembler::LoadWordFromPoolOffset(Register dst, Register pp,
+ uint32_t offset) {
+ ASSERT(dst != pp);
+ if (Address::CanHoldOffset(offset)) {
+ ldr(dst, Address(pp, offset));
+ } else {
+ const uint16_t offset_low = Utils::Low16Bits(offset);
+ const uint16_t offset_high = Utils::High16Bits(offset);
+ movz(dst, offset_low, 0);
+ if (offset_high != 0) {
+ movk(dst, offset_high, 1);
+ }
+ ldr(dst, Address(pp, dst));
+ }
+}
+
+
+intptr_t Assembler::FindExternalLabel(const ExternalLabel* label,
+ Patchability patchable) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ ASSERT(!object_pool_.IsNull());
+ const uword address = label->address();
+ ASSERT(Utils::IsAligned(address, 4));
+ // The address is stored in the object array as a RawSmi.
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(address));
+ if (patchable == kNotPatchable) {
+ // If the call site is not patchable, we can try to re-use an existing
+ // entry.
+ return FindObject(smi, kNotPatchable);
+ }
+ // If the call is patchable, do not reuse an existing entry since each
+ // reference may be patched independently.
+ object_pool_.Add(smi, Heap::kOld);
+ patchable_pool_entries_.Add(patchable);
+ return object_pool_.Length() - 1;
+}
+
+
+intptr_t Assembler::FindObject(const Object& obj, Patchability patchable) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ ASSERT(!object_pool_.IsNull());
+
+ // If the object is not patchable, check if we've already got it in the
+ // object pool.
+ if (patchable == kNotPatchable) {
+ // Special case for Object::null(), which is always at object_pool_ index 0
+ // because Lookup() below returns 0 when the object is not mapped in the
+ // table.
+ if (obj.raw() == Object::null()) {
+ return 0;
+ }
+
+ intptr_t idx = object_pool_index_table_.Lookup(obj.raw());
+ if (idx != 0) {
+ ASSERT(patchable_pool_entries_[idx] == kNotPatchable);
+ return idx;
+ }
+ }
+
+ object_pool_.Add(obj, Heap::kOld);
+ patchable_pool_entries_.Add(patchable);
+ if (patchable == kNotPatchable) {
+ // The object isn't patchable. Record the index for fast lookup.
+ object_pool_index_table_.Insert(
+ ObjIndexPair(obj.raw(), object_pool_.Length() - 1));
+ }
+ return object_pool_.Length() - 1;
+}
+
+
+intptr_t Assembler::FindImmediate(int64_t imm) {
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ ASSERT(!object_pool_.IsNull());
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(imm));
+ return FindObject(smi, kNotPatchable);
+}
+
+
+bool Assembler::CanLoadObjectFromPool(const Object& object) {
+ // TODO(zra, kmillikin): Also load other large immediates from the object
+ // pool
+ if (object.IsSmi()) {
+ // If the raw smi does not fit into a 32-bit signed int, then we'll keep
+ // the raw value in the object pool.
+ return !Utils::IsInt(32, reinterpret_cast<int64_t>(object.raw()));
+ }
+ ASSERT(object.IsNotTemporaryScopedHandle());
+ ASSERT(object.IsOld());
+ return (Isolate::Current() != Dart::vm_isolate()) &&
+ // Not in the VMHeap, OR is one of the VMHeap objects we put in every
+ // object pool.
+ // TODO(zra): Evaluate putting all VM heap objects into the pool.
+ (!object.InVMHeap() || (object.raw() == Object::null()) ||
+ (object.raw() == Bool::True().raw()) ||
+ (object.raw() == Bool::False().raw()));
+}
+
+
+bool Assembler::CanLoadImmediateFromPool(int64_t imm, Register pp) {
+ return !Utils::IsInt(32, imm) &&
+ (pp != kNoRegister) &&
+ (Isolate::Current() != Dart::vm_isolate());
+}
+
+
+void Assembler::LoadExternalLabel(Register dst,
+ const ExternalLabel* label,
+ Patchability patchable,
+ Register pp) {
+ const int32_t offset =
+ Array::element_offset(FindExternalLabel(label, patchable));
+ LoadWordFromPoolOffset(dst, pp, offset);
+}
+
+
+void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+ if (CanLoadObjectFromPool(object)) {
+ const int32_t offset =
+ Array::element_offset(FindObject(object, kNotPatchable));
+ LoadWordFromPoolOffset(dst, pp, offset);
+ } else {
+ ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
+ object.IsSmi() ||
+ object.InVMHeap());
+ LoadDecodableImmediate(dst, reinterpret_cast<int64_t>(object.raw()), pp);
+ }
+}
+
+
+void Assembler::LoadDecodableImmediate(Register reg, int64_t imm, Register pp) {
+ if ((pp != kNoRegister) && (Isolate::Current() != Dart::vm_isolate())) {
+ int64_t val_smi_tag = imm & kSmiTagMask;
+ imm &= ~kSmiTagMask; // Mask off the tag bits.
+ const int32_t offset = Array::element_offset(FindImmediate(imm));
+ LoadWordFromPoolOffset(reg, pp, offset);
+ if (val_smi_tag != 0) {
+ // Add back the tag bits.
+ orri(reg, reg, val_smi_tag);
+ }
+ } else {
+ // TODO(zra): Since this sequence only needs to be decodable, it can be
+ // of variable length.
+ LoadPatchableImmediate(reg, imm);
+ }
+}
+
+
+void Assembler::LoadPatchableImmediate(Register reg, int64_t imm) {
+ const uint32_t w0 = Utils::Low32Bits(imm);
+ const uint32_t w1 = Utils::High32Bits(imm);
+ const uint16_t h0 = Utils::Low16Bits(w0);
+ const uint16_t h1 = Utils::High16Bits(w0);
+ const uint16_t h2 = Utils::Low16Bits(w1);
+ const uint16_t h3 = Utils::High16Bits(w1);
+ movz(reg, h0, 0);
+ movk(reg, h1, 1);
+ movk(reg, h2, 2);
+ movk(reg, h3, 3);
+}
+
+
+void Assembler::LoadImmediate(Register reg, int64_t imm, Register pp) {
+ Comment("LoadImmediate");
+ if (CanLoadImmediateFromPool(imm, pp)) {
+ // It's a 64-bit constant and we're not in the VM isolate, so load from
+ // object pool.
+ // Save the bits that must be masked-off for the SmiTag
+ int64_t val_smi_tag = imm & kSmiTagMask;
+ imm &= ~kSmiTagMask; // Mask off the tag bits.
+ const int32_t offset = Array::element_offset(FindImmediate(imm));
+ LoadWordFromPoolOffset(reg, pp, offset);
+ if (val_smi_tag != 0) {
+ // Add back the tag bits.
+ orri(reg, reg, val_smi_tag);
+ }
+ } else {
+ // 1. Can we use one orri operation?
+ Operand op;
+ Operand::OperandType ot;
+ ot = Operand::CanHold(imm, kXRegSizeInBits, &op);
+ if (ot == Operand::BitfieldImm) {
+ orri(reg, ZR, imm);
+ return;
+ }
+
+ // 2. Fall back on movz, movk, movn.
+ const uint32_t w0 = Utils::Low32Bits(imm);
+ const uint32_t w1 = Utils::High32Bits(imm);
+ const uint16_t h0 = Utils::Low16Bits(w0);
+ const uint16_t h1 = Utils::High16Bits(w0);
+ const uint16_t h2 = Utils::Low16Bits(w1);
+ const uint16_t h3 = Utils::High16Bits(w1);
+
+ // Special case for w1 == 0xffffffff
+ if (w1 == 0xffffffff) {
+ if (h1 == 0xffff) {
+ movn(reg, ~h0, 0);
+ } else {
+ movn(reg, ~h1, 1);
+ movk(reg, h0, 0);
+ }
+ return;
+ }
+
+ // Special case for h3 == 0xffff
+ if (h3 == 0xffff) {
+ // We know h2 != 0xffff.
+ movn(reg, ~h2, 2);
+ if (h1 != 0xffff) {
+ movk(reg, h1, 1);
+ }
+ if (h0 != 0xffff) {
+ movk(reg, h0, 0);
+ }
+ return;
+ }
+
+ bool initialized = false;
+ if (h0 != 0) {
+ movz(reg, h0, 0);
+ initialized = true;
+ }
+ if (h1 != 0) {
+ if (initialized) {
+ movk(reg, h1, 1);
+ } else {
+ movz(reg, h1, 1);
+ initialized = true;
+ }
+ }
+ if (h2 != 0) {
+ if (initialized) {
+ movk(reg, h2, 2);
+ } else {
+ movz(reg, h2, 2);
+ initialized = true;
+ }
+ }
+ if (h3 != 0) {
+ if (initialized) {
+ movk(reg, h3, 3);
+ } else {
+ movz(reg, h3, 3);
+ }
+ }
+ }
+}
+
+
+void Assembler::AddImmediate(
+ Register dest, Register rn, int64_t imm, Register pp) {
+ ASSERT(rn != TMP2);
+ Operand op;
+ if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
+ add(dest, rn, op);
+ } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
+ Operand::Immediate) {
+ sub(dest, rn, op);
+ } else {
+ LoadImmediate(TMP2, imm, pp);
+ add(dest, rn, Operand(TMP2));
+ }
+}
+
+
+void Assembler::CompareImmediate(Register rn, int64_t imm, Register pp) {
+ ASSERT(rn != TMP2);
+ Operand op;
+ if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
+ cmp(rn, op);
+ } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
+ Operand::Immediate) {
+ cmn(rn, op);
+ } else {
+ LoadImmediate(TMP2, imm, pp);
+ cmp(rn, Operand(TMP2));
+ }
+}
+
+
+void Assembler::LoadFromOffset(Register dest, Register base, int32_t offset) {
+ ASSERT(base != TMP2);
+ if (Address::CanHoldOffset(offset)) {
+ ldr(dest, Address(base, offset));
+ } else {
+ // Since offset is 32-bits, it won't be loaded from the pool.
+ AddImmediate(TMP2, base, offset, kNoRegister);
+ ldr(dest, Address(TMP2));
+ }
+}
+
+
+void Assembler::StoreToOffset(Register src, Register base, int32_t offset) {
+ ASSERT(src != TMP2);
+ ASSERT(base != TMP2);
+ if (Address::CanHoldOffset(offset)) {
+ str(src, Address(base, offset));
+ } else if (Address::CanHoldOffset(offset, Address::PreIndex)) {
+ mov(TMP2, base);
+ str(src, Address(TMP2, offset, Address::PreIndex));
+ } else {
+ // Since offset is 32-bits, it won't be loaded from the pool.
+ AddImmediate(TMP2, base, offset, kNoRegister);
+ str(src, Address(TMP2));
+ }
+}
+
+
+void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
+ // Reserve space for arguments and align frame before entering
+ // the C++ world.
+ AddImmediate(SP, SP, -frame_space, kNoRegister);
+ if (OS::ActivationFrameAlignment() > 1) {
+ mov(TMP, SP); // SP can't be register operand of andi.
+ andi(TMP, TMP, ~(OS::ActivationFrameAlignment() - 1));
+ mov(SP, TMP);
+ }
+}
+
+
+void Assembler::EnterFrame(intptr_t frame_size) {
+ Push(LR);
+ Push(FP);
+ mov(FP, SP);
+
+ if (frame_size > 0) {
+ sub(SP, SP, Operand(frame_size));
+ }
+}
+
+
+void Assembler::LeaveFrame() {
+ mov(SP, FP);
+ Pop(FP);
+ Pop(LR);
+}
+
+
+void Assembler::EnterDartFrame(intptr_t frame_size) {
+ // Setup the frame.
+ adr(TMP, 0); // TMP gets PC of this instruction.
+ EnterFrame(0);
+ Push(TMP); // Save PC Marker.
+ PushPP(); // Save PP.
+
+ // Load the pool pointer.
+ LoadPoolPointer(PP);
+
+ // Reserve space.
+ if (frame_size > 0) {
+ sub(SP, SP, Operand(frame_size));
+ }
+}
+
+
+void Assembler::LeaveDartFrame() {
+ // Restore and untag PP.
+ LoadFromOffset(PP, FP, kSavedCallerPpSlotFromFp * kWordSize);
+ sub(PP, PP, Operand(kHeapObjectTag));
+ LeaveFrame();
+}
+
+
+void Assembler::CallRuntime(const RuntimeEntry& entry,
+ intptr_t argument_count) {
+ entry.Call(this, argument_count);
+}
+
} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/assembler_arm64.h b/runtime/vm/assembler_arm64.h
index fd9d7cb..1ead6e5 100644
--- a/runtime/vm/assembler_arm64.h
+++ b/runtime/vm/assembler_arm64.h
@@ -12,6 +12,7 @@
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/constants_arm64.h"
+#include "vm/hash_map.h"
#include "vm/object.h"
#include "vm/simulator.h"
@@ -87,6 +88,8 @@
PreIndex,
PostIndex,
Reg,
+ PCOffset,
+ Unknown,
};
// Offset is in bytes. For the unsigned imm12 case, we unscale based on the
@@ -96,14 +99,21 @@
Address(Register rn, int32_t offset = 0, AddressType at = Offset,
OperandSize sz = kDoubleWord) {
ASSERT((rn != R31) && (rn != ZR));
+ ASSERT(CanHoldOffset(offset, at, sz));
const Register crn = ConcreteRegister(rn);
const int32_t scale = Log2OperandSizeBytes(sz);
- if (Utils::IsUint(12 + scale, offset) && (at == Offset)) {
- ASSERT(offset == ((offset >> scale) << scale));
+ if ((at == Offset) &&
+ Utils::IsUint(12 + scale, offset) &&
+ (offset == ((offset >> scale) << scale))) {
encoding_ =
B24 |
((offset >> scale) << kImm12Shift) |
(static_cast<int32_t>(crn) << kRnShift);
+ } else if ((at == Offset) &&
+ Utils::IsInt(9, offset)) {
+ encoding_ =
+ ((offset & 0x1ff) << kImm9Shift) |
+ (static_cast<int32_t>(crn) << kRnShift);
} else {
ASSERT(Utils::IsInt(9, offset));
ASSERT((at == PreIndex) || (at == PostIndex));
@@ -117,8 +127,33 @@
base_ = crn;
}
- // TODO(zra): Write CanHoldOffset(int32_t off, AddressType, OperandSize).
- // TODO(zra): Write constructor for PC-relative load address.
+ static bool CanHoldOffset(int32_t offset, AddressType at = Offset,
+ OperandSize sz = kDoubleWord) {
+ if (at == Offset) {
+ // Offset fits in 12 bit unsigned and has right alignment for sz,
+ // or fits in 9 bit signed offset with no alignment restriction.
+ const int32_t scale = Log2OperandSizeBytes(sz);
+ return (Utils::IsUint(12 + scale, offset) &&
+ (offset == ((offset >> scale) << scale))) ||
+ (Utils::IsInt(9, offset));
+ } else if (at == PCOffset) {
+ return Utils::IsInt(21, offset) &&
+ (offset == ((offset >> 2) << 2));
+ } else {
+ ASSERT((at == PreIndex) || (at == PostIndex));
+ return Utils::IsInt(9, offset);
+ }
+ }
+
+ // PC-relative load address.
+ static Address PC(int32_t pc_off) {
+ ASSERT(CanHoldOffset(pc_off, PCOffset));
+ Address addr;
+ addr.encoding_ = (((pc_off >> 2) << kImm19Shift) & kImm19Mask);
+ addr.base_ = kNoRegister;
+ addr.type_ = PCOffset;
+ return addr;
+ }
// Base register rn with offset rm. rm is sign-extended according to ext.
// If ext is UXTX, rm may be optionally scaled by the
@@ -145,6 +180,8 @@
AddressType type() const { return type_; }
Register base() const { return base_; }
+ Address() : encoding_(0), type_(Unknown), base_(kNoRegister) {}
+
uint32_t encoding_;
AddressType type_;
Register base_;
@@ -169,6 +206,14 @@
class Operand : public ValueObject {
public:
+ enum OperandType {
+ Shifted,
+ Extended,
+ Immediate,
+ BitfieldImm,
+ Unknown,
+ };
+
// Data-processing operand - Uninitialized.
Operand() : encoding_(-1), type_(Unknown) { }
@@ -225,7 +270,7 @@
// Encodes the value of an immediate for a logical operation.
// Since these values are difficult to craft by hand, instead pass the
- // logical mask to the function Assembler::IsImmLogical to get n, imm_s, and
+ // logical mask to the function IsImmLogical to get n, imm_s, and
// imm_r.
Operand(uint8_t n, int8_t imm_s, int8_t imm_r) {
ASSERT((n == 1) || (n == 0));
@@ -237,13 +282,37 @@
(static_cast<int32_t>(imm_r) << kImmRShift);
}
- enum OperandType {
- Shifted,
- Extended,
- Immediate,
- BitfieldImm,
- Unknown,
- };
+ // Test if a given value can be encoded in the immediate field of a logical
+ // instruction.
+ // If it can be encoded, the function returns true, and values pointed to by
+ // n, imm_s and imm_r are updated with immediates encoded in the format
+ // required by the corresponding fields in the logical instruction.
+ // If it can't be encoded, the function returns false, and the operand is
+ // undefined.
+ static bool IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op);
+
+ // An immediate imm can be an operand to add/sub when the return value is
+ // Immediate, or a logical operation over sz bits when the return value is
+ // BitfieldImm. If the return value is Unknown, then the immediate can't be
+ // used as an operand in either instruction. The encoded operand is written
+ // to op.
+ static OperandType CanHold(int64_t imm, uint8_t sz, Operand* op) {
+ ASSERT(op != NULL);
+ ASSERT((sz == kXRegSizeInBits) || (sz == kWRegSizeInBits));
+ if (Utils::IsUint(12, imm)) {
+ op->encoding_ = imm << kImm12Shift;
+ op->type_ = Immediate;
+ } else if (((imm & 0xfff) == 0) && (Utils::IsUint(12, imm >> 12))) {
+ op->encoding_ = B22 | ((imm >> 12) << kImm12Shift);
+ op->type_ = Immediate;
+ } else if (IsImmLogical(imm, sz, op)) {
+ op->type_ = BitfieldImm;
+ } else {
+ op->encoding_ = 0;
+ op->type_ = Unknown;
+ }
+ return op->type_;
+ }
private:
uint32_t encoding() const {
@@ -262,25 +331,18 @@
class Assembler : public ValueObject {
public:
- explicit Assembler(bool use_far_branches = false)
- : buffer_(),
- object_pool_(GrowableObjectArray::Handle()),
- prologue_offset_(-1),
- use_far_branches_(use_far_branches),
- comments_() { }
+ explicit Assembler(bool use_far_branches = false);
~Assembler() { }
void PopRegister(Register r) {
- UNIMPLEMENTED();
+ Pop(r);
}
void Drop(intptr_t stack_elements) {
- UNIMPLEMENTED();
+ add(SP, SP, Operand(stack_elements * kWordSize));
}
- void Bind(Label* label) {
- UNIMPLEMENTED();
- }
+ void Bind(Label* label);
// Misc. functionality
intptr_t CodeSize() const { return buffer_.Size(); }
@@ -325,6 +387,14 @@
static const char* FpuRegisterName(FpuRegister reg);
+ void SetPrologueOffset() {
+ if (prologue_offset_ == -1) {
+ prologue_offset_ = CodeSize();
+ }
+ }
+
+ void ReserveAlignedFrameSpace(intptr_t frame_space);
+
// TODO(zra): Make sure this is right.
// Instruction pattern from entrypoint is used in Dart frame prologs
// to set up the frame and save a PC which can be used to figure out the
@@ -356,30 +426,35 @@
AddSubHelper(kDoubleWord, true, true, rd, rn, o);
}
+ // PC relative immediate add. imm is in bytes.
+ void adr(Register rd, int64_t imm) {
+ EmitPCRelOp(ADR, rd, imm);
+ }
+
// Logical immediate operations.
// TODO(zra): Add macros that check IsImmLogical, and fall back on a longer
// sequence on failure.
void andi(Register rd, Register rn, uint64_t imm) {
Operand imm_op;
- const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+ const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
ASSERT(immok);
EmitLogicalImmOp(ANDI, rd, rn, imm_op, kDoubleWord);
}
void orri(Register rd, Register rn, uint64_t imm) {
Operand imm_op;
- const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+ const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
ASSERT(immok);
EmitLogicalImmOp(ORRI, rd, rn, imm_op, kDoubleWord);
}
void eori(Register rd, Register rn, uint64_t imm) {
Operand imm_op;
- const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+ const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
ASSERT(immok);
EmitLogicalImmOp(EORI, rd, rn, imm_op, kDoubleWord);
}
void andis(Register rd, Register rn, uint64_t imm) {
Operand imm_op;
- const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+ const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
ASSERT(immok);
EmitLogicalImmOp(ANDIS, rd, rn, imm_op, kDoubleWord);
}
@@ -410,6 +485,61 @@
EmitLogicalShiftOp(BICS, rd, rn, o, kDoubleWord);
}
+ // Misc. arithmetic.
+ void udiv(Register rd, Register rn, Register rm) {
+ EmitMiscDP2Source(UDIV, rd, rn, rm, kDoubleWord);
+ }
+ void sdiv(Register rd, Register rn, Register rm) {
+ EmitMiscDP2Source(SDIV, rd, rn, rm, kDoubleWord);
+ }
+ void lslv(Register rd, Register rn, Register rm) {
+ EmitMiscDP2Source(LSLV, rd, rn, rm, kDoubleWord);
+ }
+ void lsrv(Register rd, Register rn, Register rm) {
+ EmitMiscDP2Source(LSRV, rd, rn, rm, kDoubleWord);
+ }
+ void asrv(Register rd, Register rn, Register rm) {
+ EmitMiscDP2Source(ASRV, rd, rn, rm, kDoubleWord);
+ }
+ void madd(Register rd, Register rn, Register rm, Register ra) {
+ EmitMiscDP3Source(MADD, rd, rn, rm, ra, kDoubleWord);
+ }
+
+ // Move wide immediate.
+ void movk(Register rd, uint16_t imm, int hw_idx) {
+ ASSERT(rd != SP);
+ const Register crd = ConcreteRegister(rd);
+ EmitMoveWideOp(MOVK, crd, imm, hw_idx, kDoubleWord);
+ }
+ void movn(Register rd, uint16_t imm, int hw_idx) {
+ ASSERT(rd != SP);
+ const Register crd = ConcreteRegister(rd);
+ EmitMoveWideOp(MOVN, crd, imm, hw_idx, kDoubleWord);
+ }
+ void movz(Register rd, uint16_t imm, int hw_idx) {
+ ASSERT(rd != SP);
+ const Register crd = ConcreteRegister(rd);
+ EmitMoveWideOp(MOVZ, crd, imm, hw_idx, kDoubleWord);
+ }
+
+ // Loads and Stores.
+ void ldr(Register rt, Address a) {
+ if (a.type() == Address::PCOffset) {
+ EmitLoadRegLiteral(LDRpc, rt, a, kDoubleWord);
+ } else {
+ // If we are doing pre-/post-indexing, and the base and result registers
+ // are the same, then the result of the load will be clobbered by the
+ // writeback, which is unlikely to be useful.
+ ASSERT(((a.type() != Address::PreIndex) &&
+ (a.type() != Address::PostIndex)) ||
+ (rt != a.base()));
+ EmitLoadStoreReg(LDR, rt, a, kDoubleWord);
+ }
+ }
+ void str(Register rt, Address a) {
+ EmitLoadStoreReg(STR, rt, a, kDoubleWord);
+ }
+
// Comparison.
// rn cmp o.
void cmp(Register rn, Operand o) {
@@ -420,45 +550,204 @@
adds(ZR, rn, o);
}
- // Move wide immediate.
- void movk(Register rd, int32_t imm, int32_t hw_idx) {
- ASSERT(rd != SP);
- const Register crd = ConcreteRegister(rd);
- EmitMoveWideOp(MOVK, crd, imm, hw_idx, kDoubleWord);
- }
- void movn(Register rd, int32_t imm, int32_t hw_idx) {
- ASSERT(rd != SP);
- const Register crd = ConcreteRegister(rd);
- EmitMoveWideOp(MOVN, crd, imm, hw_idx, kDoubleWord);
- }
- void movz(Register rd, int32_t imm, int32_t hw_idx) {
- ASSERT(rd != SP);
- const Register crd = ConcreteRegister(rd);
- EmitMoveWideOp(MOVZ, crd, imm, hw_idx, kDoubleWord);
+ // Conditional branch.
+ void b(Label* label, Condition cond = AL) {
+ EmitBranch(BCOND, cond, label);
}
- // Loads and Stores.
- void ldr(Register rt, Address a) {
- // If we are doing pre-/post-indexing, and the base and result registers
- // are the same, then the result of the load will be clobbered by the
- // writeback, which is unlikely to be useful.
- ASSERT(((a.type() != Address::PreIndex) &&
- (a.type() != Address::PostIndex)) ||
- (rt != a.base()));
- EmitLoadStoreReg(LDR, rt, a, kDoubleWord);
- }
- void str(Register rt, Address a) {
- EmitLoadStoreReg(STR, rt, a, kDoubleWord);
- }
+ // TODO(zra): branch and link with imm26 offset.
+ // TODO(zra): cbz, cbnz.
- // Function return.
+ // Branch, link, return.
+ void br(Register rn) {
+ EmitUnconditionalBranchRegOp(BR, rn);
+ }
+ void blr(Register rn) {
+ EmitUnconditionalBranchRegOp(BLR, rn);
+ }
void ret(Register rn = R30) {
EmitUnconditionalBranchRegOp(RET, rn);
}
+ // Exceptions.
+ void hlt(uint16_t imm) {
+ EmitExceptionGenOp(HLT, imm);
+ }
+
+ // Aliases.
+ void mov(Register rd, Register rn) {
+ if ((rd == SP) || (rn == SP)) {
+ add(rd, rn, Operand(0));
+ } else {
+ orr(rd, ZR, Operand(rn));
+ }
+ }
+ void mvn(Register rd, Register rm) {
+ orr(rd, ZR, Operand(rm));
+ }
+ void neg(Register rd, Register rm) {
+ sub(rd, ZR, Operand(rm));
+ }
+ void negs(Register rd, Register rm) {
+ subs(rd, ZR, Operand(rm));
+ }
+ void mul(Register rd, Register rn, Register rm) {
+ madd(rd, rn, rm, ZR);
+ }
+ void Push(Register reg) {
+ ASSERT(reg != PP); // Only push PP with PushPP().
+ str(reg, Address(SP, -1 * kWordSize, Address::PreIndex));
+ }
+ void Pop(Register reg) {
+ ASSERT(reg != PP); // Only pop PP with PopPP().
+ ldr(reg, Address(SP, 1 * kWordSize, Address::PostIndex));
+ }
+ void PushPP() {
+ // Add the heap object tag back to PP before putting it on the stack.
+ add(PP, PP, Operand(kHeapObjectTag));
+ str(PP, Address(SP, -1 * kWordSize, Address::PreIndex));
+ }
+ void PopPP() {
+ ldr(PP, Address(SP, 1 * kWordSize, Address::PostIndex));
+ sub(PP, PP, Operand(kHeapObjectTag));
+ }
+ void tst(Register rn, Operand o) {
+ ands(ZR, rn, o);
+ }
+ void tsti(Register rn, uint64_t imm) {
+ andis(ZR, rn, imm);
+ }
+
+ void SmiUntag(Register reg) {
+ add(reg, ZR, Operand(reg, ASR, kSmiTagSize));
+ }
+
+ // Branching to ExternalLabels.
+ void Branch(const ExternalLabel* label) {
+ LoadExternalLabel(TMP, label, kPatchable, PP);
+ br(TMP);
+ }
+
+ void BranchPatchable(const ExternalLabel* label) {
+ LoadPatchableImmediate(TMP, label->address());
+ br(TMP);
+ }
+
+ void BranchLink(const ExternalLabel* label, Register pp) {
+ if (Isolate::Current() == Dart::vm_isolate()) {
+ LoadImmediate(TMP, label->address(), kNoRegister);
+ blr(TMP);
+ } else {
+ LoadExternalLabel(TMP, label, kNotPatchable, pp);
+ blr(TMP);
+ }
+ }
+
+ void BranchLinkPatchable(const ExternalLabel* label) {
+ LoadExternalLabel(TMP, label, kPatchable, PP);
+ blr(TMP);
+ }
+
+ // Macros accepting a pp Register argument may attempt to load values from
+ // the object pool when possible. Unless you are sure that the untagged object
+ // pool pointer is in another register, or that it is not available at all,
+ // PP should be passed for pp.
+ void AddImmediate(Register dest, Register rn, int64_t imm, Register pp);
+ void CompareImmediate(Register rn, int64_t imm, Register pp);
+ void LoadFromOffset(Register dest, Register base, int32_t offset);
+ void LoadFieldFromOffset(Register dest, Register base, int32_t offset) {
+ LoadFromOffset(dest, base, offset - kHeapObjectTag);
+ }
+ void StoreToOffset(Register dest, Register base, int32_t offset);
+ void StoreFieldToOffset(Register dest, Register base, int32_t offset) {
+ StoreToOffset(dest, base, offset - kHeapObjectTag);
+ }
+
+ // Object pool, loading from pool, etc.
+ void LoadPoolPointer(Register pp);
+
+ enum Patchability {
+ kPatchable,
+ kNotPatchable,
+ };
+
+ void LoadWordFromPoolOffset(Register dst, Register pp, uint32_t offset);
+ intptr_t FindExternalLabel(const ExternalLabel* label,
+ Patchability patchable);
+ intptr_t FindObject(const Object& obj, Patchability patchable);
+ intptr_t FindImmediate(int64_t imm);
+ bool CanLoadObjectFromPool(const Object& object);
+ bool CanLoadImmediateFromPool(int64_t imm, Register pp);
+ void LoadExternalLabel(Register dst, const ExternalLabel* label,
+ Patchability patchable, Register pp);
+ void LoadObject(Register dst, const Object& obj, Register pp);
+ void LoadDecodableImmediate(Register reg, int64_t imm, Register pp);
+ void LoadPatchableImmediate(Register reg, int64_t imm);
+ void LoadImmediate(Register reg, int64_t imm, Register pp);
+
+ void PushObject(const Object& object, Register pp) {
+ LoadObject(TMP, object, pp);
+ Push(TMP);
+ }
+
+ void EnterFrame(intptr_t frame_size);
+ void LeaveFrame();
+
+ void EnterDartFrame(intptr_t frame_size);
+ void LeaveDartFrame();
+
+ void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
+
private:
AssemblerBuffer buffer_; // Contains position independent code.
- GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
+
+ // Objects and patchable jump targets.
+ GrowableObjectArray& object_pool_;
+
+ // Patchability of pool entries.
+ GrowableArray<Patchability> patchable_pool_entries_;
+
+ // Pair type parameter for DirectChainedHashMap.
+ class ObjIndexPair {
+ public:
+ // TODO(zra): A WeakTable should be used here instead, but then it would
+ // also have to be possible to register and de-register WeakTables with the
+ // heap. Also, the Assembler would need to become a StackResource.
+ // Issue 13305. In the meantime...
+ // CAUTION: the RawObject* below is only safe because:
+ // The HashMap that will use this pair type will not contain any RawObject*
+ // keys that are not in the object_pool_ array. Since the keys will be
+ // visited by the GC when it visits the object_pool_, and since all objects
+ // in the object_pool_ are Old (and so will not be moved) the GC does not
+ // also need to visit the keys here in the HashMap.
+
+ // Typedefs needed for the DirectChainedHashMap template.
+ typedef RawObject* Key;
+ typedef intptr_t Value;
+ typedef ObjIndexPair Pair;
+
+ ObjIndexPair(Key key, Value value) : key_(key), value_(value) { }
+
+ static Key KeyOf(Pair kv) { return kv.key_; }
+
+ static Value ValueOf(Pair kv) { return kv.value_; }
+
+ static intptr_t Hashcode(Key key) {
+ return reinterpret_cast<intptr_t>(key) >> kObjectAlignmentLog2;
+ }
+
+ static inline bool IsKeyEqual(Pair kv, Key key) {
+ return kv.key_ == key;
+ }
+
+ private:
+ Key key_;
+ Value value_;
+ };
+
+ // Hashmap for fast lookup in object pool.
+ DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+
int32_t prologue_offset_;
bool use_far_branches_;
@@ -480,15 +769,13 @@
GrowableArray<CodeComment*> comments_;
- bool IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op);
-
void AddSubHelper(OperandSize os, bool set_flags, bool subtract,
Register rd, Register rn, Operand o) {
ASSERT((rd != R31) && (rn != R31));
const Register crd = ConcreteRegister(rd);
const Register crn = ConcreteRegister(rn);
if (o.type() == Operand::Immediate) {
- ASSERT((rd != ZR) && (rn != ZR));
+ ASSERT(rn != ZR);
EmitAddSubImmOp(subtract ? SUBI : ADDI, crd, crn, o, os, set_flags);
} else if (o.type() == Operand::Shifted) {
ASSERT((rd != SP) && (rn != SP));
@@ -563,36 +850,158 @@
Emit(encoding);
}
- void EmitUnconditionalBranchRegOp(UnconditionalBranchRegOp op, Register rn) {
+ int32_t EncodeImm19BranchOffset(int64_t imm, int32_t instr) {
+ const int32_t imm32 = static_cast<int32_t>(imm);
+ const int32_t off = (((imm32 >> 2) << kImm19Shift) & kImm19Mask);
+ return (instr & ~kImm19Mask) | off;
+ }
+
+ int64_t DecodeImm19BranchOffset(int32_t instr) {
+ const int32_t off = (((instr >> kImm19Shift) & kImm19Shift) << 13) >> 13;
+ return static_cast<int64_t>(off);
+ }
+
+ void EmitCompareAndBranch(CompareAndBranchOp op, Register rt, int64_t imm,
+ OperandSize sz) {
+ ASSERT((sz == kDoubleWord) || (sz == kWord));
+ ASSERT(Utils::IsInt(21, imm) && ((imm & 0x3) == 0));
+ ASSERT((rt != SP) && (rt != R31));
+ const Register crt = ConcreteRegister(rt);
+ const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+ const int32_t encoded_offset = EncodeImm19BranchOffset(imm, 0);
const int32_t encoding =
- op | (static_cast<int32_t>(rn) << kRnShift);
+ op | size |
+ (static_cast<int32_t>(crt) << kRtShift) |
+ encoded_offset;
Emit(encoding);
}
- void EmitMoveWideOp(MoveWideOp op, Register rd, int32_t imm, int32_t hw_idx,
+ void EmitConditionalBranch(ConditionalBranchOp op, Condition cond,
+ int64_t imm) {
+ ASSERT(Utils::IsInt(21, imm) && ((imm & 0x3) == 0));
+ const int32_t encoding =
+ op |
+ (static_cast<int32_t>(cond) << kCondShift) |
+ (((imm >> 2) << kImm19Shift) & kImm19Mask);
+ Emit(encoding);
+ }
+
+ bool CanEncodeImm19BranchOffset(int64_t offset) {
+ ASSERT(Utils::IsAligned(offset, 4));
+ return Utils::IsInt(19, offset);
+ }
+
+ // TODO(zra): Implement far branches. Requires loading large immediates.
+ void EmitBranch(ConditionalBranchOp op, Condition cond, Label* label) {
+ if (label->IsBound()) {
+ const int64_t dest = label->Position() - buffer_.Size();
+ ASSERT(CanEncodeImm19BranchOffset(dest));
+ EmitConditionalBranch(op, cond, dest);
+ } else {
+ const int64_t position = buffer_.Size();
+ ASSERT(CanEncodeImm19BranchOffset(position));
+ EmitConditionalBranch(op, cond, label->position_);
+ label->LinkTo(position);
+ }
+ }
+
+ void EmitUnconditionalBranchRegOp(UnconditionalBranchRegOp op, Register rn) {
+ ASSERT((rn != SP) && (rn != R31));
+ const Register crn = ConcreteRegister(rn);
+ const int32_t encoding =
+ op | (static_cast<int32_t>(crn) << kRnShift);
+ Emit(encoding);
+ }
+
+ void EmitExceptionGenOp(ExceptionGenOp op, uint16_t imm) {
+ const int32_t encoding =
+ op | (static_cast<int32_t>(imm) << kImm16Shift);
+ Emit(encoding);
+ }
+
+ void EmitMoveWideOp(MoveWideOp op, Register rd, uint16_t imm, int hw_idx,
OperandSize sz) {
- ASSERT(Utils::IsUint(16, imm));
ASSERT((hw_idx >= 0) && (hw_idx <= 3));
ASSERT((sz == kDoubleWord) || (sz == kWord));
const int32_t size = (sz == kDoubleWord) ? B31 : 0;
const int32_t encoding =
op | size |
(static_cast<int32_t>(rd) << kRdShift) |
- (hw_idx << kHWShift) |
- (imm << kImm16Shift);
+ (static_cast<int32_t>(hw_idx) << kHWShift) |
+ (static_cast<int32_t>(imm) << kImm16Shift);
Emit(encoding);
}
void EmitLoadStoreReg(LoadStoreRegOp op, Register rt, Address a,
OperandSize sz) {
+ const Register crt = ConcreteRegister(rt);
const int32_t size = Log2OperandSizeBytes(sz);
const int32_t encoding =
op | (size << kSzShift) |
- (static_cast<int32_t>(rt) << kRtShift) |
+ (static_cast<int32_t>(crt) << kRtShift) |
a.encoding();
Emit(encoding);
}
+ void EmitLoadRegLiteral(LoadRegLiteralOp op, Register rt, Address a,
+ OperandSize sz) {
+ ASSERT((sz == kDoubleWord) || (sz == kWord));
+ ASSERT((rt != SP) && (rt != R31));
+ const Register crt = ConcreteRegister(rt);
+ const int32_t size = (sz == kDoubleWord) ? B30 : 0;
+ const int32_t encoding =
+ op | size |
+ (static_cast<int32_t>(crt) << kRtShift) |
+ a.encoding();
+ Emit(encoding);
+ }
+
+ void EmitPCRelOp(PCRelOp op, Register rd, int64_t imm) {
+ ASSERT(Utils::IsInt(21, imm));
+ ASSERT((rd != R31) && (rd != SP));
+ const Register crd = ConcreteRegister(rd);
+ const int32_t loimm = (imm & 0x3) << 29;
+ const int32_t hiimm = ((imm >> 2) << kImm19Shift) & kImm19Mask;
+ const int32_t encoding =
+ op | loimm | hiimm |
+ (static_cast<int32_t>(crd) << kRdShift);
+ Emit(encoding);
+ }
+
+ void EmitMiscDP2Source(MiscDP2SourceOp op,
+ Register rd, Register rn, Register rm,
+ OperandSize sz) {
+ ASSERT((rd != SP) && (rn != SP) && (rm != SP));
+ const Register crd = ConcreteRegister(rd);
+ const Register crn = ConcreteRegister(rn);
+ const Register crm = ConcreteRegister(rm);
+ const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+ const int32_t encoding =
+ op | size |
+ (static_cast<int32_t>(crd) << kRdShift) |
+ (static_cast<int32_t>(crn) << kRnShift) |
+ (static_cast<int32_t>(crm) << kRmShift);
+ Emit(encoding);
+ }
+
+ void EmitMiscDP3Source(MiscDP3SourceOp op,
+ Register rd, Register rn, Register rm, Register ra,
+ OperandSize sz) {
+ ASSERT((rd != SP) && (rn != SP) && (rm != SP) && (ra != SP));
+ const Register crd = ConcreteRegister(rd);
+ const Register crn = ConcreteRegister(rn);
+ const Register crm = ConcreteRegister(rm);
+ const Register cra = ConcreteRegister(ra);
+ const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+ const int32_t encoding =
+ op | size |
+ (static_cast<int32_t>(crd) << kRdShift) |
+ (static_cast<int32_t>(crn) << kRnShift) |
+ (static_cast<int32_t>(crm) << kRmShift) |
+ (static_cast<int32_t>(cra) << kRaShift);
+ Emit(encoding);
+ }
+
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(Assembler);
};
diff --git a/runtime/vm/assembler_arm64_test.cc b/runtime/vm/assembler_arm64_test.cc
index 72feb31..eb1006b 100644
--- a/runtime/vm/assembler_arm64_test.cc
+++ b/runtime/vm/assembler_arm64_test.cc
@@ -304,8 +304,8 @@
__ movz(R0, 43, 0);
__ movz(R1, 42, 0);
__ add(R2, SP, Operand(1));
- __ str(R1, Address(R2, -1, Address::PreIndex));
- __ ldr(R0, Address(R2, -1, Address::PostIndex));
+ __ str(R1, Address(R2, -1));
+ __ ldr(R0, Address(R2, -1));
__ ret();
}
@@ -491,8 +491,6 @@
EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
}
-// TODO(zra): ands and bics after branches are implemented.
-
// Logical immediate operations.
ASSEMBLER_TEST_GENERATE(AndImm, assembler) {
@@ -553,7 +551,739 @@
EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
}
-// TODO(zra): andis after branches are implemented.
+
+// Comparisons, branching.
+ASSEMBLER_TEST_GENERATE(BranchALForward, assembler) {
+ Label l;
+ __ movz(R0, 42, 0);
+ __ b(&l, AL);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BranchALForward, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BranchALBackwards, assembler) {
+ Label l, leave;
+ __ movz(R0, 42, 0);
+ __ b(&l, AL);
+
+ __ movz(R0, 0, 0);
+ __ Bind(&leave);
+ __ ret();
+ __ movz(R0, 0, 0);
+
+ __ Bind(&l);
+ __ b(&leave, AL);
+ __ movz(R0, 0, 0);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BranchALBackwards, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEqBranch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 234, 0);
+ __ movz(R2, 234, 0);
+
+ __ cmp(R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEqBranch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEqBranchNotTaken, assembler) {
+ Label l;
+
+ __ movz(R0, 0, 0);
+ __ movz(R1, 233, 0);
+ __ movz(R2, 234, 0);
+
+ __ cmp(R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 42, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEqBranchNotTaken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEq1Branch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 1, 0);
+
+ __ cmp(R1, Operand(1));
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEq1Branch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmnEq1Branch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movn(R1, 0, 0); // R1 <- -1
+
+ __ cmn(R1, Operand(1));
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmnEq1Branch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpLtBranch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 233, 0);
+ __ movz(R2, 234, 0);
+
+ __ cmp(R1, Operand(R2));
+ __ b(&l, LT);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpLtBranch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpLtBranchNotTaken, assembler) {
+ Label l;
+
+ __ movz(R0, 0, 0);
+ __ movz(R1, 235, 0);
+ __ movz(R2, 234, 0);
+
+ __ cmp(R1, Operand(R2));
+ __ b(&l, LT);
+ __ movz(R0, 42, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpLtBranchNotTaken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndsBranch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 2, 0);
+ __ movz(R2, 1, 0);
+
+ __ ands(R3, R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndsBranch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndsBranchNotTaken, assembler) {
+ Label l;
+
+ __ movz(R0, 0, 0);
+ __ movz(R1, 2, 0);
+ __ movz(R2, 2, 0);
+
+ __ ands(R3, R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 42, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndsBranchNotTaken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BicsBranch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 2, 0);
+ __ movz(R2, 2, 0);
+
+ __ bics(R3, R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BicsBranch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BicsBranchNotTaken, assembler) {
+ Label l;
+
+ __ movz(R0, 0, 0);
+ __ movz(R1, 2, 0);
+ __ movz(R2, 1, 0);
+
+ __ bics(R3, R1, Operand(R2));
+ __ b(&l, EQ);
+ __ movz(R0, 42, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BicsBranchNotTaken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndisBranch, assembler) {
+ Label l;
+
+ __ movz(R0, 42, 0);
+ __ movz(R1, 2, 0);
+
+ __ andis(R3, R1, 1);
+ __ b(&l, EQ);
+ __ movz(R0, 0, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndisBranch, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndisBranchNotTaken, assembler) {
+ Label l;
+
+ __ movz(R0, 0, 0);
+ __ movz(R1, 2, 0);
+
+ __ andis(R3, R1, 2);
+ __ b(&l, EQ);
+ __ movz(R0, 42, 0);
+ __ Bind(&l);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndisBranchNotTaken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Address of PC-rel offset, br, blr.
+ASSEMBLER_TEST_GENERATE(AdrBr, assembler) {
+ __ movz(R0, 123, 0);
+ __ adr(R1, 3 * Instr::kInstrSize); // R1 <- PC + 3*Instr::kInstrSize
+ __ br(R1);
+ __ ret();
+
+ // br goes here.
+ __ movz(R0, 42, 0);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AdrBr, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AdrBlr, assembler) {
+ __ movz(R0, 123, 0);
+ __ add(R3, ZR, Operand(LR)); // Save LR.
+ __ adr(R1, 4 * Instr::kInstrSize); // R1 <- PC + 4*Instr::kInstrSize
+ __ blr(R1);
+ __ add(LR, ZR, Operand(R3));
+ __ ret();
+
+ // blr goes here.
+ __ movz(R0, 42, 0);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AdrBlr, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Misc. arithmetic.
+ASSEMBLER_TEST_GENERATE(Udiv, assembler) {
+ __ movz(R0, 27, 0);
+ __ movz(R1, 9, 0);
+ __ udiv(R2, R0, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(3, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv, assembler) {
+ __ movz(R0, 27, 0);
+ __ movz(R1, 9, 0);
+ __ neg(R1, R1);
+ __ sdiv(R2, R0, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(-3, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Udiv_zero, assembler) {
+ __ movz(R0, 27, 0);
+ __ movz(R1, 0, 0);
+ __ udiv(R2, R0, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv_zero, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv_zero, assembler) {
+ __ movz(R0, 27, 0);
+ __ movz(R1, 0, 0);
+ __ sdiv(R2, R0, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv_zero, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Udiv_corner, assembler) {
+ __ movz(R0, 0x8000, 3); // R0 <- 0x8000000000000000
+ __ movn(R1, 0, 0); // R1 <- 0xffffffffffffffff
+ __ udiv(R2, R0, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv_corner, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv_corner, assembler) {
+ __ movz(R3, 0x8000, 3); // R0 <- 0x8000000000000000
+ __ movn(R1, 0, 0); // R1 <- 0xffffffffffffffff
+ __ sdiv(R2, R3, R1);
+ __ mov(R0, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv_corner, test) {
+ EXPECT(test != NULL);
+ typedef int (*Tst)();
+ EXPECT_EQ(static_cast<int64_t>(0x8000000000000000),
+ EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lslv, assembler) {
+ __ movz(R1, 21, 0);
+ __ movz(R2, 1, 0);
+ __ lslv(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Lslv, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lsrv, assembler) {
+ __ movz(R1, 84, 0);
+ __ movz(R2, 1, 0);
+ __ lsrv(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Lsrv, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LShiftingV, assembler) {
+ __ movz(R1, 1, 0);
+ __ movz(R2, 63, 0);
+ __ lslv(R1, R1, R2);
+ __ lsrv(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LShiftingV, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(1, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(RShiftingV, assembler) {
+ __ movz(R1, 1, 0);
+ __ movz(R2, 63, 0);
+ __ lslv(R1, R1, R2);
+ __ asrv(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(RShiftingV, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Mult_pos, assembler) {
+ __ movz(R1, 6, 0);
+ __ movz(R2, 7, 0);
+ __ mul(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Mult_pos, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Mult_neg, assembler) {
+ __ movz(R1, 6, 0);
+ __ movz(R2, 7, 0);
+ __ neg(R2, R2);
+ __ mul(R0, R1, R2);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Mult_neg, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Loading immediate values without the object pool.
+ASSEMBLER_TEST_GENERATE(LoadImmediateSmall, assembler) {
+ __ LoadImmediate(R0, 42, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateSmall, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMed, assembler) {
+ __ LoadImmediate(R0, 0xf1234123, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMed, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0xf1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMed2, assembler) {
+ __ LoadImmediate(R0, 0x4321f1234123, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMed2, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0x4321f1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateLarge, assembler) {
+ __ LoadImmediate(R0, 0x9287436598237465, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateLarge, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(static_cast<int64_t>(0x9287436598237465),
+ EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateSmallNeg, assembler) {
+ __ LoadImmediate(R0, -42, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateSmallNeg, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg, assembler) {
+ __ LoadImmediate(R0, -0x1212341234, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-0x1212341234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg2, assembler) {
+ __ LoadImmediate(R0, -0x1212340000, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg2, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-0x1212340000, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg3, assembler) {
+ __ LoadImmediate(R0, -0x1200001234, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg3, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-0x1200001234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg4, assembler) {
+ __ LoadImmediate(R0, -0x12341234, kNoRegister);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg4, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-0x12341234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Loading immediate values with the object pool.
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPSmall, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadImmediate(R0, 42, PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPSmall, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadImmediate(R0, 0xf1234123, PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPMed, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0xf1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed2, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadImmediate(R0, 0x4321f1234124, PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPMed2, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0x4321f1234124, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPLarge, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadImmediate(R0, 0x9287436598237465, PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPLarge, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(static_cast<int64_t>(0x9287436598237465),
+ EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// LoadObject null.
+ASSEMBLER_TEST_GENERATE(LoadObjectNull, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadObject(R0, Object::null_object(), PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectNull, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(reinterpret_cast<int64_t>(Object::null()),
+ EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadObjectTrue, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadObject(R0, Bool::True(), PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectTrue, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(reinterpret_cast<int64_t>(Bool::True().raw()),
+ EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadObjectFalse, assembler) {
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadObject(R0, Bool::False(), PP);
+ __ PopPP();
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectFalse, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(reinterpret_cast<int64_t>(Bool::False().raw()),
+ EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
} // namespace dart
diff --git a/runtime/vm/assembler_arm_test.cc b/runtime/vm/assembler_arm_test.cc
index 76cb9b6..c020887 100644
--- a/runtime/vm/assembler_arm_test.cc
+++ b/runtime/vm/assembler_arm_test.cc
@@ -1530,7 +1530,7 @@
if (TargetCPUFeatures::integer_division_supported()) {
__ mov(R0, ShifterOperand(27));
__ mov(R1, ShifterOperand(0));
- __ udiv(R2, R0, R1);
+ __ sdiv(R2, R0, R1);
__ mov(R0, ShifterOperand(R2));
} else {
__ LoadImmediate(R0, 0);
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index 522928e..cd74865 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -665,6 +665,10 @@
void MoveRegister(Register to, Register from);
void PopRegister(Register r);
+ // Macros accepting a pp Register argument may attempt to load values from
+ // the object pool when possible. Unless you are sure that the untagged object
+ // pool pointer is in another register, or that it is not available at all,
+ // PP should be passed for pp.
void AddImmediate(Register reg, const Immediate& imm, Register pp);
void AddImmediate(const Address& address, const Immediate& imm, Register pp);
diff --git a/runtime/vm/bit_vector.h b/runtime/vm/bit_vector.h
index 62cf753..69a9d3f 100644
--- a/runtime/vm/bit_vector.h
+++ b/runtime/vm/bit_vector.h
@@ -12,7 +12,7 @@
namespace dart {
// Bit vector implementation.
-class BitVector: public ZoneAllocated {
+class BitVector : public ZoneAllocated {
public:
// Iterator for the elements of this BitVector.
class Iterator : public ValueObject {
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index cee8b72..d12752f 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -67,6 +67,11 @@
Bootstrap::typed_data_source_paths_,
Bootstrap::typed_data_patch_paths_),
+ INIT_LIBRARY(ObjectStore::kProfiler,
+ profiler,
+ Bootstrap::profiler_source_paths_,
+ Bootstrap::profiler_patch_paths_),
+
{ ObjectStore::kNone, NULL, NULL, NULL, NULL }
};
diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h
index 5d3e53b..9a3c09b 100644
--- a/runtime/vm/bootstrap.h
+++ b/runtime/vm/bootstrap.h
@@ -30,6 +30,7 @@
static const char* math_source_paths_[];
static const char* mirrors_source_paths_[];
static const char* typed_data_source_paths_[];
+ static const char* profiler_source_paths_[];
static const char* utf_source_paths_[];
// Source path mapping for patch URI and 'parts'.
@@ -42,6 +43,7 @@
static const char* math_patch_paths_[];
static const char* mirrors_patch_paths_[];
static const char* typed_data_patch_paths_[];
+ static const char* profiler_patch_paths_[];
};
} // namespace dart
diff --git a/runtime/vm/bootstrap_natives.cc b/runtime/vm/bootstrap_natives.cc
index 4682e98..6efff89 100644
--- a/runtime/vm/bootstrap_natives.cc
+++ b/runtime/vm/bootstrap_natives.cc
@@ -53,39 +53,67 @@
}
+const uint8_t* BootstrapNatives::Symbol(Dart_NativeFunction* nf) {
+ int num_entries = sizeof(BootStrapEntries) / sizeof(struct NativeEntries);
+ for (int i = 0; i < num_entries; i++) {
+ struct NativeEntries* entry = &(BootStrapEntries[i]);
+ if (reinterpret_cast<Dart_NativeFunction*>(entry->function_) == nf) {
+ return reinterpret_cast<const uint8_t*>(entry->name_);
+ }
+ }
+ return NULL;
+}
+
+
void Bootstrap::SetupNativeResolver() {
Library& library = Library::Handle();
Dart_NativeEntryResolver resolver =
reinterpret_cast<Dart_NativeEntryResolver>(BootstrapNatives::Lookup);
+ Dart_NativeEntrySymbol symbol_resolver =
+ reinterpret_cast<Dart_NativeEntrySymbol>(
+ BootstrapNatives::Symbol);
+
library = Library::AsyncLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::CoreLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::MathLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::MirrorsLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::InternalLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::IsolateLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
library = Library::TypedDataLibrary();
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
+
+ library = Library::ProfilerLibrary();
+ ASSERT(!library.IsNull());
+ library.set_native_entry_resolver(resolver);
+ library.set_native_entry_symbol_resolver(symbol_resolver);
}
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index e6038e0..c4e6ea91 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -348,6 +348,11 @@
V(WeakProperty_setValue, 2) \
V(Uri_isWindowsPlatform, 0) \
V(LibraryPrefix_load, 1) \
+ V(UserTag_new, 2) \
+ V(UserTag_label, 1) \
+ V(UserTag_makeCurrent, 1) \
+ V(Profiler_getCurrentTag, 0) \
+ V(Profiler_clearCurrentTag, 0) \
class BootstrapNatives : public AllStatic {
@@ -356,6 +361,8 @@
int argument_count,
bool* auto_setup_scope);
+ static const uint8_t* Symbol(Dart_NativeFunction* nf);
+
#define DECLARE_BOOTSTRAP_NATIVE(name, ignored) \
static void DN_##name(Dart_NativeArguments args);
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index c461a5e..7909d8d 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -89,6 +89,10 @@
class_heap_stats_table_ = new_stats_table;
}
ASSERT(top_ < capacity_);
+ if (!Class::is_valid_id(top_)) {
+ FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
+ top_);
+ }
cls.set_id(top_);
table_[top_] = cls.raw();
top_++; // Increment next index.
@@ -102,7 +106,7 @@
if (index >= capacity_) {
// Grow the capacity of the class table.
intptr_t new_capacity = index + capacity_increment_;
- if (new_capacity < capacity_) {
+ if (!Class::is_valid_id(index) || new_capacity < capacity_) {
FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
index);
}
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 0266e1b..1b95b95 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -950,21 +950,6 @@
}
-// A non-closure object was invoked as a closure, so call the "call" method
-// on it.
-// Arg0: arguments descriptor.
-// Arg1: arguments array, including non-closure object.
-DEFINE_RUNTIME_ENTRY(InvokeNonClosure, 2) {
- const Array& args_descriptor = Array::CheckedHandle(arguments.ArgAt(0));
- const Array& function_args = Array::CheckedHandle(arguments.ArgAt(1));
-
- const Object& result = Object::Handle(
- DartEntry::InvokeClosure(function_args, args_descriptor));
- CheckResultError(result);
- arguments.SetReturn(result);
-}
-
-
static bool CanOptimizeFunction(const Function& function, Isolate* isolate) {
const intptr_t kLowInvocationCount = -100000000;
if (isolate->debugger()->IsStepping() ||
diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h
index aeb6357..e9a8dbd 100644
--- a/runtime/vm/code_generator.h
+++ b/runtime/vm/code_generator.h
@@ -39,7 +39,6 @@
DECLARE_RUNTIME_ENTRY(OptimizeInvokedFunction);
DECLARE_RUNTIME_ENTRY(TraceICCall);
DECLARE_RUNTIME_ENTRY(PatchStaticCall);
-DECLARE_RUNTIME_ENTRY(InvokeNonClosure);
DECLARE_RUNTIME_ENTRY(ReThrow);
DECLARE_RUNTIME_ENTRY(StackOverflow);
DECLARE_RUNTIME_ENTRY(Throw);
diff --git a/runtime/vm/code_patcher_arm64.cc b/runtime/vm/code_patcher_arm64.cc
index 2e81384..1e03d79 100644
--- a/runtime/vm/code_patcher_arm64.cc
+++ b/runtime/vm/code_patcher_arm64.cc
@@ -14,58 +14,72 @@
RawArray* CodePatcher::GetClosureArgDescAt(uword return_address,
const Code& code) {
- UNIMPLEMENTED();
- return NULL;
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern call(return_address, code);
+ return call.ClosureArgumentsDescriptor();
}
uword CodePatcher::GetStaticCallTargetAt(uword return_address,
const Code& code) {
- UNIMPLEMENTED();
- return 0;
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern call(return_address, code);
+ return call.TargetAddress();
}
void CodePatcher::PatchStaticCallAt(uword return_address,
const Code& code,
uword new_target) {
- UNIMPLEMENTED();
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern call(return_address, code);
+ call.SetTargetAddress(new_target);
}
void CodePatcher::PatchInstanceCallAt(uword return_address,
const Code& code,
uword new_target) {
- UNIMPLEMENTED();
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern call(return_address, code);
+ call.SetTargetAddress(new_target);
}
int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+ // TODO(zra): Needed for debugger.
UNIMPLEMENTED();
return 0;
}
void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+ // TODO(zra): Needed for debugger.
UNIMPLEMENTED();
}
void CodePatcher::InsertCallAt(uword start, uword target) {
- UNIMPLEMENTED();
+ // The inserted call should not overlap the lazy deopt jump code.
+ ASSERT(start + CallPattern::kLengthInBytes <= target);
+ CallPattern::InsertAt(start, target);
}
uword CodePatcher::GetInstanceCallAt(uword return_address,
const Code& code,
ICData* ic_data) {
- UNIMPLEMENTED();
- return 0;
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern call(return_address, code);
+ if (ic_data != NULL) {
+ *ic_data = call.IcData();
+ }
+ return call.TargetAddress();
}
intptr_t CodePatcher::InstanceCallSizeInBytes() {
- // The instance call instruction sequence has a variable size on ARM.
+ // The instance call instruction sequence has a variable size on ARM64.
UNREACHABLE();
return 0;
}
@@ -73,8 +87,14 @@
RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
uword return_address, const Code& code, ICData* ic_data_result) {
- UNIMPLEMENTED();
- return NULL;
+ ASSERT(code.ContainsInstructionAt(return_address));
+ CallPattern static_call(return_address, code);
+ ICData& ic_data = ICData::Handle();
+ ic_data ^= static_call.IcData();
+ if (ic_data_result != NULL) {
+ *ic_data_result = ic_data.raw();
+ }
+ return ic_data.GetTargetAt(0);
}
diff --git a/runtime/vm/code_patcher_arm64_test.cc b/runtime/vm/code_patcher_arm64_test.cc
index 7c2fe8f..32970b2 100644
--- a/runtime/vm/code_patcher_arm64_test.cc
+++ b/runtime/vm/code_patcher_arm64_test.cc
@@ -5,6 +5,59 @@
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM64)
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/code_generator.h"
+#include "vm/code_patcher.h"
+#include "vm/dart_entry.h"
+#include "vm/instructions.h"
+#include "vm/native_entry.h"
+#include "vm/native_entry_test.h"
+#include "vm/stub_code.h"
+#include "vm/symbols.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+ASSEMBLER_TEST_GENERATE(IcDataAccess, assembler) {
+ const String& class_name = String::Handle(Symbols::New("ownerClass"));
+ const Script& script = Script::Handle();
+ const Class& owner_class =
+ Class::Handle(Class::New(class_name, script, Scanner::kNoSourcePos));
+ const String& function_name = String::Handle(Symbols::New("callerFunction"));
+ const Function& function = Function::Handle(
+ Function::New(function_name, RawFunction::kRegularFunction,
+ true, false, false, false, false, owner_class, 0));
+
+ const String& target_name = String::Handle(String::New("targetFunction"));
+ const Array& args_descriptor =
+ Array::Handle(ArgumentsDescriptor::New(1, Object::null_array()));
+ const ICData& ic_data = ICData::ZoneHandle(ICData::New(function,
+ target_name,
+ args_descriptor,
+ 15,
+ 1));
+
+ __ LoadObject(R5, ic_data, PP);
+ ExternalLabel target_label(
+ "InlineCache", StubCode::OneArgCheckInlineCacheEntryPoint());
+ __ BranchLinkPatchable(&target_label);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+ uword return_address =
+ test->entry() + test->code().Size() - Instr::kInstrSize;
+ ICData& ic_data = ICData::Handle();
+ CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data);
+ EXPECT_STREQ("targetFunction",
+ String::Handle(ic_data.target_name()).ToCString());
+ EXPECT_EQ(1, ic_data.num_args_tested());
+ EXPECT_EQ(0, ic_data.NumberOfChecks());
+}
+
+} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index c1baf8d..d1afca8 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -193,6 +193,7 @@
// Register aliases.
const Register TMP = IP; // Used as scratch register by assembler.
+const Register TMP2 = kNoRegister; // There is no second assembler temporary.
const Register CTX = R9; // Caches current context in generated code.
const Register PP = R10; // Caches object pool pointer in generated code.
const Register SPREG = SP; // Stack pointer register.
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index 308a279..d16cf72 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -27,8 +27,8 @@
R13 = 13,
R14 = 14,
R15 = 15,
- R16 = 16,
- R17 = 17,
+ R16 = 16, // IP0 aka TMP
+ R17 = 17, // IP1 aka TMP2
R18 = 18,
R19 = 19,
R20 = 20,
@@ -36,9 +36,9 @@
R22 = 22,
R23 = 23,
R24 = 24,
- kLastFreeCpuRegister = 24,
- R25 = 25, // IP0
- R26 = 26, // IP1
+ R25 = 25,
+ R26 = 26,
+ kLastFreeCpuRegister = 26,
R27 = 27, // PP
R28 = 28, // CTX
R29 = 29, // FP
@@ -53,8 +53,8 @@
ZR = 33,
// Aliases.
- IP0 = R25,
- IP1 = R26,
+ IP0 = R16,
+ IP1 = R17,
FP = R29,
LR = R30,
};
@@ -107,13 +107,12 @@
const FpuRegister kNoFpuRegister = kNoVRegister;
// Register aliases.
-const Register TMP = R25; // Used as scratch register by assembler.
-const Register TMP0 = R25;
-const Register TMP1 = R26;
-const Register CTX = R27; // Caches current context in generated code.
-const Register PP = R26; // Caches object pool pointer in generated code.
-const Register SPREG = R31; // Stack pointer register.
+const Register TMP = R16; // Used as scratch register by assembler.
+const Register TMP2 = R17;
+const Register CTX = R28; // Caches current context in generated code.
+const Register PP = R27; // Caches object pool pointer in generated code.
const Register FPREG = FP; // Frame pointer register.
+const Register SPREG = R31; // Stack pointer register.
const Register ICREG = R5; // IC data register.
// Exception object is passed in this register to the catch handlers when an
@@ -142,8 +141,8 @@
const RegList kAbiPreservedCpuRegs =
(1 << R19) | (1 << R20) | (1 << R21) | (1 << R22) |
(1 << R23) | (1 << R24) | (1 << R25) | (1 << R26) |
- (1 << R27) | (1 << R28) | (1 << R29);
-const int kAbiPreservedCpuRegCount = 11;
+ (1 << R27) | (1 << R28);
+const int kAbiPreservedCpuRegCount = 10;
const VRegister kAbiFirstPreservedFpuReg = V8;
const VRegister kAbiLastPreservedFpuReg = V15;
const int kAbiPreservedFpuRegCount = 8;
@@ -154,14 +153,14 @@
(1 << R4) | (1 << R5) | (1 << R6) | (1 << R7) |
(1 << R8) | (1 << R9) | (1 << R10) | (1 << R11) |
(1 << R12) | (1 << R13) | (1 << R14) | (1 << R15) |
- (1 << R16) | (1 << R17) | (1 << R18) | (1 << R19) |
- (1 << R20) | (1 << R21) | (1 << R22) | (1 << R23) |
- (1 << R24);
+ (1 << R18) | (1 << R19) | (1 << R20) | (1 << R21) |
+ (1 << R22) | (1 << R23) | (1 << R24) | (1 << R25) |
+ (1 << R26);
// Registers available to Dart that are not preserved by runtime calls.
const RegList kDartVolatileCpuRegs =
kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
-const int kDartVolatileCpuRegCount = 19;
+const int kDartVolatileCpuRegCount = 17;
const VRegister kDartFirstVolatileFpuReg = V0;
const VRegister kDartLastVolatileFpuReg = V7;
const int kDartVolatileFpuRegCount = 8;
@@ -259,6 +258,21 @@
DPSimd2Fixed = B28 | DPSimd1Fixed,
};
+// C3.2.1
+enum CompareAndBranchOp {
+ CompareAndBranchMask = 0x7e000000,
+ CompareAndBranchFixed = CompareBranchFixed | B29,
+ CBZ = CompareBranchFixed,
+ CBNZ = CompareBranchFixed | B24,
+};
+
+// C.3.2.2
+enum ConditionalBranchOp {
+ ConditionalBranchMask = 0xfe000000,
+ ConditionalBranchFixed = CompareBranchFixed | B30,
+ BCOND = ConditionalBranchFixed,
+};
+
// C3.2.3
enum ExceptionGenOp {
ExceptionGenMask = 0xff000000,
@@ -275,6 +289,22 @@
HINT = SystemFixed | B17 | B16 | B13 | B4 | B3 | B2 | B1 | B0,
};
+// C3.2.5
+enum TestAndBranchOp {
+ TestAndBranchMask = 0x7e000000,
+ TestAndBranchFixed = CompareBranchFixed | B29 | B25,
+ TBZ = TestAndBranchFixed,
+ TBNZ = TestAndBranchFixed | B24,
+};
+
+// C3.2.6
+enum UnconditionalBranchOp {
+ UnconditionalBranchMask = 0x7c000000,
+ UnconditionalBranchFixed = CompareBranchFixed,
+ B = UnconditionalBranchFixed,
+ BL = UnconditionalBranchFixed | B31,
+};
+
// C3.2.7
enum UnconditionalBranchRegOp {
UnconditionalBranchRegMask = 0xfe000000,
@@ -291,6 +321,13 @@
LDR = LoadStoreRegFixed | B22,
};
+// C3.3.5
+enum LoadRegLiteralOp {
+ LoadRegLiteralMask = 0x3b000000,
+ LoadRegLiteralFixed = LoadStoreFixed | B28,
+ LDRpc = LoadRegLiteralFixed,
+};
+
// C3.4.1
enum AddSubImmOp {
AddSubImmMask = 0x1f000000,
@@ -318,6 +355,14 @@
MOVK = MoveWideFixed | B30 | B29,
};
+// C3.4.6
+enum PCRelOp {
+ PCRelMask = 0x1f000000,
+ PCRelFixed = DPImmediateFixed,
+ ADR = PCRelFixed,
+ ADRP = PCRelFixed | B31,
+};
+
// C3.5.1
enum AddSubShiftExtOp {
AddSubShiftExtMask = 0x1f000000,
@@ -326,6 +371,25 @@
SUB = AddSubShiftExtFixed | B30,
};
+// C3.5.8
+enum MiscDP2SourceOp {
+ MiscDP2SourceMask = 0x5fe00000,
+ MiscDP2SourceFixed = DPRegisterFixed | B28 | B23 | B22,
+ UDIV = MiscDP2SourceFixed | B11,
+ SDIV = MiscDP2SourceFixed | B11 | B10,
+ LSLV = MiscDP2SourceFixed | B13,
+ LSRV = MiscDP2SourceFixed | B13 | B10,
+ ASRV = MiscDP2SourceFixed | B13 | B11,
+};
+
+// C3.5.9
+enum MiscDP3SourceOp {
+ MiscDP3SourceMask = 0x1f000000,
+ MiscDP3SourceFixed = DPRegisterFixed | B28 | B24,
+ MADD = MiscDP3SourceFixed,
+};
+
+// C3.5.10
enum LogicalShiftOp {
LogicalShiftMask = 0x1f000000,
LogicalShiftFixed = DPRegisterFixed,
@@ -346,14 +410,22 @@
_V(DPRegister) \
_V(DPSimd1) \
_V(DPSimd2) \
+_V(CompareAndBranch) \
+_V(ConditionalBranch) \
_V(ExceptionGen) \
_V(System) \
-_V(LoadStoreReg) \
+_V(TestAndBranch) \
+_V(UnconditionalBranch) \
_V(UnconditionalBranchReg) \
+_V(LoadStoreReg) \
+_V(LoadRegLiteral) \
_V(AddSubImm) \
_V(LogicalImm) \
_V(MoveWide) \
+_V(PCRel) \
_V(AddSubShiftExt) \
+_V(MiscDP2Source) \
+_V(MiscDP3Source) \
_V(LogicalShift) \
@@ -382,7 +454,6 @@
enum R31Type {
R31IsSP,
R31IsZR,
- R31IsUndef,
};
// Constants used for the decoding or encoding of the individual fields of
@@ -423,8 +494,19 @@
kImm12Bits = 12,
kImm12ShiftShift = 22,
kImm12ShiftBits = 2,
+ kImm14Shift = 5,
+ kImm14Bits = 14,
kImm16Shift = 5,
kImm16Bits = 16,
+ kImm16Mask = 0xffff << kImm16Shift,
+ kImm19Shift = 5,
+ kImm19Bits = 19,
+ kImm19Mask = 0x7ffff << kImm19Shift,
+ kImm26Shift = 0,
+ kImm26Bits = 26,
+
+ kCondShift = 0,
+ kCondBits = 4,
// Bitfield immediates.
kNShift = 22,
@@ -455,8 +537,8 @@
const uint32_t kImmExceptionIsRedirectedCall = 0xca11;
const uint32_t kImmExceptionIsUnreachable = 0xdebf;
-const uint32_t kImmExceptionIsPrintf = 0xdeb1;
const uint32_t kImmExceptionIsDebug = 0xdeb0;
+const uint32_t kImmExceptionIsPrintf = 0xdeb1;
// Helper functions for decoding logical immediates.
static inline uint64_t RotateRight(
@@ -502,6 +584,18 @@
static const int32_t kBreakPointInstruction = // hlt #kImmExceptionIsDebug.
HLT | (kImmExceptionIsDebug << kImm16Shift);
static const int kBreakPointInstructionSize = kInstrSize;
+ static const int32_t kRedirectInstruction =
+ HLT | (kImmExceptionIsRedirectedCall << kImm16Shift);
+
+ // Read one particular bit out of the instruction bits.
+ inline int Bit(int nr) const {
+ return (InstructionBits() >> nr) & 1;
+ }
+
+ // Read a bit field out of the instruction bits.
+ inline int Bits(int shift, int count) const {
+ return (InstructionBits() >> shift) & ((1 << count) - 1);
+ }
// Get the raw instruction bits.
inline int32_t InstructionBits() const {
@@ -513,14 +607,23 @@
*reinterpret_cast<int32_t*>(this) = value;
}
- // Read one particular bit out of the instruction bits.
- inline int Bit(int nr) const {
- return (InstructionBits() >> nr) & 1;
+ inline void SetMoveWideBits(
+ MoveWideOp op, Register rd, uint16_t imm, int hw, OperandSize sz) {
+ ASSERT((hw >= 0) && (hw <= 3));
+ ASSERT((sz == kDoubleWord) || (sz == kWord));
+ const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+ SetInstructionBits(
+ op | size |
+ (static_cast<int32_t>(rd) << kRdShift) |
+ (static_cast<int32_t>(hw) << kHWShift) |
+ (static_cast<int32_t>(imm) << kImm16Shift));
}
- // Read a bit field out of the instruction bits.
- inline int Bits(int shift, int count) const {
- return (InstructionBits() >> shift) & ((1 << count) - 1);
+ inline void SetUnconditionalBranchRegBits(
+ UnconditionalBranchRegOp op, Register rn) {
+ SetInstructionBits(
+ op |
+ (static_cast<int32_t>(rn) << kRnShift));
}
inline int NField() const { return Bit(22); }
@@ -557,6 +660,20 @@
inline int ImmRField() const { return Bits(kImmRShift, kImmRBits); }
inline int ImmSField() const { return Bits(kImmSShift, kImmSBits); }
+ inline int Imm14Field() const { return Bits(kImm14Shift, kImm14Bits); }
+ inline int64_t SImm14Field() const {
+ return (static_cast<int32_t>(Imm14Field()) << 18) >> 18; }
+ inline int Imm19Field() const { return Bits(kImm19Shift, kImm19Bits); }
+ inline int64_t SImm19Field() const {
+ return (static_cast<int32_t>(Imm19Field()) << 13) >> 13; }
+ inline int Imm26Field() const { return Bits(kImm26Shift, kImm26Bits); }
+ inline int64_t SImm26Field() const {
+ return (static_cast<int32_t>(Imm26Field()) << 6) >> 6; }
+
+ inline Condition ConditionField() const {
+ return static_cast<Condition>(Bits(kCondShift, kCondBits));
+ }
+
// Shift and Extend.
inline bool IsShift() const {
return IsLogicalShiftOp() || (Bit(kAddShiftExtendShift) == 0);
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index acc1898..6593d23 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -61,6 +61,7 @@
// Register aliases.
const Register TMP = kNoRegister; // No scratch register used by assembler.
+const Register TMP2 = kNoRegister; // No second assembler scratch register.
const Register CTX = ESI; // Caches current context in generated code.
const Register PP = kNoRegister; // No object pool pointer.
const Register SPREG = ESP; // Stack pointer register.
diff --git a/runtime/vm/constants_mips.h b/runtime/vm/constants_mips.h
index 9aafaa4..4fa1898 100644
--- a/runtime/vm/constants_mips.h
+++ b/runtime/vm/constants_mips.h
@@ -176,6 +176,7 @@
// Register aliases.
const Register TMP = AT; // Used as scratch register by assembler.
+const Register TMP2 = kNoRegister; // No second assembler scratch register.
const Register CTX = S6; // Caches current context in generated code.
const Register PP = S7; // Caches object pool pointer in generated code.
const Register SPREG = SP; // Stack pointer register.
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index 6839b67..f5e76ef 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -85,6 +85,7 @@
// Register aliases.
const Register TMP = R11; // Used as scratch register by the assembler.
+const Register TMP2 = kNoRegister; // No second assembler scratch register.
const Register CTX = R14; // Caches current context in generated code.
// Caches object pool pointer in generated code.
const Register PP = R15;
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index e03377d..3a31cec 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -101,6 +101,7 @@
CodeObservers::InitOnce();
ThreadInterrupter::InitOnce();
Profiler::InitOnce();
+
#if defined(USING_SIMULATOR)
Simulator::InitOnce();
#endif
@@ -249,7 +250,14 @@
if (FLAG_print_class_table) {
isolate->class_table()->Print();
}
+
+ // Setup for profiling.
+ Profiler::InitProfilingForIsolate(isolate);
+
Service::SendIsolateStartupMessage();
+ // Create tag table.
+ isolate->set_tag_table(
+ GrowableObjectArray::Handle(GrowableObjectArray::New()));
return Error::null();
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 7522f73..71c4de0 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -93,6 +93,16 @@
}
+WeakReferenceSetBuilder* ApiState::NewWeakReferenceSetBuilder() {
+ return new WeakReferenceSetBuilder(this);
+}
+
+
+void ApiState::DelayWeakReferenceSet(WeakReferenceSet* reference_set) {
+ WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_);
+}
+
+
Dart_Handle Api::InitNewHandle(Isolate* isolate, RawObject* raw) {
LocalHandles* local_handles = Api::TopScope(isolate)->local_handles();
ASSERT(local_handles != NULL);
@@ -742,35 +752,62 @@
}
-DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(
- Dart_WeakPersistentHandle* keys,
- intptr_t num_keys,
- Dart_WeakPersistentHandle* values,
- intptr_t num_values) {
+DART_EXPORT Dart_WeakReferenceSetBuilder Dart_NewWeakReferenceSetBuilder() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE(isolate);
ApiState* state = isolate->api_state();
ASSERT(state != NULL);
- if (keys == NULL) {
- RETURN_NULL_ERROR(keys);
- }
- if (num_keys <= 0) {
- return Api::NewError(
- "%s expects argument 'num_keys' to be greater than 0.",
- CURRENT_FUNC);
- }
- if (values == NULL) {
- RETURN_NULL_ERROR(values);
- }
- if (num_values <= 0) {
- return Api::NewError(
- "%s expects argument 'num_values' to be greater than 0.",
- CURRENT_FUNC);
- }
+ return reinterpret_cast<Dart_WeakReferenceSetBuilder>(
+ state->NewWeakReferenceSetBuilder());
+}
- WeakReferenceSet* reference_set = new WeakReferenceSet(keys, num_keys,
- values, num_values);
+
+DART_EXPORT Dart_WeakReferenceSet Dart_NewWeakReferenceSet(
+ Dart_WeakReferenceSetBuilder set_builder,
+ Dart_WeakPersistentHandle key,
+ Dart_WeakPersistentHandle value) {
+ ASSERT(set_builder != NULL && key != NULL);
+ WeakReferenceSetBuilder* builder =
+ reinterpret_cast<WeakReferenceSetBuilder*>(set_builder);
+ ApiState* state = builder->api_state();
+ ASSERT(state == Isolate::Current()->api_state());
+ WeakReferenceSet* reference_set = builder->NewWeakReferenceSet();
+ reference_set->AppendKey(key);
+ if (value != NULL) {
+ reference_set->AppendValue(value);
+ }
state->DelayWeakReferenceSet(reference_set);
+ return reinterpret_cast<Dart_WeakReferenceSet>(reference_set);
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle key,
+ Dart_WeakPersistentHandle value) {
+ ASSERT(reference_set != NULL);
+ WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+ set->Append(key, value);
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendKeyToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle key) {
+ ASSERT(reference_set != NULL);
+ WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+ set->AppendKey(key);
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendValueToWeakReferenceSet(
+ Dart_WeakReferenceSet reference_set,
+ Dart_WeakPersistentHandle value) {
+ ASSERT(reference_set != NULL);
+ WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+ set->AppendValue(value);
return Api::Success();
}
@@ -816,6 +853,52 @@
}
+class PrologueWeakVisitor : public HandleVisitor {
+ public:
+ PrologueWeakVisitor(Isolate* isolate,
+ Dart_GcPrologueWeakHandleCallback callback)
+ : HandleVisitor(isolate),
+ callback_(callback) {
+ }
+
+ void VisitHandle(uword addr) {
+ NoGCScope no_gc;
+ FinalizablePersistentHandle* handle =
+ reinterpret_cast<FinalizablePersistentHandle*>(addr);
+ RawObject* raw_obj = handle->raw();
+ if (raw_obj->IsHeapObject()) {
+ ASSERT(handle->IsPrologueWeakPersistent());
+ ReusableInstanceHandleScope reused_instance_handle(isolate());
+ Instance& instance = reused_instance_handle.Handle();
+ instance ^= reinterpret_cast<RawInstance*>(handle->raw());
+ intptr_t num_native_fields = instance.NumNativeFields();
+ intptr_t* native_fields = instance.NativeFieldsDataAddr();
+ if (native_fields != NULL) {
+ callback_(isolate()->init_callback_data(),
+ reinterpret_cast<Dart_WeakPersistentHandle>(addr),
+ num_native_fields,
+ native_fields);
+ }
+ }
+ }
+
+ private:
+ Dart_GcPrologueWeakHandleCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrologueWeakVisitor);
+};
+
+
+DART_EXPORT Dart_Handle Dart_VisitPrologueWeakHandles(
+ Dart_GcPrologueWeakHandleCallback callback) {
+ Isolate* isolate = Isolate::Current();
+ CHECK_ISOLATE(isolate);
+ PrologueWeakVisitor visitor(isolate, callback);
+ isolate->VisitPrologueWeakPersistentHandles(&visitor);
+ return Api::Success();
+}
+
+
// --- Initialization and Globals ---
DART_EXPORT const char* Dart_VersionString() {
@@ -3130,6 +3213,7 @@
if (result.IsError()) {
return Api::NewHandle(isolate, result.raw());
}
+
if (constructor.IsConstructor()) {
ASSERT(result.IsNull());
} else {
@@ -3872,8 +3956,7 @@
intptr_t value) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
- ReusableObjectHandleScope reused_obj_handle(isolate);
- const Instance& instance = Api::UnwrapInstanceHandle(reused_obj_handle, obj);
+ const Instance& instance = Api::UnwrapInstanceHandle(isolate, obj);
if (instance.IsNull()) {
RETURN_TYPE_ERROR(isolate, obj, Instance);
}
@@ -4586,7 +4669,8 @@
DART_EXPORT Dart_Handle Dart_SetNativeResolver(
Dart_Handle library,
- Dart_NativeEntryResolver resolver) {
+ Dart_NativeEntryResolver resolver,
+ Dart_NativeEntrySymbol symbol) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
@@ -4594,6 +4678,7 @@
RETURN_TYPE_ERROR(isolate, library, Library);
}
lib.set_native_entry_resolver(resolver);
+ lib.set_native_entry_symbol_resolver(symbol);
return Api::Success();
}
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index f826f41..fa123f1 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -1389,7 +1389,8 @@
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
- Dart_Handle result = Dart_SetNativeResolver(lib, &ByteDataNativeResolver);
+ Dart_Handle result =
+ Dart_SetNativeResolver(lib, &ByteDataNativeResolver, NULL);
EXPECT_VALID(result);
// Invoke 'main' function.
@@ -1457,7 +1458,8 @@
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle result = Dart_SetNativeResolver(lib,
- &ExternalByteDataNativeResolver);
+ &ExternalByteDataNativeResolver,
+ NULL);
EXPECT_VALID(result);
// Invoke 'main' function.
@@ -2309,7 +2311,7 @@
}
-TEST_CASE(WeakPersistentHandleExternalAllocationSizeOversized) {
+TEST_CASE(WeakPersistentHandleExternalAllocationSizeNewspaceGC) {
Dart_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current());
Heap* heap = Isolate::Current()->heap();
Dart_WeakPersistentHandle weak1 = NULL;
@@ -2350,6 +2352,43 @@
}
+TEST_CASE(WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
+ // Check that external allocation in old space can trigger GC.
+ Isolate* isolate = Isolate::Current();
+ Dart_EnterScope();
+ Dart_Handle live = Api::NewHandle(isolate, String::New("live", Heap::kOld));
+ EXPECT_VALID(live);
+ Dart_WeakPersistentHandle weak = NULL;
+ EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld));
+ const intptr_t kSmallExternalSize = 1 * KB;
+ {
+ Dart_EnterScope();
+ Dart_Handle dead = Api::NewHandle(isolate, String::New("dead", Heap::kOld));
+ EXPECT_VALID(dead);
+ weak = Dart_NewWeakPersistentHandle(dead,
+ NULL,
+ kSmallExternalSize,
+ NopCallback);
+ EXPECT_VALID(AsHandle(weak));
+ Dart_ExitScope();
+ }
+ EXPECT_EQ(kSmallExternalSize,
+ isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+ // Large enough to trigger GC in old space. Not actually allocated.
+ const intptr_t kHugeExternalSize = Heap::kHeapSizeInMB * MB;
+ Dart_NewWeakPersistentHandle(live,
+ NULL,
+ kHugeExternalSize,
+ NopCallback);
+ // Expect small garbage to be collected.
+ EXPECT_EQ(kHugeExternalSize,
+ isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+ Dart_DeleteWeakPersistentHandle(reinterpret_cast<Dart_Isolate>(isolate),
+ weak);
+ Dart_ExitScope();
+}
+
+
TEST_CASE(WeakPersistentHandleExternalAllocationSizeOddReferents) {
Heap* heap = Isolate::Current()->heap();
Dart_WeakPersistentHandle weak1 = NULL;
@@ -2358,13 +2397,13 @@
static const intptr_t kWeak2ExternalSize = 2 * KB;
{
Dart_EnterScope();
- Dart_Handle dart_null = Dart_Null(); // VM heap object.
- EXPECT_VALID(dart_null);
+ Dart_Handle dart_true = Dart_True(); // VM heap object.
+ EXPECT_VALID(dart_true);
weak1 = Dart_NewWeakPersistentHandle(
- dart_null, NULL, kWeak1ExternalSize, NopCallback);
+ dart_true, NULL, kWeak1ExternalSize, NopCallback);
EXPECT_VALID(AsHandle(weak1));
Dart_Handle zero = Dart_NewInteger(0); // Smi.
- EXPECT_VALID(dart_null);
+ EXPECT_VALID(zero);
weak2 = Dart_NewWeakPersistentHandle(
zero, NULL, kWeak2ExternalSize, NopCallback);
EXPECT_VALID(AsHandle(weak2));
@@ -2377,7 +2416,7 @@
Dart_DeleteWeakPersistentHandle(isolate, weak1);
Dart_DeleteWeakPersistentHandle(isolate, weak2);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
- EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
+ EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
}
@@ -2467,21 +2506,24 @@
}
{
- Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
- array1, ARRAY_SIZE(array1)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
- Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
- array2, ARRAY_SIZE(array2)));
+ Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
- Dart_WeakPersistentHandle array3[] = { weak3, weak2 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
- array3, ARRAY_SIZE(array3)));
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
- Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
- array4, ARRAY_SIZE(array4)));
+ set = Dart_NewWeakReferenceSet(builder, weak3, weak3);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak2, weak2));
+
+ set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2499,26 +2541,28 @@
{
Dart_EnterScope();
- Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
- array1, ARRAY_SIZE(array1)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
- Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
- array2, ARRAY_SIZE(array2)));
+ Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
- Dart_WeakPersistentHandle array3[] = { weak2 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
- array3, ARRAY_SIZE(array3)));
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
+
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
// Strong reference to weak3 to retain weak3 and weak4.
Dart_PersistentHandle weak3_strong_ref =
Dart_NewPersistentHandle(AsHandle(weak3));
EXPECT_VALID(AsHandle(weak3_strong_ref));
- Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
- array4, ARRAY_SIZE(array4)));
+ set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
@@ -2539,21 +2583,23 @@
}
{
- Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
- array1, ARRAY_SIZE(array1)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
- Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
- array2, ARRAY_SIZE(array2)));
+ Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
- Dart_WeakPersistentHandle array3[] = { weak2 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
- array3, ARRAY_SIZE(array3)));
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
- Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
- array4, ARRAY_SIZE(array4)));
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
+
+ set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2570,16 +2616,20 @@
}
{
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
+
Dart_WeakPersistentHandle lweak3 = Dart_NewWeakPersistentHandle(
Dart_Null(), NULL, 0, NopCallback);
- Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
- array1, ARRAY_SIZE(array1)));
+
+ Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
// lweak3 is unreferenced so weak2 is unreferenced and should be cleared
- Dart_WeakPersistentHandle array2[] = { weak2, lweak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
- array2, ARRAY_SIZE(array2)));
+ set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak3, lweak3));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2596,6 +2646,9 @@
}
{
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
+
Dart_WeakPersistentHandle lweak2 = Dart_NewWeakPersistentHandle(
Dart_Null(), NULL, 0, NopCallback);
Dart_WeakPersistentHandle lweak3 = Dart_NewWeakPersistentHandle(
@@ -2603,17 +2656,16 @@
Dart_WeakPersistentHandle lweak4 = Dart_NewWeakPersistentHandle(
Dart_Null(), NULL, 0, NopCallback);
// lweak{2,3,4} are cleared and should have no effect on weak1
- Dart_WeakPersistentHandle array1[] = { strong_weak,
- lweak2,
- lweak3,
- lweak4 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
- array1, ARRAY_SIZE(array1)));
+ Dart_WeakReferenceSet set =
+ Dart_NewWeakReferenceSet(builder, strong_weak, strong_weak);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak2, lweak2));
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak3, lweak3));
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak4, lweak4));
// weak1 is weakly reachable and should be cleared
- Dart_WeakPersistentHandle array2[] = { weak1 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
- array2, ARRAY_SIZE(array2)));
+ set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+ EXPECT_NOTNULL(set);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2690,7 +2742,8 @@
// Garbage collect old space without invoking API callbacks.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
- Heap::kIgnoreApiCallbacks);
+ Heap::kIgnoreApiCallbacks,
+ Heap::kGCTestCase);
{
Dart_EnterScope();
@@ -2713,7 +2766,8 @@
}
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
- Heap::kInvokeApiCallbacks);
+ Heap::kInvokeApiCallbacks,
+ Heap::kGCTestCase);
{
Dart_EnterScope();
@@ -2802,10 +2856,15 @@
// A strongly referenced key should preserve all the values.
{
- Dart_WeakPersistentHandle keys[] = { strong_weak };
- Dart_WeakPersistentHandle values[] = { weak1, weak2, weak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
- values, ARRAY_SIZE(values)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
+
+ Dart_WeakReferenceSet set =
+ Dart_NewWeakReferenceSet(builder, strong_weak, 0);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak1));
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak2));
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak3));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2822,10 +2881,13 @@
// Key membership does not imply a strong reference.
{
- Dart_WeakPersistentHandle keys[] = { strong_weak, weak3 };
- Dart_WeakPersistentHandle values[] = { weak1, weak2 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
- values, ARRAY_SIZE(values)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
+
+ Dart_WeakReferenceSet set =
+ Dart_NewWeakReferenceSet(builder, strong_weak, weak1);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak2));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
@@ -2904,10 +2966,15 @@
// A strongly referenced key should preserve all the values.
{
- Dart_WeakPersistentHandle keys[] = { strong_weak };
- Dart_WeakPersistentHandle values[] = { weak1, weak2, weak3 };
- EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
- values, ARRAY_SIZE(values)));
+ Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+ EXPECT_NOTNULL(builder);
+
+ Dart_WeakReferenceSet set =
+ Dart_NewWeakReferenceSet(builder, strong_weak, 0);
+ EXPECT_NOTNULL(set);
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak1));
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak2));
+ EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak3));
GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
}
@@ -3020,7 +3087,8 @@
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
- Heap::kIgnoreApiCallbacks);
+ Heap::kIgnoreApiCallbacks,
+ Heap::kGCTestCase);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
@@ -3070,7 +3138,8 @@
// Garbage collect old space again without invoking callbacks.
// Nothing should change.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
- Heap::kIgnoreApiCallbacks);
+ Heap::kIgnoreApiCallbacks,
+ Heap::kGCTestCase);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(28, global_epilogue_callback_status);
@@ -6117,7 +6186,7 @@
EXPECT(false);
}
}
- result = Dart_SetNativeResolver(result, &PatchNativeResolver);
+ result = Dart_SetNativeResolver(result, &PatchNativeResolver, NULL);
EXPECT_VALID(result);
Dart_Handle script_url = NewString("theScript");
@@ -6230,23 +6299,23 @@
Dart_Handle type = Dart_GetType(lib, NewString("Test"), 0, NULL);
EXPECT_VALID(type);
- result = Dart_SetNativeResolver(Dart_Null(), &MyNativeResolver1);
+ result = Dart_SetNativeResolver(Dart_Null(), &MyNativeResolver1, NULL);
EXPECT(Dart_IsError(result));
EXPECT_STREQ(
"Dart_SetNativeResolver expects argument 'library' to be non-null.",
Dart_GetError(result));
- result = Dart_SetNativeResolver(Dart_True(), &MyNativeResolver1);
+ result = Dart_SetNativeResolver(Dart_True(), &MyNativeResolver1, NULL);
EXPECT(Dart_IsError(result));
EXPECT_STREQ("Dart_SetNativeResolver expects argument 'library' to be of "
"type Library.",
Dart_GetError(result));
- result = Dart_SetNativeResolver(error, &MyNativeResolver1);
+ result = Dart_SetNativeResolver(error, &MyNativeResolver1, NULL);
EXPECT(Dart_IsError(result));
EXPECT_STREQ("incoming error", Dart_GetError(result));
- result = Dart_SetNativeResolver(lib, &MyNativeResolver1);
+ result = Dart_SetNativeResolver(lib, &MyNativeResolver1, NULL);
EXPECT_VALID(result);
// Call a function and make sure native resolution works.
@@ -6258,7 +6327,7 @@
EXPECT_EQ(654321, value);
// A second call succeeds.
- result = Dart_SetNativeResolver(lib, &MyNativeResolver2);
+ result = Dart_SetNativeResolver(lib, &MyNativeResolver2, NULL);
EXPECT_VALID(result);
// 'foo' has already been resolved so gets the old value.
@@ -6278,7 +6347,7 @@
EXPECT_EQ(123456, value);
// A NULL resolver is okay, but resolution will fail.
- result = Dart_SetNativeResolver(lib, NULL);
+ result = Dart_SetNativeResolver(lib, NULL, NULL);
EXPECT_VALID(result);
EXPECT_ERROR(Dart_Invoke(type, NewString("baz"), 0, NULL),
@@ -6701,7 +6770,8 @@
EXPECT_VALID(result);
lib = Dart_LoadScript(url, source, 0, 0);
EXPECT_VALID(lib);
- result = Dart_SetNativeResolver(lib, &IsolateInterruptTestNativeLookup);
+ result =
+ Dart_SetNativeResolver(lib, &IsolateInterruptTestNativeLookup, NULL);
DART_CHECK_VALID(result);
sync->Notify();
@@ -7063,7 +7133,7 @@
Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
EXPECT_VALID(lib);
EXPECT(Dart_IsLibrary(lib));
- result = Dart_SetNativeResolver(lib, &MyNativeClosureResolver);
+ result = Dart_SetNativeResolver(lib, &MyNativeClosureResolver, NULL);
EXPECT_VALID(result);
result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
@@ -7210,7 +7280,7 @@
Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
EXPECT_VALID(lib);
EXPECT(Dart_IsLibrary(lib));
- result = Dart_SetNativeResolver(lib, &MyStaticNativeClosureResolver);
+ result = Dart_SetNativeResolver(lib, &MyStaticNativeClosureResolver, NULL);
EXPECT_VALID(result);
result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
index ad9a2df..5226aef 100644
--- a/runtime/vm/dart_api_state.h
+++ b/runtime/vm/dart_api_state.h
@@ -215,8 +215,6 @@
if (SpaceForExternal() == Heap::kNew) {
SetExternalNewSpaceBit();
}
- // TODO(koda): On repeated/large external allocations for existing objects,
- // without any intervening normal allocation, GC will not trigger.
isolate->heap()->AllocateExternal(external_size(), SpaceForExternal());
}
@@ -229,8 +227,7 @@
// Called when the referent has moved, potentially between generations.
void UpdateRelocated(Isolate* isolate) {
if (IsSetNewSpaceBit() && (SpaceForExternal() == Heap::kOld)) {
- isolate->heap()->FreeExternal(external_size(), Heap::kNew);
- isolate->heap()->AllocateExternal(external_size(), Heap::kOld);
+ isolate->heap()->PromoteExternal(external_size());
ClearExternalNewSpaceBit();
}
}
@@ -561,63 +558,6 @@
};
-class WeakReferenceSet {
- public:
- WeakReferenceSet(Dart_WeakPersistentHandle* keys, intptr_t keys_length,
- Dart_WeakPersistentHandle* values, intptr_t values_length)
- : next_(NULL),
- keys_(keys), num_keys_(keys_length),
- values_(values), num_values_(values_length) {
- }
- ~WeakReferenceSet() {}
-
- WeakReferenceSet* next() const { return next_; }
-
- intptr_t num_keys() const { return num_keys_; }
- RawObject** get_key(intptr_t i) {
- ASSERT(i >= 0);
- ASSERT(i < num_keys_);
- FinalizablePersistentHandle* ref =
- FinalizablePersistentHandle::Cast(keys_[i]);
- return ref->raw_addr();
- }
-
- intptr_t num_values() const { return num_values_; }
- RawObject** get_value(intptr_t i) {
- ASSERT(i >= 0);
- ASSERT(i < num_values_);
- FinalizablePersistentHandle* ref =
- FinalizablePersistentHandle::Cast(values_[i]);
- return ref->raw_addr();
- }
-
- static WeakReferenceSet* Pop(WeakReferenceSet** queue) {
- ASSERT(queue != NULL);
- WeakReferenceSet* head = *queue;
- if (head != NULL) {
- *queue = head->next();
- head->next_ = NULL;
- }
- return head;
- }
-
- static void Push(WeakReferenceSet* reference_set, WeakReferenceSet** queue) {
- ASSERT(reference_set != NULL);
- ASSERT(queue != NULL);
- reference_set->next_ = *queue;
- *queue = reference_set;
- }
-
- private:
- WeakReferenceSet* next_;
- Dart_WeakPersistentHandle* keys_;
- intptr_t num_keys_;
- Dart_WeakPersistentHandle* values_;
- intptr_t num_values_;
- DISALLOW_COPY_AND_ASSIGN(WeakReferenceSet);
-};
-
-
// Structure used for the implementation of local scopes used in dart_api.
// These local scopes manage handles and memory allocated in the scope.
class ApiLocalScope {
@@ -660,6 +600,55 @@
};
+class ApiNativeScope {
+ public:
+ ApiNativeScope() {
+ // Currently no support for nesting native scopes.
+ ASSERT(Current() == NULL);
+ Thread::SetThreadLocal(Api::api_native_key_, reinterpret_cast<uword>(this));
+ }
+
+ ~ApiNativeScope() {
+ ASSERT(Current() == this);
+ Thread::SetThreadLocal(Api::api_native_key_, 0);
+ }
+
+ static inline ApiNativeScope* Current() {
+ return reinterpret_cast<ApiNativeScope*>(
+ Thread::GetThreadLocal(Api::api_native_key_));
+ }
+
+ Zone* zone() { return zone_.GetZone(); }
+
+ private:
+ ApiZone zone_;
+};
+
+
+// Api growable arrays use a zone for allocation. The constructor
+// picks the zone from the current isolate if in an isolate
+// environment. When outside an isolate environment it picks the zone
+// from the current native scope.
+template<typename T>
+class ApiGrowableArray : public BaseGrowableArray<T, ValueObject> {
+ public:
+ explicit ApiGrowableArray(int initial_capacity)
+ : BaseGrowableArray<T, ValueObject>(
+ initial_capacity,
+ ApiNativeScope::Current()->zone()) {}
+ ApiGrowableArray()
+ : BaseGrowableArray<T, ValueObject>(
+ ApiNativeScope::Current()->zone()) {}
+ ApiGrowableArray(intptr_t initial_capacity, Zone* zone)
+ : BaseGrowableArray<T, ValueObject>(initial_capacity, zone) {}
+};
+
+
+// Forward declarations.
+class WeakReferenceSetBuilder;
+class WeakReferenceSet;
+
+
// Implementation of the API State used in dart api for maintaining
// local scopes, persistent handles etc. These are setup on a per isolate
// basis and destroyed when the isolate is shutdown.
@@ -758,6 +747,10 @@
}
}
+ void VisitPrologueWeakHandles(HandleVisitor* visitor) {
+ prologue_weak_persistent_handles().VisitHandles(visitor);
+ }
+
bool IsValidLocalHandle(Dart_Handle object) const {
ApiLocalScope* scope = top_scope_;
while (scope != NULL) {
@@ -822,9 +815,9 @@
return acquired_error_;
}
- void DelayWeakReferenceSet(WeakReferenceSet* reference_set) {
- WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_);
- }
+ WeakReferenceSetBuilder* NewWeakReferenceSetBuilder();
+
+ void DelayWeakReferenceSet(WeakReferenceSet* reference_set);
private:
PersistentHandles persistent_handles_;
@@ -844,45 +837,107 @@
};
-class ApiNativeScope {
+class WeakReferenceSet {
public:
- ApiNativeScope() {
- // Currently no support for nesting native scopes.
- ASSERT(Current() == NULL);
- Thread::SetThreadLocal(Api::api_native_key_, reinterpret_cast<uword>(this));
+ explicit WeakReferenceSet(Zone* zone)
+ : next_(NULL),
+ keys_(1, zone),
+ values_(1, zone) {
+ }
+ ~WeakReferenceSet() {}
+
+ WeakReferenceSet* next() const { return next_; }
+
+ intptr_t num_keys() const { return keys_.length(); }
+ RawObject** get_key(intptr_t i) {
+ ASSERT(i >= 0);
+ ASSERT(i < num_keys());
+ FinalizablePersistentHandle* ref =
+ FinalizablePersistentHandle::Cast(keys_[i]);
+ return ref->raw_addr();
}
- ~ApiNativeScope() {
- ASSERT(Current() == this);
- Thread::SetThreadLocal(Api::api_native_key_, 0);
+ intptr_t num_values() const { return values_.length(); }
+ RawObject** get_value(intptr_t i) {
+ ASSERT(i >= 0);
+ ASSERT(i < num_values());
+ FinalizablePersistentHandle* ref =
+ FinalizablePersistentHandle::Cast(values_[i]);
+ return ref->raw_addr();
}
- static inline ApiNativeScope* Current() {
- return reinterpret_cast<ApiNativeScope*>(
- Thread::GetThreadLocal(Api::api_native_key_));
+ bool SingletonKeyEqualsValue() const {
+ ASSERT((num_keys() == 1) && (num_values() == 1));
+ return (keys_[0] == values_[0]);
}
- Zone* zone() { return zone_.GetZone(); }
+ void Append(Dart_WeakPersistentHandle key, Dart_WeakPersistentHandle value) {
+ keys_.Add(key);
+ values_.Add(value);
+ }
+
+ void AppendKey(Dart_WeakPersistentHandle key) {
+ keys_.Add(key);
+ }
+
+ void AppendValue(Dart_WeakPersistentHandle value) {
+ values_.Add(value);
+ }
+
+ static WeakReferenceSet* Pop(WeakReferenceSet** queue) {
+ ASSERT(queue != NULL);
+ WeakReferenceSet* head = *queue;
+ if (head != NULL) {
+ *queue = head->next();
+ head->next_ = NULL;
+ }
+ return head;
+ }
+
+ static void Push(WeakReferenceSet* reference_set, WeakReferenceSet** queue) {
+ ASSERT(reference_set != NULL);
+ ASSERT(queue != NULL);
+ reference_set->next_ = *queue;
+ *queue = reference_set;
+ }
+
+ void* operator new(uword size, Zone* zone) {
+ return reinterpret_cast<void*>(zone->AllocUnsafe(size));
+ }
+
+ // Disallow explicit deallocation of WeakReferenceSet.
+ void operator delete(void* pointer) { UNREACHABLE(); }
private:
- ApiZone zone_;
+ WeakReferenceSet* next_;
+ ApiGrowableArray<Dart_WeakPersistentHandle> keys_;
+ ApiGrowableArray<Dart_WeakPersistentHandle> values_;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakReferenceSet);
};
-// Api growable arrays use a zone for allocation. The constructor
-// picks the zone from the current isolate if in an isolate
-// environment. When outside an isolate environment it picks the zone
-// from the current native scope.
-template<typename T>
-class ApiGrowableArray : public BaseGrowableArray<T, ValueObject> {
+class WeakReferenceSetBuilder {
public:
- explicit ApiGrowableArray(int initial_capacity)
- : BaseGrowableArray<T, ValueObject>(
- initial_capacity,
- ApiNativeScope::Current()->zone()) {}
- ApiGrowableArray()
- : BaseGrowableArray<T, ValueObject>(
- ApiNativeScope::Current()->zone()) {}
+ ApiState* api_state() const {
+ return api_state_;
+ }
+
+ WeakReferenceSet* NewWeakReferenceSet() {
+ return new (zone_) WeakReferenceSet(zone_);
+ }
+
+ private:
+ explicit WeakReferenceSetBuilder(ApiState* api_state)
+ : api_state_(api_state),
+ zone_(api_state->top_scope()->zone()) {
+ }
+
+ ApiState* api_state_;
+ Zone* zone_;
+
+ friend class ApiState;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WeakReferenceSetBuilder);
};
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 3d487b2..d87143b 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -28,6 +28,8 @@
namespace dart {
DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");
+DEFINE_FLAG(bool, trace_debugger_stacktrace, false,
+ "Trace debugger stacktrace collection");
Debugger::EventHandler* Debugger::event_handler_ = NULL;
@@ -51,10 +53,12 @@
SourceBreakpoint::SourceBreakpoint(intptr_t id,
const Script& script,
- intptr_t token_pos)
+ intptr_t token_pos,
+ intptr_t end_token_pos)
: id_(id),
script_(script.raw()),
token_pos_(token_pos),
+ end_token_pos_(end_token_pos),
is_resolved_(false),
is_enabled_(false),
next_(NULL),
@@ -84,6 +88,7 @@
(token_pos <= func.end_token_pos()));
function_ = func.raw();
token_pos_ = token_pos;
+ end_token_pos_ = token_pos;
line_number_ = -1; // Recalcualte lazily.
is_resolved_ = true;
}
@@ -446,6 +451,10 @@
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
if (var_info.kind == RawLocalVarDescriptors::kSavedEntryContext) {
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tFound saved entry ctx at index %" Pd "\n",
+ var_info.index);
+ }
return GetLocalContextVar(var_info.index);
}
}
@@ -464,6 +473,10 @@
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
if (var_info.kind == RawLocalVarDescriptors::kSavedCurrentContext) {
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tFound saved current ctx at index %" Pd "\n",
+ var_info.index);
+ }
return GetLocalContextVar(var_info.index);
}
}
@@ -625,6 +638,52 @@
}
+void ActivationFrame::PrintContextMismatchError(
+ const String& var_name,
+ intptr_t ctx_slot,
+ intptr_t frame_ctx_level,
+ intptr_t var_ctx_level) {
+ OS::PrintErr("-------------------------\n"
+ "Encountered context mismatch\n"
+ "\tvar name: %s\n"
+ "\tctx_slot: %" Pd "\n"
+ "\tframe_ctx_level: %" Pd "\n"
+ "\tvar_ctx_level: %" Pd "\n\n",
+ var_name.ToCString(),
+ ctx_slot,
+ frame_ctx_level,
+ var_ctx_level);
+
+ OS::PrintErr("-------------------------\n"
+ "Current frame:\n%s\n",
+ this->ToCString());
+
+ OS::PrintErr("-------------------------\n"
+ "Context contents:\n");
+ ctx_.Dump(8);
+
+ OS::PrintErr("-------------------------\n"
+ "Debugger stack trace...\n\n");
+ DebuggerStackTrace* stack =
+ Isolate::Current()->debugger()->StackTrace();
+ intptr_t num_frames = stack->Length();
+ for (intptr_t i = 0; i < num_frames; i++) {
+ ActivationFrame* frame = stack->FrameAt(i);
+ OS::PrintErr("#%04" Pd " %s", i, frame->ToCString());
+ }
+
+ OS::PrintErr("-------------------------\n"
+ "All frames...\n\n");
+ StackFrameIterator iterator(false);
+ StackFrame* frame = iterator.NextFrame();
+ intptr_t num = 0;
+ while ((frame != NULL)) {
+ frame = iterator.NextFrame();
+ OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
+ }
+}
+
+
void ActivationFrame::VariableAt(intptr_t i,
String* name,
intptr_t* token_pos,
@@ -648,6 +707,8 @@
ASSERT(var_info.kind == RawLocalVarDescriptors::kContextVar);
if (ctx_.IsNull()) {
// The context has been removed by the optimizing compiler.
+ //
+ // TODO(turnidge): This may be erroneous. Revisit.
*value = Symbols::OptimizedOut().raw();
return;
}
@@ -660,6 +721,11 @@
intptr_t level_diff = frame_ctx_level - var_ctx_level;
intptr_t ctx_slot = var_info.index;
if (level_diff == 0) {
+ if ((ctx_slot < 0) ||
+ (ctx_slot >= ctx_.num_variables())) {
+ PrintContextMismatchError(*name, ctx_slot,
+ frame_ctx_level, var_ctx_level);
+ }
ASSERT((ctx_slot >= 0) && (ctx_slot < ctx_.num_variables()));
*value = ctx_.At(ctx_slot);
} else {
@@ -669,6 +735,12 @@
level_diff--;
var_ctx = var_ctx.parent();
}
+ if (var_ctx.IsNull() ||
+ (ctx_slot < 0) ||
+ (ctx_slot >= var_ctx.num_variables())) {
+ PrintContextMismatchError(*name, ctx_slot,
+ frame_ctx_level, var_ctx_level);
+ }
ASSERT(!var_ctx.IsNull());
ASSERT((ctx_slot >= 0) && (ctx_slot < var_ctx.num_variables()));
*value = var_ctx.At(ctx_slot);
@@ -694,18 +766,22 @@
const char* ActivationFrame::ToCString() {
- const char* kFormat = "Function: '%s' url: '%s' line: %d";
-
const String& url = String::Handle(SourceUrl());
intptr_t line = LineNumber();
const char* func_name = Debugger::QualifiedFunctionName(function());
-
- intptr_t len =
- OS::SNPrint(NULL, 0, kFormat, func_name, url.ToCString(), line);
- len++; // String terminator.
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
- OS::SNPrint(chars, len, kFormat, func_name, url.ToCString(), line);
- return chars;
+ return Isolate::Current()->current_zone()->
+ PrintToString("[ Frame pc(0x%" Px ") fp(0x%" Px ") sp(0x%" Px ")\n"
+ "\tfunction = %s\n"
+ "\turl = %s\n"
+ "\tline = %" Pd "\n"
+ "\tcontext = %s\n"
+ "\tcontext level = %" Pd " ]\n",
+ pc(), fp(), sp(),
+ func_name,
+ url.ToCString(),
+ line,
+ ctx_.ToCString(),
+ ContextLevel());
}
@@ -1065,21 +1141,42 @@
new ActivationFrame(pc, frame->fp(), frame->sp(), code,
deopt_frame, deopt_frame_offset);
- // Recover the context for this frame.
- if (callee_activation == NULL) {
- // No callee. Use incoming entry context. Could be from
- // isolate's top context or from an entry frame.
- ASSERT(!entry_ctx.IsNull());
- activation->SetContext(entry_ctx);
+ // Is there a closure call at the current PC?
+ //
+ // We can't just check the callee_activation to see if it is a
+ // closure function, because it may not be on the stack yet.
+ bool is_closure_call = false;
+ const PcDescriptors& pc_desc =
+ PcDescriptors::Handle(code.pc_descriptors());
+ ASSERT(code.ContainsInstructionAt(pc));
+ for (int i = 0; i < pc_desc.Length(); i++) {
+ if (pc_desc.PC(i) == pc &&
+ pc_desc.DescriptorKind(i) == PcDescriptors::kClosureCall) {
+ is_closure_call = true;
+ break;
+ }
+ }
- } else if (callee_activation->function().IsClosureFunction()) {
+ // Recover the context for this frame.
+ if (is_closure_call) {
// If the callee is a closure, we should have stored the context
// in the current frame before making the call.
const Context& closure_call_ctx =
Context::Handle(isolate, activation->GetSavedCurrentContext());
ASSERT(!closure_call_ctx.IsNull());
activation->SetContext(closure_call_ctx);
-
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tUsing closure call ctx: %s\n",
+ closure_call_ctx.ToCString());
+ }
+ } else if (callee_activation == NULL) {
+ // No callee available. Use incoming entry context. Could be from
+ // isolate's top context or from an entry frame.
+ ASSERT(!entry_ctx.IsNull());
+ activation->SetContext(entry_ctx);
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tUsing entry ctx: %s\n", entry_ctx.ToCString());
+ }
} else {
// Use the context provided by our callee. This is either the
// callee's context or a context that was saved in the callee's
@@ -1090,6 +1187,13 @@
const Context& callee_ctx =
Context::Handle(isolate, callee_activation->GetSavedEntryContext());
activation->SetContext(callee_ctx);
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tUsing callee call ctx: %s\n",
+ callee_ctx.ToCString());
+ }
+ }
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tLine number: %" Pd "\n", activation->LineNumber());
}
return activation;
}
@@ -1132,9 +1236,17 @@
frame != NULL;
frame = iterator.NextFrame()) {
ASSERT(frame->IsValid());
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
+ frame->ToCString());
+ }
if (frame->IsEntryFrame()) {
current_activation = NULL;
entry_ctx = reinterpret_cast<EntryFrame*>(frame)->SavedContext();
+ if (FLAG_trace_debugger_stacktrace) {
+ OS::PrintErr("\tFound saved ctx in entry frame:\n\t%s\n",
+ entry_ctx.ToCString());
+ }
} else if (frame->IsDartFrame()) {
code = frame->LookupDartCode();
@@ -1144,6 +1256,13 @@
!it.Done();
it.Advance()) {
inlined_code = it.code();
+ if (FLAG_trace_debugger_stacktrace) {
+ const Function& function =
+ Function::Handle(inlined_code.function());
+ ASSERT(!function.IsNull());
+ OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
+ function.ToFullyQualifiedCString());
+ }
intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
current_activation = CollectDartFrame(isolate,
it.pc(),
@@ -1302,14 +1421,22 @@
}
-// Given a function and a token position, return the best fit
+// Given a function and a token range, return the best fit
// token position to set a breakpoint. The best fit is the safe point
-// with the lowest compiled code address that follows the requsted
-// token position.
+// with the lowest compiled code address within the token range.
intptr_t Debugger::ResolveBreakpointPos(const Function& func,
- intptr_t requested_token_pos) {
+ intptr_t requested_token_pos,
+ intptr_t last_token_pos) {
ASSERT(func.HasCode());
ASSERT(!func.HasOptimizedCode());
+
+ if (requested_token_pos < func.token_pos()) {
+ requested_token_pos = func.token_pos();
+ }
+ if (last_token_pos > func.end_token_pos()) {
+ last_token_pos = func.end_token_pos();
+ }
+
Code& code = Code::Handle(func.unoptimized_code());
ASSERT(!code.IsNull());
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
@@ -1317,20 +1444,14 @@
intptr_t best_fit_pos = INT_MAX;
uword lowest_pc = kUwordMax;
intptr_t lowest_pc_index = -1;
+
for (intptr_t i = 0; i < desc.Length(); i++) {
intptr_t desc_token_pos = desc.TokenPos(i);
ASSERT(desc_token_pos >= 0);
if (IsSafePoint(desc, i)) {
- if ((desc_token_pos < func.token_pos()) ||
- (desc_token_pos > func.end_token_pos())) {
- // The position is outside of the function token range. This can
- // happen in constructors, for initializer expressions that are
- // inlined in the field declaration.
- ASSERT(func.IsConstructor());
- continue;
- }
- if (desc_token_pos < requested_token_pos) {
- // This descriptor is before the first acceptable token position.
+ if ((desc_token_pos < requested_token_pos) ||
+ (desc_token_pos > last_token_pos)) {
+ // This descriptor is outside the desired token range.
continue;
}
if (desc_token_pos < best_fit_pos) {
@@ -1542,7 +1663,8 @@
SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
- intptr_t token_pos) {
+ intptr_t token_pos,
+ intptr_t last_token_pos) {
Function& func = Function::Handle(isolate_);
func = FindBestFit(script, token_pos);
if (func.IsNull()) {
@@ -1565,14 +1687,15 @@
// have already been compiled. We can resolve the breakpoint now.
DeoptimizeWorld();
func ^= functions.At(0);
- intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos);
+ intptr_t breakpoint_pos =
+ ResolveBreakpointPos(func, token_pos, last_token_pos);
if (breakpoint_pos >= 0) {
SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos);
if (bpt != NULL) {
// A source breakpoint for this location already exists.
return bpt;
}
- bpt = new SourceBreakpoint(nextId(), script, token_pos);
+ bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos);
bpt->SetResolved(func, breakpoint_pos);
RegisterSourceBreakpoint(bpt);
@@ -1584,6 +1707,14 @@
MakeCodeBreakpointsAt(func, bpt);
}
bpt->Enable();
+ if (FLAG_verbose_debug) {
+ intptr_t line_number;
+ script.GetTokenLocation(breakpoint_pos, &line_number, NULL);
+ OS::Print("Resolved breakpoint for "
+ "function '%s' at line %" Pd "\n",
+ func.ToFullyQualifiedCString(),
+ line_number);
+ }
SignalBpResolved(bpt);
return bpt;
}
@@ -1600,7 +1731,7 @@
}
SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos);
if (bpt == NULL) {
- bpt = new SourceBreakpoint(nextId(), script, token_pos);
+ bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos);
}
RegisterSourceBreakpoint(bpt);
bpt->Enable();
@@ -1639,7 +1770,9 @@
const Function& target_function) {
ASSERT(!target_function.IsNull());
const Script& script = Script::Handle(target_function.script());
- return SetBreakpoint(script, target_function.token_pos());
+ return SetBreakpoint(script,
+ target_function.token_pos(),
+ target_function.end_token_pos());
}
@@ -1684,7 +1817,7 @@
SourceBreakpoint* bpt = NULL;
ASSERT(first_token_idx <= last_token_idx);
while ((bpt == NULL) && (first_token_idx <= last_token_idx)) {
- bpt = SetBreakpoint(script, first_token_idx);
+ bpt = SetBreakpoint(script, first_token_idx, last_token_idx);
first_token_idx++;
}
if ((bpt == NULL) && FLAG_verbose_debug) {
@@ -2150,7 +2283,8 @@
// and set the code breakpoints.
if (!bpt->IsResolved()) {
// Resolve source breakpoint in the newly compiled function.
- intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos());
+ intptr_t bp_pos =
+ ResolveBreakpointPos(func, bpt->token_pos(), bpt->end_token_pos());
if (bp_pos < 0) {
if (FLAG_verbose_debug) {
OS::Print("Failed resolving breakpoint for function '%s'\n",
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 0664bac..65cd192 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -27,10 +27,14 @@
// SourceBreakpoint.
class SourceBreakpoint {
public:
- SourceBreakpoint(intptr_t id, const Script& script, intptr_t token_pos);
+ SourceBreakpoint(intptr_t id,
+ const Script& script,
+ intptr_t token_pos,
+ intptr_t end_token_pos);
RawFunction* function() const { return function_; }
intptr_t token_pos() const { return token_pos_; }
+ intptr_t end_token_pos() const { return end_token_pos_; }
intptr_t id() const { return id_; }
RawScript* script() { return script_; }
@@ -56,6 +60,7 @@
const intptr_t id_;
RawScript* script_;
intptr_t token_pos_;
+ intptr_t end_token_pos_;
bool is_resolved_;
bool is_enabled_;
SourceBreakpoint* next_;
@@ -177,6 +182,11 @@
void PrintToJSONObject(JSONObject* jsobj);
private:
+ void PrintContextMismatchError(const String& var_name,
+ intptr_t ctx_slot,
+ intptr_t frame_ctx_level,
+ intptr_t var_ctx_level);
+
intptr_t PcDescIndex();
intptr_t TryIndex();
void GetPcDescriptors();
@@ -392,12 +402,12 @@
RawFunction* FindInnermostClosure(const Function& function,
intptr_t token_pos);
intptr_t ResolveBreakpointPos(const Function& func,
- intptr_t requested_token_pos);
+ intptr_t requested_token_pos,
+ intptr_t last_token_pos);
void DeoptimizeWorld();
void SetInternalBreakpoints(const Function& target_function);
- SourceBreakpoint* SetBreakpoint(const Script& script, intptr_t token_pos);
- SourceBreakpoint* SetBreakpoint(const Function& target_function,
- intptr_t first_token_pos,
+ SourceBreakpoint* SetBreakpoint(const Script& script,
+ intptr_t token_pos,
intptr_t last_token_pos);
void RemoveInternalBreakpoints();
void UnlinkCodeBreakpoints(SourceBreakpoint* src_bpt);
diff --git a/runtime/vm/debugger_api_impl.cc b/runtime/vm/debugger_api_impl.cc
index 5baae81..4eedf6e 100644
--- a/runtime/vm/debugger_api_impl.cc
+++ b/runtime/vm/debugger_api_impl.cc
@@ -308,6 +308,24 @@
}
+DART_EXPORT Dart_Handle Dart_GetFunctionOrigin(Dart_Handle function_in) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ UNWRAP_AND_CHECK_PARAM(Function, function, function_in);
+
+ Dart_Handle state = Api::CheckIsolateState(isolate);
+ if (::Dart_IsError(state)) {
+ return state;
+ }
+
+ const Class& cls = Class::Handle(function.origin());
+ if (!cls.IsTopLevel()) {
+ return Dart_NewInteger(cls.id());
+ }
+ return Api::Null();
+}
+
+
DART_EXPORT Dart_Handle Dart_GetLocalVariables(
Dart_ActivationFrame activation_frame) {
Isolate* isolate = Isolate::Current();
diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc
index 744306a..6c7fa81 100644
--- a/runtime/vm/debugger_api_impl_test.cc
+++ b/runtime/vm/debugger_api_impl_test.cc
@@ -1393,7 +1393,8 @@
LoadScript(kScriptChars);
Dart_Handle result = Dart_SetNativeResolver(script_lib,
- &InterruptNativeResolver);
+ &InterruptNativeResolver,
+ NULL);
EXPECT_VALID(result);
Dart_Handle retval = Invoke("main");
diff --git a/runtime/vm/disassembler_arm64.cc b/runtime/vm/disassembler_arm64.cc
index 2e453d1..ca2c3e8 100644
--- a/runtime/vm/disassembler_arm64.cc
+++ b/runtime/vm/disassembler_arm64.cc
@@ -34,6 +34,7 @@
void PrintShiftExtendRm(Instr* instr);
void PrintMemOperand(Instr* instr);
void PrintS(Instr* instr);
+ void PrintCondition(Instr* instr);
// Handle formatting of instructions and their options.
int FormatRegister(Instr* instr, const char* option);
@@ -84,8 +85,8 @@
static const char* reg_names[kNumberOfCpuRegisters] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
- "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
- "r24", "ip0", "ip1", "pp", "ctx", "fp", "lr", "r31",
+ "ip0", "ip1", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "pp", "ctx", "fp", "lr", "r31",
};
@@ -115,6 +116,20 @@
};
+// These condition names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* cond_names[kMaxCondition] = {
+ "eq", "ne", "cs" , "cc" , "mi" , "pl" , "vs" , "vc" ,
+ "hi", "ls", "ge", "lt", "gt", "le", "", "invalid",
+};
+
+
+// Print the condition guarding the instruction.
+void ARM64Decoder::PrintCondition(Instr* instr) {
+ Print(cond_names[instr->ConditionField()]);
+}
+
+
// Print the register shift operands for the instruction. Generally used for
// data processing instructions.
void ARM64Decoder::PrintShiftExtendRm(Instr* instr) {
@@ -180,6 +195,18 @@
Print("]");
} else {
switch (instr->Bits(10, 2)) {
+ case 0: {
+ // rn + signed 9-bit immediate, pre-index, no writeback.
+ const int32_t imm9 = instr->SImm9Field();
+ Print("[");
+ PrintRegister(rn, R31IsSP);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ ", #%d",
+ imm9);
+ Print("]");
+ break;
+ }
case 1: {
const int32_t imm9 = instr->SImm9Field();
// rn + signed 9-bit immediate, post-index, writeback.
@@ -192,18 +219,6 @@
imm9);
break;
}
- case 3: {
- const int32_t imm9 = instr->SImm9Field();
- // rn + signed 9-bit immediate, pre-index, writeback.
- Print("[");
- PrintRegister(rn, R31IsSP);
- buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
- remaining_size_in_buffer(),
- ", #%d",
- imm9);
- Print("] !");
- break;
- }
case 2: {
const Register rm = instr->RmField();
const Extend ext = instr->ExtendTypeField();
@@ -222,6 +237,18 @@
Print("]");
break;
}
+ case 3: {
+ const int32_t imm9 = instr->SImm9Field();
+ // rn + signed 9-bit immediate, pre-index, writeback.
+ Print("[");
+ PrintRegister(rn, R31IsSP);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ ", #%d",
+ imm9);
+ Print("] !");
+ break;
+ }
default: {
Print("???");
}
@@ -250,6 +277,10 @@
int reg = instr->RtField();
PrintRegister(reg, R31IsZR);
return 2;
+ } else if (format[1] == 'a') { // 'ra: Ra register
+ int reg = instr->RaField();
+ PrintRegister(reg, R31IsZR);
+ return 2;
}
UNREACHABLE();
return -1;
@@ -263,6 +294,71 @@
// characters that were consumed from the formatting string.
int ARM64Decoder::FormatOption(Instr* instr, const char* format) {
switch (format[0]) {
+ case 'b': {
+ if (format[3] == 'i') {
+ ASSERT(STRING_STARTS_WITH(format, "bitimm"));
+ const uint64_t imm = instr->ImmLogical();
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "0x%"Px64,
+ imm);
+ return 6;
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "bitpos"));
+ int bitpos = instr->Bits(19, 4) | (instr->Bit(31) << 5);
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "#%d",
+ bitpos);
+ return 6;
+ }
+ }
+ case 'c': {
+ ASSERT(STRING_STARTS_WITH(format, "cond"));
+ PrintCondition(instr);
+ return 4;
+ }
+ case 'd': {
+ if (format[4] == '2') {
+ ASSERT(STRING_STARTS_WITH(format, "dest26"));
+ int64_t off = instr->SImm26Field() << 2;
+ uword destination = reinterpret_cast<uword>(instr) + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%#" Px "",
+ destination);
+ } else {
+ if (format[5] == '4') {
+ ASSERT(STRING_STARTS_WITH(format, "dest14"));
+ int64_t off = instr->SImm14Field() << 2;
+ uword destination = reinterpret_cast<uword>(instr) + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%#" Px "",
+ destination);
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "dest19"));
+ int64_t off = instr->SImm19Field() << 2;
+ uword destination = reinterpret_cast<uword>(instr) + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%#" Px "",
+ destination);
+ }
+ }
+ return 6;
+ }
+ case 'h': {
+ ASSERT(STRING_STARTS_WITH(format, "hw"));
+ const int shift = instr->HWField() << 4;
+ if (shift != 0) {
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "lsl %d",
+ shift);
+ }
+ return 2;
+ }
case 'i': { // 'imm12, imm16
uint64_t imm;
int ret = 5;
@@ -288,6 +384,38 @@
imm);
return ret;
}
+ case 'm': {
+ ASSERT(STRING_STARTS_WITH(format, "memop"));
+ PrintMemOperand(instr);
+ return 5;
+ }
+ case 'p': {
+ if (format[2] == 'a') {
+ ASSERT(STRING_STARTS_WITH(format, "pcadr"));
+ const int64_t immhi = instr->SImm19Field();
+ const int64_t immlo = instr->Bits(29, 2);
+ const int64_t off = (immhi << 2) | immlo;
+ const int64_t pc = reinterpret_cast<int64_t>(instr);
+ const int64_t dest = pc + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "0x%"Px64,
+ dest);
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "pcldr"));
+ const int64_t off = instr->SImm19Field() << 2;
+ const int64_t pc = reinterpret_cast<int64_t>(instr);
+ const int64_t dest = pc + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "0x%"Px64,
+ dest);
+ }
+ return 5;
+ }
+ case 'r': {
+ return FormatRegister(instr, format);
+ }
case 's': { // 's: S flag.
if (format[1] == 'h') {
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
@@ -328,34 +456,6 @@
UNREACHABLE();
}
}
- case 'r': {
- return FormatRegister(instr, format);
- }
- case 'h': {
- ASSERT(STRING_STARTS_WITH(format, "hw"));
- const int shift = instr->HWField() << 4;
- if (shift != 0) {
- buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
- remaining_size_in_buffer(),
- "lsl %d",
- shift);
- }
- return 2;
- }
- case 'm': {
- ASSERT(STRING_STARTS_WITH(format, "memop"));
- PrintMemOperand(instr);
- return 5;
- }
- case 'b': {
- ASSERT(STRING_STARTS_WITH(format, "bitimm"));
- const uint64_t imm = instr->ImmLogical();
- buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
- remaining_size_in_buffer(),
- "0x%"Px64,
- imm);
- return 6;
- }
default: {
UNREACHABLE();
break;
@@ -421,13 +521,31 @@
}
+void ARM64Decoder::DecodeLoadRegLiteral(Instr* instr) {
+ if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
+ (instr->Bits(24, 3) != 0)) {
+ Unknown(instr);
+ }
+ if (instr->Bit(30)) {
+ Format(instr, "ldrx 'rt, 'pcldr");
+ } else {
+ Format(instr, "ldrw 'rt, 'pcldr");
+ }
+}
+
+
void ARM64Decoder::DecodeAddSubImm(Instr* instr) {
switch (instr->Bit(30)) {
case 0: {
- if ((instr->RdField() == R31) && (instr->SFField())) {
+ if ((instr->RdField() == R31) && (instr->SField() == 1)) {
Format(instr, "cmni'sf 'rn, 'imm12s");
} else {
- Format(instr, "addi'sf's 'rd, 'rn, 'imm12s");
+ if (((instr->RdField() == R31) || (instr->RnField() == R31)) &&
+ (instr->Imm12Field() == 0) && (instr->Bit(29) == 0)) {
+ Format(instr, "mov'sf 'rd, 'rn");
+ } else {
+ Format(instr, "addi'sf's 'rd, 'rn, 'imm12s");
+ }
}
break;
}
@@ -452,9 +570,14 @@
case 0:
Format(instr, "andi'sf 'rd, 'rn, 'bitimm");
break;
- case 1:
- Format(instr, "orri'sf 'rd, 'rn, 'bitimm");
+ case 1: {
+ if (instr->RnField() == R31) {
+ Format(instr, "mov'sf 'rd, 'bitimm");
+ } else {
+ Format(instr, "orri'sf 'rd, 'rn, 'bitimm");
+ }
break;
+ }
case 2:
Format(instr, "eori'sf 'rd, 'rn, 'bitimm");
break;
@@ -468,6 +591,16 @@
}
+void ARM64Decoder::DecodePCRel(Instr* instr) {
+ const int op = instr->Bit(31);
+ if (op == 0) {
+ Format(instr, "adr 'rd, 'pcadr");
+ } else {
+ Unknown(instr);
+ }
+}
+
+
void ARM64Decoder::DecodeDPImmediate(Instr* instr) {
if (instr->IsMoveWideOp()) {
DecodeMoveWide(instr);
@@ -475,6 +608,8 @@
DecodeAddSubImm(instr);
} else if (instr->IsLogicalImmOp()) {
DecodeLogicalImm(instr);
+ } else if (instr->IsPCRelOp()) {
+ DecodePCRel(instr);
} else {
Unknown(instr);
}
@@ -531,6 +666,44 @@
}
+void ARM64Decoder::DecodeCompareAndBranch(Instr* instr) {
+ const int op = instr->Bit(24);
+ if (op == 0) {
+ Format(instr, "cbz'sf 'rt, 'dest19");
+ } else {
+ Format(instr, "cbnz'sf 'rt, 'dest19");
+ }
+}
+
+
+void ARM64Decoder::DecodeConditionalBranch(Instr* instr) {
+ if ((instr->Bit(24) != 0) || (instr->Bit(4) != 0)) {
+ Unknown(instr);
+ return;
+ }
+ Format(instr, "b'cond 'dest19");
+}
+
+
+void ARM64Decoder::DecodeTestAndBranch(Instr* instr) {
+ const int op = instr->Bit(24);
+ if (op == 0) {
+ Format(instr, "tbz'sf 'rt, 'bitpos, 'dest14");
+ } else {
+ Format(instr, "tbnz'sf 'rt, 'bitpos, 'dest14");
+ }
+}
+
+
+void ARM64Decoder::DecodeUnconditionalBranch(Instr* instr) {
+ const int op = instr->Bit(31);
+ if (op == 0) {
+ Format(instr, "b 'dest26");
+ } else {
+ Format(instr, "bl 'dest26");
+ }
+}
+
void ARM64Decoder::DecodeCompareBranch(Instr* instr) {
if (instr->IsExceptionGenOp()) {
DecodeExceptionGen(instr);
@@ -538,6 +711,14 @@
DecodeSystem(instr);
} else if (instr->IsUnconditionalBranchRegOp()) {
DecodeUnconditionalBranchReg(instr);
+ } else if (instr->IsCompareAndBranchOp()) {
+ DecodeCompareAndBranch(instr);
+ } else if (instr->IsConditionalBranchOp()) {
+ DecodeConditionalBranch(instr);
+ } else if (instr->IsTestAndBranchOp()) {
+ DecodeTestAndBranch(instr);
+ } else if (instr->IsUnconditionalBranchOp()) {
+ DecodeUnconditionalBranch(instr);
} else {
Unknown(instr);
}
@@ -547,6 +728,8 @@
void ARM64Decoder::DecodeLoadStore(Instr* instr) {
if (instr->IsLoadStoreRegOp()) {
DecodeLoadStoreReg(instr);
+ } else if (instr->IsLoadRegLiteralOp()) {
+ DecodeLoadRegLiteral(instr);
} else {
Unknown(instr);
}
@@ -587,9 +770,15 @@
case 1:
Format(instr, "bic'sf 'rd, 'rn, 'shift_op");
break;
- case 2:
- Format(instr, "orr'sf 'rd, 'rn, 'shift_op");
+ case 2: {
+ if ((instr->RnField() == R31) && (instr->IsShift()) &&
+ (instr->Imm16Field() == 0) && (instr->ShiftTypeField() == LSL)) {
+ Format(instr, "mov'sf 'rd, 'rm");
+ } else {
+ Format(instr, "orr'sf 'rd, 'rn, 'shift_op");
+ }
break;
+ }
case 3:
Format(instr, "orn'sf 'rd, 'rn, 'shift_op");
break;
@@ -612,11 +801,58 @@
}
+void ARM64Decoder::DecodeMiscDP2Source(Instr* instr) {
+ if (instr->Bit(29) != 0) {
+ Unknown(instr);
+ }
+
+ const int op = instr->Bits(10, 5);
+ switch (op) {
+ case 2:
+ Format(instr, "udiv'sf 'rd, 'rn, 'rm");
+ break;
+ case 3:
+ Format(instr, "sdiv'sf 'rd, 'rn, 'rm");
+ break;
+ case 8:
+ Format(instr, "lsl'sf 'rd, 'rn, 'rm");
+ break;
+ case 9:
+ Format(instr, "lsr'sf 'rd, 'rn, 'rm");
+ break;
+ case 10:
+ Format(instr, "asr'sf 'rd, 'rn, 'rm");
+ break;
+ default:
+ Unknown(instr);
+ break;
+ }
+}
+
+
+void ARM64Decoder::DecodeMiscDP3Source(Instr* instr) {
+ if ((instr->Bits(29, 2) == 0) && (instr->Bits(21, 3) == 0) &&
+ (instr->Bit(15) == 0)) {
+ if (instr->RaField() == R31) {
+ Format(instr, "mul'sf, 'rd, 'rn, 'rm");
+ } else {
+ Format(instr, "madd'sf 'rd, 'rn, 'rm, 'ra");
+ }
+ } else {
+ Unknown(instr);
+ }
+}
+
+
void ARM64Decoder::DecodeDPRegister(Instr* instr) {
if (instr->IsAddSubShiftExtOp()) {
DecodeAddSubShiftExt(instr);
} else if (instr->IsLogicalShiftOp()) {
DecodeLogicalShift(instr);
+ } else if (instr->IsMiscDP2SourceOp()) {
+ DecodeMiscDP2Source(instr);
+ } else if (instr->IsMiscDP3SourceOp()) {
+ DecodeMiscDP3Source(instr);
} else {
Unknown(instr);
}
@@ -646,9 +882,10 @@
DecodeDPRegister(instr);
} else if (instr->IsDPSimd1Op()) {
DecodeDPSimd1(instr);
- } else {
- ASSERT(instr->IsDPSimd2Op());
+ } else if (instr->IsDPSimd2Op()) {
DecodeDPSimd2(instr);
+ } else {
+ Unknown(instr);
}
}
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index 00e12a47..db6d1e0 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -105,6 +105,9 @@
if (TMP != kNoRegister) {
blocked_cpu_registers_[TMP] = true;
}
+ if (TMP2 != kNoRegister) {
+ blocked_cpu_registers_[TMP2] = true;
+ }
if (PP != kNoRegister) {
blocked_cpu_registers_[PP] = true;
}
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 845c7ff..387f31a 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -953,6 +953,9 @@
if (TMP != kNoRegister) {
blocked_registers[TMP] = true;
}
+ if (TMP2 != kNoRegister) {
+ blocked_registers[TMP2] = true;
+ }
if (PP != kNoRegister) {
blocked_registers[PP] = true;
}
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index bbf06f5..dd4fc01 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -301,7 +301,8 @@
PolymorphicInstanceCallInstr* instance_call =
current->AsPolymorphicInstanceCall();
if (!inline_only_recognized_methods ||
- instance_call->HasSingleRecognizedTarget()) {
+ instance_call->HasSingleRecognizedTarget() ||
+ instance_call->HasSingleDispatcherTarget()) {
instance_calls_.Add(InstanceCallInfo(instance_call, graph));
} else {
// Method not inlined because inlining too deep and method
@@ -473,9 +474,9 @@
inlining_call_sites_ = call_sites_temp;
collected_call_sites_->Clear();
// Inline call sites at the current depth.
+ InlineInstanceCalls();
InlineStaticCalls();
InlineClosureCalls();
- InlineInstanceCalls();
// Increment the inlining depth. Checked before recursive inlining.
++inlining_depth_;
}
@@ -712,9 +713,18 @@
return false;
}
- collected_call_sites_->FindCallSites(callee_graph,
- inlining_depth_,
- &inlined_info_);
+ if (function.IsInvokeFieldDispatcher() ||
+ function.IsNoSuchMethodDispatcher()) {
+ // Append call sites to the currently processed list so that dispatcher
+ // methods get inlined regardless of the current depth.
+ inlining_call_sites_->FindCallSites(callee_graph,
+ 0,
+ &inlined_info_);
+ } else {
+ collected_call_sites_->FindCallSites(callee_graph,
+ inlining_depth_,
+ &inlined_info_);
+ }
// Add the function to the cache.
if (!in_cache) {
diff --git a/runtime/vm/gc_marker.cc b/runtime/vm/gc_marker.cc
index 25640ff..caa53a0 100644
--- a/runtime/vm/gc_marker.cc
+++ b/runtime/vm/gc_marker.cc
@@ -365,16 +365,26 @@
while (queue != NULL) {
WeakReferenceSet* reference_set = WeakReferenceSet::Pop(&queue);
ASSERT(reference_set != NULL);
+ intptr_t num_keys = reference_set->num_keys();
+ intptr_t num_values = reference_set->num_values();
+ if ((num_keys == 1) && (num_values == 1) &&
+ reference_set->SingletonKeyEqualsValue()) {
+ // We do not have to process sets that have just one key/value pair
+ // and the key and value are identical.
+ continue;
+ }
bool is_unreachable = true;
// Test each key object for reachability. If a key object is
// reachable, all value objects should be marked.
- for (intptr_t k = 0; k < reference_set->num_keys(); ++k) {
+ for (intptr_t k = 0; k < num_keys; ++k) {
if (!IsUnreachable(*reference_set->get_key(k))) {
- for (intptr_t v = 0; v < reference_set->num_values(); ++v) {
+ for (intptr_t v = 0; v < num_values; ++v) {
visitor->VisitPointer(reference_set->get_value(v));
}
is_unreachable = false;
- delete reference_set;
+ // Since we have found a key object that is reachable and all
+ // value objects have been marked we can break out of iterating
+ // this set and move on to the next set.
break;
}
}
@@ -389,17 +399,16 @@
DrainMarkingStack(isolate, visitor);
} else {
// Break out of the loop if there has been no forward process.
+ // All key objects in the weak reference sets are unreachable
+ // so we reset the weak reference sets queue.
+ state->set_delayed_weak_reference_sets(NULL);
break;
}
}
- // Deallocate any unmarked references on the delay queue.
- if (state->delayed_weak_reference_sets() != NULL) {
- WeakReferenceSet* queue = state->delayed_weak_reference_sets();
- state->set_delayed_weak_reference_sets(NULL);
- while (queue != NULL) {
- delete WeakReferenceSet::Pop(&queue);
- }
- }
+ ASSERT(state->delayed_weak_reference_sets() == NULL);
+ // All weak reference sets are zone allocated and unmarked references which
+ // were on the delay queue will be freed when the zone is released in the
+ // epilog callback.
}
diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc
index a0743ab..675f98b 100644
--- a/runtime/vm/heap.cc
+++ b/runtime/vm/heap.cc
@@ -104,6 +104,9 @@
} else {
ASSERT(space == kOld);
old_space_->AllocateExternal(size);
+ if (old_space_->NeedsGarbageCollection()) {
+ CollectGarbage(kOld);
+ }
}
}
@@ -116,6 +119,11 @@
}
}
+void Heap::PromoteExternal(intptr_t size) {
+ new_space_->FreeExternal(size);
+ old_space_->AllocateExternal(size);
+}
+
bool Heap::Contains(uword addr) const {
return new_space_->Contains(addr) ||
old_space_->Contains(addr);
@@ -203,31 +211,30 @@
}
-void Heap::CollectGarbage(Space space, ApiCallbacks api_callbacks) {
+void Heap::CollectGarbage(Space space,
+ ApiCallbacks api_callbacks,
+ GCReason reason) {
Isolate* isolate = Isolate::Current();
TIMERSCOPE(isolate, time_gc);
bool invoke_api_callbacks = (api_callbacks == kInvokeApiCallbacks);
switch (space) {
case kNew: {
VMTagScope tagScope(isolate, VMTag::kGCNewSpaceTagId);
- RecordBeforeGC(kNew, kNewSpace);
+ RecordBeforeGC(kNew, reason);
UpdateClassHeapStatsBeforeGC(kNew);
new_space_->Scavenge(invoke_api_callbacks);
RecordAfterGC();
PrintStats();
- // TODO(koda): Replace promotion failure tracking with
- // old_space_->NeedsGarbageCollection.
- if (new_space_->HadPromotionFailure() || old_space_->NeedExternalGC()) {
+ if (old_space_->NeedsGarbageCollection()) {
// Old collections should call the API callbacks.
- CollectGarbage(kOld, kInvokeApiCallbacks);
+ CollectGarbage(kOld, kInvokeApiCallbacks, kPromotion);
}
break;
}
case kOld:
case kCode: {
VMTagScope tagScope(isolate, VMTag::kGCOldSpaceTagId);
- bool promotion_failure = new_space_->HadPromotionFailure();
- RecordBeforeGC(kOld, promotion_failure ? kPromotionFailure : kOldSpace);
+ RecordBeforeGC(kOld, reason);
UpdateClassHeapStatsBeforeGC(kOld);
old_space_->MarkSweep(invoke_api_callbacks);
RecordAfterGC();
@@ -252,13 +259,12 @@
void Heap::CollectGarbage(Space space) {
- ApiCallbacks api_callbacks;
if (space == kOld) {
- api_callbacks = kInvokeApiCallbacks;
+ CollectGarbage(space, kInvokeApiCallbacks, kOldSpace);
} else {
- api_callbacks = kIgnoreApiCallbacks;
+ ASSERT(space == kNew);
+ CollectGarbage(space, kInvokeApiCallbacks, kNewSpace);
}
- CollectGarbage(space, api_callbacks);
}
@@ -408,8 +414,8 @@
switch (gc_reason) {
case kNewSpace:
return "new space";
- case kPromotionFailure:
- return "promotion failure";
+ case kPromotion:
+ return "promotion";
case kOldSpace:
return "old space";
case kFull:
diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h
index 7d40316..0c49d75 100644
--- a/runtime/vm/heap.h
+++ b/runtime/vm/heap.h
@@ -48,7 +48,7 @@
enum GCReason {
kNewSpace,
- kPromotionFailure,
+ kPromotion,
kOldSpace,
kFull,
kGCAtAlloc,
@@ -106,6 +106,8 @@
// Track external data.
void AllocateExternal(intptr_t size, Space space);
void FreeExternal(intptr_t size, Space space);
+ // Move external size from new to old space. Does not by itself trigger GC.
+ void PromoteExternal(intptr_t size);
// Heap contains the specified address.
bool Contains(uword addr) const;
@@ -142,7 +144,7 @@
RawObject* FindObject(FindObjectVisitor* visitor) const;
void CollectGarbage(Space space);
- void CollectGarbage(Space space, ApiCallbacks api_callbacks);
+ void CollectGarbage(Space space, ApiCallbacks api_callbacks, GCReason reason);
void CollectAllGarbage();
// Enables growth control on the page space heaps. This should be
diff --git a/runtime/vm/instructions_arm64.cc b/runtime/vm/instructions_arm64.cc
index 0d1b10a..6617537 100644
--- a/runtime/vm/instructions_arm64.cc
+++ b/runtime/vm/instructions_arm64.cc
@@ -21,13 +21,16 @@
target_address_pool_index_(-1),
args_desc_(Array::Handle()),
ic_data_(ICData::Handle()) {
- UNIMPLEMENTED();
-}
+ ASSERT(code.ContainsInstructionAt(pc));
+ // Last instruction: blr ip0.
+ ASSERT(*(reinterpret_cast<uint32_t*>(end_) - 1) == 0xd63f0200);
-
-int CallPattern::LengthInBytes() {
- UNIMPLEMENTED();
- return 0;
+ Register reg;
+ ic_data_load_end_ =
+ InstructionPattern::DecodeLoadWordFromPool(end_ - Instr::kInstrSize,
+ ®,
+ &target_address_pool_index_);
+ ASSERT(reg == IP0);
}
@@ -40,8 +43,23 @@
const Array& object_pool,
Register* reg,
Object* obj) {
- UNIMPLEMENTED();
- return 0;
+ // 1. LoadWordFromPool
+ // or
+ // 2. LoadDecodableImmediate
+ uword start = 0;
+ Instr* instr = Instr::At(end - Instr::kInstrSize);
+ if (instr->IsLoadStoreRegOp()) {
+ // Case 1.
+ intptr_t index = 0;
+ start = DecodeLoadWordFromPool(end, reg, &index);
+ *obj = object_pool.At(index);
+ } else {
+ // Case 2.
+ intptr_t value = 0;
+ start = DecodeLoadWordImmediate(end, reg, &value);
+ *obj = reinterpret_cast<RawObject*>(value);
+ }
+ return start;
}
@@ -53,8 +71,69 @@
uword InstructionPattern::DecodeLoadWordImmediate(uword end,
Register* reg,
intptr_t* value) {
- UNIMPLEMENTED();
- return 0;
+ // 1. LoadWordFromPool
+ // or
+ // 2. LoadWordFromPool
+ // orri
+ // or
+ // 3. LoadPatchableImmediate
+ uword start = end - Instr::kInstrSize;
+ Instr* instr = Instr::At(start);
+ bool odd = false;
+
+ // Case 2.
+ if (instr->IsLogicalImmOp()) {
+ ASSERT(instr->Bit(29) == 1);
+ odd = true;
+ // end points at orri so that we can pass it to DecodeLoadWordFromPool.
+ end = start;
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ // Case 2 falls through to case 1.
+ }
+
+ // Case 1.
+ if (instr->IsLoadStoreRegOp()) {
+ start = DecodeLoadWordFromPool(end, reg, value);
+ if (odd) {
+ *value |= 1;
+ }
+ return start;
+ }
+
+ // Case 3.
+ // movk dst, imm3, 3; movk dst, imm2, 2; movk dst, imm1, 1; movz dst, imm0, 0
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->Bits(29, 2) == 3);
+ ASSERT(instr->HWField() == 3); // movk dst, imm3, 3
+ *reg = instr->RdField();
+ *value = static_cast<int64_t>(instr->Imm16Field()) << 48;
+
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->Bits(29, 2) == 3);
+ ASSERT(instr->HWField() == 2); // movk dst, imm2, 2
+ ASSERT(instr->RdField() == *reg);
+ *value |= static_cast<int64_t>(instr->Imm16Field()) << 32;
+
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->Bits(29, 2) == 3);
+ ASSERT(instr->HWField() == 1); // movk dst, imm1, 1
+ ASSERT(instr->RdField() == *reg);
+ *value |= static_cast<int64_t>(instr->Imm16Field()) << 16;
+
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->Bits(29, 2) == 2);
+ ASSERT(instr->HWField() == 0); // movz dst, imm0, 0
+ ASSERT(instr->RdField() == *reg);
+ *value |= static_cast<int64_t>(instr->Imm16Field());
+
+ return start;
}
@@ -66,62 +145,187 @@
uword InstructionPattern::DecodeLoadWordFromPool(uword end,
Register* reg,
intptr_t* index) {
- UNIMPLEMENTED();
- return 0;
+ // 1. ldr dst, [pp, offset]
+ // or
+ // 2. movz dst, low_offset, 0
+ // movk dst, hi_offset, 1 (optional)
+ // ldr dst, [pp, dst]
+ uword start = end - Instr::kInstrSize;
+ Instr* instr = Instr::At(start);
+ intptr_t offset = 0;
+
+ // Last instruction is always an ldr into a 64-bit X register.
+ ASSERT(instr->IsLoadStoreRegOp() && (instr->Bit(22) == 1) &&
+ (instr->Bits(30, 2) == 3));
+
+ // Grab the destination register from the ldr instruction.
+ *reg = instr->RtField();
+
+ if (instr->Bit(24) == 1) {
+ // pp + scaled unsigned 12-bit immediate offset.
+ // Case 1.
+ offset = instr->Imm12Field() << 3;
+ } else {
+ ASSERT(instr->Bits(10, 2) == 2);
+ // We have to look at the preceding one or two instructions to find the
+ // offset.
+
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->RdField() == *reg);
+ if (instr->Bits(29, 2) == 2) { // movz dst, low_offset, 0
+ ASSERT(instr->HWField() == 0);
+ offset = instr->Imm16Field();
+ // no high offset.
+ } else {
+ ASSERT(instr->Bits(29, 2) == 3); // movk dst, high_offset, 1
+ ASSERT(instr->HWField() == 1);
+ offset = instr->Imm16Field() << 16;
+
+ start -= Instr::kInstrSize;
+ instr = Instr::At(start);
+ ASSERT(instr->IsMoveWideOp());
+ ASSERT(instr->RdField() == *reg);
+ ASSERT(instr->Bits(29, 2) == 2); // movz dst, low_offset, 0
+ ASSERT(instr->HWField() == 0);
+ offset |= instr->Imm16Field();
+ }
+ }
+ ASSERT(Utils::IsAligned(offset, 8));
+ *index = (offset - Array::data_offset()) / 8;
+ return start;
}
RawICData* CallPattern::IcData() {
- UNIMPLEMENTED();
- return NULL;
+ if (ic_data_.IsNull()) {
+ Register reg;
+ args_desc_load_end_ =
+ InstructionPattern::DecodeLoadObject(ic_data_load_end_,
+ object_pool_,
+ ®,
+ &ic_data_);
+ ASSERT(reg == R5);
+ }
+ return ic_data_.raw();
}
RawArray* CallPattern::ClosureArgumentsDescriptor() {
- UNIMPLEMENTED();
- return NULL;
+ if (args_desc_.IsNull()) {
+ IcData(); // Loading of the ic_data must be decoded first, if not already.
+ Register reg;
+ InstructionPattern::DecodeLoadObject(args_desc_load_end_,
+ object_pool_,
+ ®,
+ &args_desc_);
+ ASSERT(reg == R4);
+ }
+ return args_desc_.raw();
}
uword CallPattern::TargetAddress() const {
- UNIMPLEMENTED();
- return 0;
+ ASSERT(target_address_pool_index_ >= 0);
+ const Object& target_address =
+ Object::Handle(object_pool_.At(target_address_pool_index_));
+ ASSERT(target_address.IsSmi());
+ // The address is stored in the object array as a RawSmi.
+ return reinterpret_cast<uword>(target_address.raw());
}
void CallPattern::SetTargetAddress(uword target_address) const {
- UNIMPLEMENTED();
+ ASSERT(Utils::IsAligned(target_address, 4));
+ // The address is stored in the object array as a RawSmi.
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(target_address));
+ object_pool_.SetAt(target_address_pool_index_, smi);
+ // No need to flush the instruction cache, since the code is not modified.
}
void CallPattern::InsertAt(uword pc, uword target_address) {
- UNIMPLEMENTED();
+ Instr* movz0 = Instr::At(pc + (0 * Instr::kInstrSize));
+ Instr* movk1 = Instr::At(pc + (1 * Instr::kInstrSize));
+ Instr* movk2 = Instr::At(pc + (2 * Instr::kInstrSize));
+ Instr* movk3 = Instr::At(pc + (3 * Instr::kInstrSize));
+ Instr* blr = Instr::At(pc + (4 * Instr::kInstrSize));
+ const uint32_t w0 = Utils::Low32Bits(target_address);
+ const uint32_t w1 = Utils::High32Bits(target_address);
+ const uint16_t h0 = Utils::Low16Bits(w0);
+ const uint16_t h1 = Utils::High16Bits(w0);
+ const uint16_t h2 = Utils::Low16Bits(w1);
+ const uint16_t h3 = Utils::High16Bits(w1);
+
+ movz0->SetMoveWideBits(MOVZ, IP0, h0, 0, kDoubleWord);
+ movk1->SetMoveWideBits(MOVK, IP0, h1, 1, kDoubleWord);
+ movk2->SetMoveWideBits(MOVK, IP0, h2, 2, kDoubleWord);
+ movk3->SetMoveWideBits(MOVK, IP0, h3, 3, kDoubleWord);
+ blr->SetUnconditionalBranchRegBits(BLR, IP0);
+
+ ASSERT(kLengthInBytes == 5 * Instr::kInstrSize);
+ CPU::FlushICache(pc, kLengthInBytes);
}
JumpPattern::JumpPattern(uword pc, const Code& code) : pc_(pc) { }
-int JumpPattern::pattern_length_in_bytes() {
- UNIMPLEMENTED();
- return 0;
-}
-
-
bool JumpPattern::IsValid() const {
- UNIMPLEMENTED();
- return false;
+ Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+ Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+ Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+ Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+ Instr* br = Instr::At(pc_ + (4 * Instr::kInstrSize));
+ return (movz0->IsMoveWideOp()) && (movz0->Bits(29, 2) == 2) &&
+ (movk1->IsMoveWideOp()) && (movk1->Bits(29, 2) == 3) &&
+ (movk2->IsMoveWideOp()) && (movk2->Bits(29, 2) == 3) &&
+ (movk3->IsMoveWideOp()) && (movk3->Bits(29, 2) == 3) &&
+ (br->IsUnconditionalBranchRegOp()) && (br->Bits(16, 5) == 31);
}
uword JumpPattern::TargetAddress() const {
- UNIMPLEMENTED();
- return 0;
+ Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+ Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+ Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+ Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+ const uint16_t imm0 = movz0->Imm16Field();
+ const uint16_t imm1 = movk1->Imm16Field();
+ const uint16_t imm2 = movk2->Imm16Field();
+ const uint16_t imm3 = movk3->Imm16Field();
+ const int64_t target =
+ (static_cast<int64_t>(imm0)) |
+ (static_cast<int64_t>(imm1) << 16) |
+ (static_cast<int64_t>(imm2) << 32) |
+ (static_cast<int64_t>(imm3) << 48);
+ return target;
}
void JumpPattern::SetTargetAddress(uword target_address) const {
- UNIMPLEMENTED();
+ Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+ Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+ Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+ Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+ const int32_t movz0_bits = movz0->InstructionBits();
+ const int32_t movk1_bits = movk1->InstructionBits();
+ const int32_t movk2_bits = movk2->InstructionBits();
+ const int32_t movk3_bits = movk3->InstructionBits();
+
+ const uint32_t w0 = Utils::Low32Bits(target_address);
+ const uint32_t w1 = Utils::High32Bits(target_address);
+ const uint16_t h0 = Utils::Low16Bits(w0);
+ const uint16_t h1 = Utils::High16Bits(w0);
+ const uint16_t h2 = Utils::Low16Bits(w1);
+ const uint16_t h3 = Utils::High16Bits(w1);
+
+ movz0->SetInstructionBits((movz0_bits & ~kImm16Mask) | (h0 << kImm16Shift));
+ movk1->SetInstructionBits((movk1_bits & ~kImm16Mask) | (h1 << kImm16Shift));
+ movk2->SetInstructionBits((movk2_bits & ~kImm16Mask) | (h2 << kImm16Shift));
+ movk3->SetInstructionBits((movk3_bits & ~kImm16Mask) | (h3 << kImm16Shift));
+ CPU::FlushICache(pc_, 4 * Instr::kInstrSize);
}
} // namespace dart
diff --git a/runtime/vm/instructions_arm64.h b/runtime/vm/instructions_arm64.h
index 7cda4cd..d25b378 100644
--- a/runtime/vm/instructions_arm64.h
+++ b/runtime/vm/instructions_arm64.h
@@ -59,7 +59,7 @@
// This constant length is only valid for inserted call patterns used for
// lazy deoptimization. Regular call pattern may vary in length.
- static int LengthInBytes();
+ static const int kLengthInBytes = 5 * Instr::kInstrSize;
static void InsertAt(uword pc, uword target_address);
@@ -82,9 +82,11 @@
public:
JumpPattern(uword pc, const Code& code);
- static const int kLengthInBytes = 3 * Instr::kInstrSize;
+ static const int kLengthInBytes = 5 * Instr::kInstrSize;
- static int pattern_length_in_bytes();
+ int pattern_length_in_bytes() const {
+ return kLengthInBytes;
+ }
bool IsValid() const;
uword TargetAddress() const;
diff --git a/runtime/vm/instructions_arm64_test.cc b/runtime/vm/instructions_arm64_test.cc
index 7c2fe8f..fb97900 100644
--- a/runtime/vm/instructions_arm64_test.cc
+++ b/runtime/vm/instructions_arm64_test.cc
@@ -5,6 +5,64 @@
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM64)
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/cpu.h"
+#include "vm/instructions.h"
+#include "vm/stub_code.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+ASSEMBLER_TEST_GENERATE(Call, assembler) {
+ __ BranchLinkPatchable(&StubCode::InvokeDartCodeLabel());
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Call, test) {
+ // The return address, which must be the address of an instruction contained
+ // in the code, points to the Ret instruction above, i.e. one instruction
+ // before the end of the code buffer.
+ CallPattern call(test->entry() + test->code().Size() - Instr::kInstrSize,
+ test->code());
+ EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+ call.TargetAddress());
+}
+
+
+ASSEMBLER_TEST_GENERATE(Jump, assembler) {
+ __ BranchPatchable(&StubCode::InvokeDartCodeLabel());
+ __ BranchPatchable(&StubCode::AllocateArrayLabel());
+}
+
+
+ASSEMBLER_TEST_RUN(Jump, test) {
+ const Code& code = test->code();
+ const Instructions& instrs = Instructions::Handle(code.instructions());
+ bool status =
+ VirtualMemory::Protect(reinterpret_cast<void*>(instrs.EntryPoint()),
+ instrs.size(),
+ VirtualMemory::kReadWrite);
+ EXPECT(status);
+ JumpPattern jump1(test->entry(), test->code());
+ EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+ jump1.TargetAddress());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes(),
+ test->code());
+ EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
+ jump2.TargetAddress());
+ uword target1 = jump1.TargetAddress();
+ uword target2 = jump2.TargetAddress();
+ jump1.SetTargetAddress(target2);
+ jump2.SetTargetAddress(target1);
+ EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
+ jump1.TargetAddress());
+ EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+ jump2.TargetAddress());
+}
+
+} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 993a614..c296af7 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -2160,6 +2160,13 @@
}
+bool PolymorphicInstanceCallInstr::HasSingleDispatcherTarget() const {
+ if (!ic_data().HasOneTarget()) return false;
+ const Function& target = Function::Handle(ic_data().GetTargetAt(0));
+ return target.IsNoSuchMethodDispatcher() || target.IsInvokeFieldDispatcher();
+}
+
+
LocationSummary* StaticCallInstr::MakeLocationSummary(bool optimizing) const {
return MakeCallSummary();
}
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 3d867f6..3bd24ba 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -89,7 +89,7 @@
V(::, cos, MathCos, 1282146521) \
V(::, min, MathMin, 1022567780) \
V(::, max, MathMax, 612058870) \
- V(::, _doublePow, MathDoublePow, 1591032382) \
+ V(::, _doublePow, MathDoublePow, 2125162289) \
V(Float32x4, Float32x4., Float32x4Constructor, 1314950569) \
V(Float32x4, Float32x4.zero, Float32x4Zero, 1432281809) \
V(Float32x4, Float32x4.splat, Float32x4Splat, 1148280442) \
@@ -2980,6 +2980,8 @@
bool HasSingleRecognizedTarget() const;
+ bool HasSingleDispatcherTarget() const;
+
virtual intptr_t CallCount() const { return ic_data().AggregateCount(); }
DECLARE_INSTRUCTION(PolymorphicInstanceCall)
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index b40b3b3..e08297b 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -182,31 +182,53 @@
LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
- const intptr_t kNumInputs = 0;
- const intptr_t kNumTemps = 1;
- LocationSummary* result =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
- result->set_out(0, Location::RegisterLocation(R0));
- result->set_temp(0, Location::RegisterLocation(R4)); // Arg. descriptor.
- return result;
+ return MakeCallSummary();
}
void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- // The arguments to the stub include the closure, as does the arguments
- // descriptor.
- Register temp_reg = locs()->temp(0).reg();
+ // Load closure object (first argument) in R1.
int argument_count = ArgumentCount();
+ __ ldr(R1, Address(SP, (argument_count - 1) * kWordSize));
+
+ // Load arguments descriptor in R4.
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names()));
- __ LoadObject(temp_reg, arguments_descriptor);
- ASSERT(temp_reg == R4);
- compiler->GenerateDartCall(deopt_id(),
- token_pos(),
- &StubCode::CallClosureFunctionLabel(),
- PcDescriptors::kClosureCall,
- locs());
+ __ LoadObject(R4, arguments_descriptor);
+
+ // Load the closure function into R0.
+ __ ldr(R0, FieldAddress(R1, Closure::function_offset()));
+
+ // Load closure context in CTX; note that CTX has already been preserved.
+ __ ldr(CTX, FieldAddress(R1, Closure::context_offset()));
+
+ // R4: Arguments descriptor.
+ // R0: Function.
+ __ ldr(R2, FieldAddress(R0, Function::code_offset()));
+
+ // R2: code.
+ // R5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+ __ LoadImmediate(R5, 0);
+ __ ldr(R2, FieldAddress(R2, Code::instructions_offset()));
+ __ AddImmediate(R2, Instructions::HeaderSize() - kHeapObjectTag);
+ __ blx(R2);
+ compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+ deopt_id(),
+ token_pos());
+ compiler->RecordSafepoint(locs());
+ // Marks either the continuation point in unoptimized code or the
+ // deoptimization point in optimized code, after call.
+ const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+ if (compiler->is_optimizing()) {
+ compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+ } else {
+ // Add deoptimization continuation point after the call and before the
+ // arguments are removed.
+ compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+ deopt_id_after,
+ token_pos());
+ }
__ Drop(argument_count);
}
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index d6a5573..e1dfcde 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -5825,31 +5825,53 @@
LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
- const intptr_t kNumInputs = 0;
- const intptr_t kNumTemps = 1;
- LocationSummary* result =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
- result->set_out(0, Location::RegisterLocation(EAX));
- result->set_temp(0, Location::RegisterLocation(EDX)); // Arg. descriptor.
- return result;
+ return MakeCallSummary();
}
void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- // The arguments to the stub include the closure, as does the arguments
- // descriptor.
- Register temp_reg = locs()->temp(0).reg();
- int argument_count = ArgumentCount();
+ // Load closure object (first argument) in EDI.
+ intptr_t argument_count = ArgumentCount();
+ __ movl(EDI, Address(ESP, (argument_count - 1) * kWordSize));
+
+ // Load arguments descriptors.
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names()));
- __ LoadObject(temp_reg, arguments_descriptor);
- ASSERT(temp_reg == EDX);
- compiler->GenerateDartCall(deopt_id(),
- token_pos(),
- &StubCode::CallClosureFunctionLabel(),
- PcDescriptors::kClosureCall,
- locs());
+ __ LoadObject(EDX, arguments_descriptor);
+
+ // Load the closure function into EAX.
+ __ movl(EAX, FieldAddress(EDI, Closure::function_offset()));
+
+ // Load closure context in CTX; note that CTX has already been preserved.
+ __ movl(CTX, FieldAddress(EDI, Closure::context_offset()));
+
+ // EBX: Code (compiled code or lazy compile stub).
+ __ movl(EBX, FieldAddress(EAX, Function::code_offset()));
+
+ // EAX: Function.
+ // EDX: Arguments descriptor array.
+ // ECX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+ __ xorl(ECX, ECX);
+ __ movl(EBX, FieldAddress(EBX, Code::instructions_offset()));
+ __ addl(EBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ call(EBX);
+ compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+ deopt_id(),
+ token_pos());
+ compiler->RecordSafepoint(locs());
+ // Marks either the continuation point in unoptimized code or the
+ // deoptimization point in optimized code, after call.
+ const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+ if (compiler->is_optimizing()) {
+ compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+ } else {
+ // Add deoptimization continuation point after the call and before the
+ // arguments are removed.
+ compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+ deopt_id_after,
+ token_pos());
+ }
__ Drop(argument_count);
}
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index ad6a564..7725d2c 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -205,31 +205,51 @@
LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
- const intptr_t kNumInputs = 0;
- const intptr_t kNumTemps = 1;
- LocationSummary* result =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
- result->set_out(0, Location::RegisterLocation(V0));
- result->set_temp(0, Location::RegisterLocation(S4)); // Arg. descriptor.
- return result;
+ return MakeCallSummary();
}
void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- // The arguments to the stub include the closure, as does the arguments
- // descriptor.
- Register temp_reg = locs()->temp(0).reg();
+ // Load closure object (first argument) in T1.
int argument_count = ArgumentCount();
+ __ lw(T1, Address(SP, (argument_count - 1) * kWordSize));
+
+ // Load arguments descriptor in S4.
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names()));
- ASSERT(temp_reg == S4);
- __ LoadObject(temp_reg, arguments_descriptor);
- compiler->GenerateDartCall(deopt_id(),
- token_pos(),
- &StubCode::CallClosureFunctionLabel(),
- PcDescriptors::kClosureCall,
- locs());
+ __ LoadObject(S4, arguments_descriptor);
+
+ // Load the closure function in T0.
+ __ lw(T0, FieldAddress(T1, Closure::function_offset()));
+
+ // Load closure context in CTX; note that CTX has already been preserved.
+ __ lw(CTX, FieldAddress(T1, Closure::context_offset()));
+
+ // Load closure function code in T2.
+ // S4: arguments descriptor array.
+ // S5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+ __ LoadImmediate(S5, 0);
+ __ lw(T2, FieldAddress(T0, Function::code_offset()));
+ __ lw(T2, FieldAddress(T2, Code::instructions_offset()));
+ __ AddImmediate(T2, Instructions::HeaderSize() - kHeapObjectTag);
+ __ jalr(T2);
+ compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+ deopt_id(),
+ token_pos());
+ compiler->RecordSafepoint(locs());
+ // Marks either the continuation point in unoptimized code or the
+ // deoptimization point in optimized code, after call.
+ const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+ if (compiler->is_optimizing()) {
+ compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+ } else {
+ // Add deoptimization continuation point after the call and before the
+ // arguments are removed.
+ compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+ deopt_id_after,
+ token_pos());
+ }
__ Drop(argument_count);
}
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index bbdd1fc..c6c7def 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -5414,31 +5414,53 @@
LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
- const intptr_t kNumInputs = 0;
- const intptr_t kNumTemps = 1;
- LocationSummary* result =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
- result->set_out(0, Location::RegisterLocation(RAX));
- result->set_temp(0, Location::RegisterLocation(R10)); // Arg. descriptor.
- return result;
+ return MakeCallSummary();
}
void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- // The arguments to the stub include the closure, as does the arguments
- // descriptor.
- Register temp_reg = locs()->temp(0).reg();
- int argument_count = ArgumentCount();
+ // Load closure object (first argument) in R13.
+ intptr_t argument_count = ArgumentCount();
+ __ movq(R13, Address(RSP, (argument_count - 1) * kWordSize));
+
+ // Arguments descriptor is expected in R10.
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names()));
- __ LoadObject(temp_reg, arguments_descriptor, PP);
- ASSERT(temp_reg == R10);
- compiler->GenerateDartCall(deopt_id(),
- token_pos(),
- &StubCode::CallClosureFunctionLabel(),
- PcDescriptors::kClosureCall,
- locs());
+ __ LoadObject(R10, arguments_descriptor, PP);
+
+ // Load the actual closure function.
+ __ movq(RAX, FieldAddress(R13, Closure::function_offset()));
+
+ // Load closure context in CTX; note that CTX has already been preserved.
+ __ movq(CTX, FieldAddress(R13, Closure::context_offset()));
+
+ // Load closure function code in RAX.
+ __ movq(RCX, FieldAddress(RAX, Function::code_offset()));
+
+ // RAX: Function.
+ // R10: Arguments descriptor array.
+ // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+ __ xorq(RBX, RBX);
+ __ movq(RCX, FieldAddress(RCX, Code::instructions_offset()));
+ __ addq(RCX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ call(RCX);
+ compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+ deopt_id(),
+ token_pos());
+ compiler->RecordSafepoint(locs());
+ // Marks either the continuation point in unoptimized code or the
+ // deoptimization point in optimized code, after call.
+ const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+ if (compiler->is_optimizing()) {
+ compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+ } else {
+ // Add deoptimization continuation point after the call and before the
+ // arguments are removed.
+ compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+ deopt_id_after,
+ token_pos());
+ }
__ Drop(argument_count);
}
diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc
index 993fe06..6c6f03a9 100644
--- a/runtime/vm/intrinsifier.cc
+++ b/runtime/vm/intrinsifier.cc
@@ -125,6 +125,7 @@
// Set up all core lib functions that can be intrisified.
lib = Library::CoreLibrary();
+ ASSERT(!lib.IsNull());
CORE_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
// Integer intrinsics are in the core library, but we don't want to intrinsify
@@ -135,12 +136,19 @@
// Set up all math lib functions that can be intrisified.
lib = Library::MathLibrary();
+ ASSERT(!lib.IsNull());
MATH_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
// Set up all dart:typed_data lib functions that can be intrisified.
lib = Library::TypedDataLibrary();
+ ASSERT(!lib.IsNull());
TYPED_DATA_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
+ // Setup all dart:profiler lib functions that can be intrinsified.
+ lib = Library::ProfilerLibrary();
+ ASSERT(!lib.IsNull());
+ PROFILER_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
+
#undef SETUP_FUNCTION
}
@@ -172,6 +180,8 @@
TYPED_DATA_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
} else if (lib.raw() == Library::MathLibrary()) {
MATH_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
+ } else if (lib.raw() == Library::ProfilerLibrary()) {
+ PROFILER_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
}
#undef FIND_INTRINSICS
}
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 82f4d11..9f36db3 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -134,6 +134,11 @@
V(_Float64x2Array, ., TypedData_Float64x2Array_factory, 1654170890) \
+#define PROFILER_LIB_INTRINSIC_LIST(V) \
+ V(_UserTag, makeCurrent, UserTag_makeCurrent, 1038211262) \
+ V(::, _getCurrentTag, Profiler_getCurrentTag, 1559793589) \
+ V(::, _clearCurrentTag, Profiler_clearCurrentTag, 874694572) \
+
// TODO(srdjan): Implement _FixedSizeArrayIterator, get:current and
// _FixedSizeArrayIterator, moveNext.
@@ -158,6 +163,7 @@
CORE_INTEGER_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
MATH_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
TYPED_DATA_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
+ PROFILER_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
#undef DECLARE_FUNCTION
};
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 1b29a2b..84ef39b 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -1707,6 +1707,52 @@
}
+// On stack: user tag (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+ // R1: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+ // R0: UserTag.
+ __ ldr(R0, Address(SP, + 0 * kWordSize));
+ // Set Isolate::current_tag_.
+ __ str(R0, Address(R1, Isolate::current_tag_offset()));
+ // R0: UserTag's tag.
+ __ ldr(R0, FieldAddress(R0, UserTag::tag_offset()));
+ // Set Isolate::user_tag_.
+ __ str(R0, Address(R1, Isolate::user_tag_offset()));
+ // Set return value.
+ const int32_t raw_null = reinterpret_cast<int32_t>(Object::null());
+ __ LoadImmediate(R0, raw_null);
+ __ Ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+ // R1: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+ // Set return value to Isolate::current_tag_.
+ __ ldr(R0, Address(R1, Isolate::current_tag_offset()));
+ __ Ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+ // R1: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+ // Set return value to Isolate::current_tag_.
+ __ ldr(R0, Address(R1, Isolate::current_tag_offset()));
+ // Clear Isolate::current_tag_.
+ const int32_t raw_null = reinterpret_cast<int32_t>(UserTag::null());
+ __ LoadImmediate(R2, raw_null);
+ __ str(R2, Address(R1, Isolate::current_tag_offset()));
+ // Clear Isolate::user_tag_.
+ __ eor(R2, R2, ShifterOperand(R2));
+ __ str(R2, Address(R1, Isolate::user_tag_offset()));
+ __ Ret();
+}
+
} // namespace dart
#endif // defined TARGET_ARCH_ARM
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index 6369048..3312c6b 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -403,6 +403,20 @@
}
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+ UNIMPLEMENTED();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+ UNIMPLEMENTED();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+ UNIMPLEMENTED();
+}
+
} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 6b4b5d2..cbf7d00 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -1739,6 +1739,62 @@
StringEquality(assembler, kTwoByteStringCid);
}
+
+// On stack: user tag (+1), return-address (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+ Isolate* isolate = Isolate::Current();
+ const Address current_tag_addr =
+ Address::Absolute(reinterpret_cast<uword>(isolate) +
+ Isolate::current_tag_offset());
+ const Address user_tag_addr =
+ Address::Absolute(reinterpret_cast<uword>(isolate) +
+ Isolate::user_tag_offset());
+ // EAX: UserTag.
+ __ movl(EAX, Address(ESP, + 1 * kWordSize));
+ // Set Isolate::current_tag_.
+ __ movl(current_tag_addr, EAX);
+ // EAX: UserTag's tag.
+ __ movl(EAX, FieldAddress(EAX, UserTag::tag_offset()));
+ // Set Isolate::user_tag_.
+ __ movl(user_tag_addr, EAX);
+ // Set return value.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<int32_t>(Object::null()));
+ __ movl(EAX, raw_null);
+ __ ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+ Isolate* isolate = Isolate::Current();
+ const Address current_tag_addr =
+ Address::Absolute(reinterpret_cast<uword>(isolate) +
+ Isolate::current_tag_offset());
+ // Set return value to Isolate::current_tag_.
+ __ movl(EAX, current_tag_addr);
+ __ ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+ Isolate* isolate = Isolate::Current();
+ const Address current_tag_addr =
+ Address::Absolute(reinterpret_cast<uword>(isolate) +
+ Isolate::current_tag_offset());
+ const Address user_tag_addr =
+ Address::Absolute(reinterpret_cast<uword>(isolate) +
+ Isolate::user_tag_offset());
+ // Set return value to Isolate::current_tag_.
+ __ movl(EAX, current_tag_addr);
+ // Clear Isolate::current_tag_.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<int32_t>(UserTag::null()));
+ __ movl(current_tag_addr, raw_null);
+ // Clear Isolate::user_tag_.
+ __ movl(user_tag_addr, Immediate(0));
+ __ ret();
+}
+
#undef __
} // namespace dart
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 14b3105..677be141 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -1778,6 +1778,50 @@
StringEquality(assembler, kTwoByteStringCid);
}
+// On stack: user tag (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+ // T1: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(T1, reinterpret_cast<uword>(isolate));
+ // V0: UserTag.
+ __ lw(V0, Address(SP, + 0 * kWordSize));
+ // Set Isolate::current_tag_.
+ __ sw(V0, Address(T1, Isolate::current_tag_offset()));
+ // V0: UserTag's tag.
+ __ lw(V0, FieldAddress(V0, UserTag::tag_offset()));
+ // Set Isolate::user_tag_.
+ __ sw(V0, Address(T1, Isolate::user_tag_offset()));
+ // Set return value.
+ __ Ret();
+ __ delay_slot()->LoadImmediate(V0, reinterpret_cast<int32_t>(Object::null()));
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+ // V0: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(V0, reinterpret_cast<uword>(isolate));
+ // Set return value.
+ __ Ret();
+ __ delay_slot()->lw(V0, Address(V0, Isolate::current_tag_offset()));
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+ // T1: Isolate.
+ Isolate* isolate = Isolate::Current();
+ __ LoadImmediate(T1, reinterpret_cast<uword>(isolate));
+ // Set return value to Isolate::current_tag_.
+ __ lw(V0, Address(T1, Isolate::current_tag_offset()));
+ // Clear Isolate::current_tag_.
+ const int32_t raw_null = reinterpret_cast<int32_t>(UserTag::null());
+ __ LoadImmediate(T0, raw_null);
+ __ sw(T0, Address(T1, Isolate::current_tag_offset()));
+ // Clear Isolate::user_tag_.
+ __ Ret();
+ __ delay_slot()->sw(ZR, Address(T1, Isolate::user_tag_offset()));
+}
+
} // namespace dart
#endif // defined TARGET_ARCH_MIPS
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index b329fb1..0f0d065 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -1647,6 +1647,56 @@
StringEquality(assembler, kTwoByteStringCid);
}
+
+// On stack: user tag (+1), return-address (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+ // RBX: Isolate.
+ Isolate* isolate = Isolate::Current();
+ const Immediate& isolate_address =
+ Immediate(reinterpret_cast<int64_t>(isolate));
+ __ movq(RBX, isolate_address);
+ // RAX: UserTag.
+ __ movq(RAX, Address(RSP, + 1 * kWordSize));
+ // Set Isolate::current_tag_.
+ __ movq(Address(RBX, Isolate::current_tag_offset()), RAX);
+ // RAX: UserTag's tag.
+ __ movq(RAX, FieldAddress(RAX, UserTag::tag_offset()));
+ // Set Isolate::user_tag_.
+ __ movq(Address(RBX, Isolate::user_tag_offset()), RAX);
+ // Set return value.
+ __ LoadObject(RAX, Object::null_object(), PP);
+ __ ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+ // RBX: Isolate.
+ Isolate* isolate = Isolate::Current();
+ const Immediate& isolate_address =
+ Immediate(reinterpret_cast<int64_t>(isolate));
+ __ movq(RBX, isolate_address);
+ // Set return value to Isolate::current_tag_.
+ __ movq(RAX, Address(RBX, Isolate::current_tag_offset()));
+ __ ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+ // RBX: Isolate.
+ Isolate* isolate = Isolate::Current();
+ const Immediate& isolate_address =
+ Immediate(reinterpret_cast<int64_t>(isolate));
+ __ movq(RBX, isolate_address);
+ // Set return value to Isolate::current_tag_.
+ __ movq(RAX, Address(RBX, Isolate::current_tag_offset()));
+ // Clear Isolate::current_tag_.
+ __ LoadObject(RCX, Object::null_object(), PP);
+ __ movq(Address(RBX, Isolate::current_tag_offset()), RCX);
+ // Clear Isolate::user_tag_.
+ __ movq(Address(RBX, Isolate::user_tag_offset()), Immediate(0));
+ __ ret();
+}
+
#undef __
} // namespace dart
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index aa8f2a7..511da89 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -336,11 +336,14 @@
object_id_ring_(NULL),
profiler_data_(NULL),
thread_state_(NULL),
+ tag_table_(GrowableObjectArray::null()),
+ current_tag_(UserTag::null()),
next_(NULL),
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS)
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT)
reusable_handles_() {
set_vm_tag(VMTag::kIdleTagId);
+ set_user_tag(UserTags::kNoUserTag);
}
#undef REUSABLE_HANDLE_SCOPE_INIT
#undef REUSABLE_HANDLE_INITIALIZERS
@@ -408,9 +411,6 @@
Isolate* result = new Isolate();
ASSERT(result != NULL);
- // Setup for profiling.
- Profiler::InitProfilingForIsolate(result);
-
// Add to isolate list.
AddIsolateTolist(result);
@@ -453,7 +453,6 @@
}
}
-
return result;
}
@@ -852,6 +851,12 @@
// Visit the top context which is stored in the isolate.
visitor->VisitPointer(reinterpret_cast<RawObject**>(&top_context_));
+ // Visit the current tag which is stored in the isolate.
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(¤t_tag_));
+
+ // Visit the tag table which is stored in the isolate.
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&tag_table_));
+
// Visit objects in the debugger.
debugger()->VisitObjectPointers(visitor);
@@ -870,6 +875,13 @@
}
+void Isolate::VisitPrologueWeakPersistentHandles(HandleVisitor* visitor) {
+ if (api_state() != NULL) {
+ api_state()->VisitPrologueWeakHandles(visitor);
+ }
+}
+
+
void Isolate::PrintToJSONStream(JSONStream* stream, bool ref) {
JSONObject jsobj(stream);
jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate"));
@@ -902,10 +914,14 @@
}
{
JSONObject jsheap(&jsobj, "heap");
- jsheap.AddProperty("usedNew", heap()->UsedInWords(Heap::kNew));
- jsheap.AddProperty("capacityNew", heap()->CapacityInWords(Heap::kNew));
- jsheap.AddProperty("usedOld", heap()->UsedInWords(Heap::kOld));
- jsheap.AddProperty("capacityOld", heap()->CapacityInWords(Heap::kOld));
+ jsheap.AddProperty("usedNew",
+ heap()->UsedInWords(Heap::kNew) * kWordSize);
+ jsheap.AddProperty("capacityNew",
+ heap()->CapacityInWords(Heap::kNew) * kWordSize);
+ jsheap.AddProperty("usedOld",
+ heap()->UsedInWords(Heap::kOld) * kWordSize);
+ jsheap.AddProperty("capacityOld",
+ heap()->CapacityInWords(Heap::kOld) * kWordSize);
}
// TODO(turnidge): Don't compute a full stack trace every time we
@@ -971,6 +987,24 @@
}
+void Isolate::set_tag_table(const GrowableObjectArray& value) {
+ tag_table_ = value.raw();
+}
+
+
+void Isolate::set_current_tag(const UserTag& tag) {
+ intptr_t user_tag = tag.tag();
+ set_user_tag(static_cast<uword>(user_tag));
+ current_tag_ = tag.raw();
+}
+
+
+void Isolate::clear_current_tag() {
+ set_user_tag(UserTags::kNoUserTag);
+ current_tag_ = UserTag::null();
+}
+
+
void Isolate::VisitIsolates(IsolateVisitor* visitor) {
if (visitor == NULL) {
return;
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 2998fd8..b699552 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -30,6 +30,7 @@
class Error;
class Field;
class Function;
+class GrowableObjectArray;
class HandleScope;
class HandleVisitor;
class Heap;
@@ -42,18 +43,21 @@
class MessageHandler;
class Mutex;
class Object;
+class ObjectIdRing;
class ObjectPointerVisitor;
class ObjectStore;
class RawInstance;
class RawArray;
class RawContext;
class RawDouble;
+class RawGrowableObjectArray;
class RawMint;
class RawObject;
class RawInteger;
class RawError;
class RawFloat32x4;
class RawInt32x4;
+class RawUserTag;
class SampleBuffer;
class Simulator;
class StackResource;
@@ -61,7 +65,7 @@
class StubCode;
class TypeArguments;
class TypeParameter;
-class ObjectIdRing;
+class UserTag;
class IsolateVisitor {
@@ -113,8 +117,9 @@
bool validate_frames);
// Visits weak object pointers.
- void VisitWeakPersistentHandles(HandleVisitor* visit,
+ void VisitWeakPersistentHandles(HandleVisitor* visitor,
bool visit_prologue_weak_persistent_handles);
+ void VisitPrologueWeakPersistentHandles(HandleVisitor* visitor);
StoreBuffer* store_buffer() { return &store_buffer_; }
static intptr_t store_buffer_offset() {
@@ -470,6 +475,23 @@
return &vm_tag_counters_;
}
+ uword user_tag() const {
+ return user_tag_;
+ }
+ static intptr_t user_tag_offset() {
+ return OFFSET_OF(Isolate, user_tag_);
+ }
+ static intptr_t current_tag_offset() {
+ return OFFSET_OF(Isolate, current_tag_);
+ }
+
+ RawGrowableObjectArray* tag_table() const { return tag_table_; }
+ void set_tag_table(const GrowableObjectArray& value);
+
+ RawUserTag* current_tag() const { return current_tag_; }
+ void set_current_tag(const UserTag& tag);
+ void clear_current_tag();
+
#if defined(DEBUG)
#define REUSABLE_HANDLE_SCOPE_ACCESSORS(object) \
void set_reusable_##object##_handle_scope_active(bool value) { \
@@ -499,6 +521,11 @@
void ProfileIdle();
+ void set_user_tag(uword tag) {
+ user_tag_ = tag;
+ }
+
+
template<class T> T* AllocateReusableHandle();
static ThreadLocalKey isolate_key;
@@ -553,6 +580,9 @@
InterruptableThreadState* thread_state_;
VMTagCounters vm_tag_counters_;
+ uword user_tag_;
+ RawGrowableObjectArray* tag_table_;
+ RawUserTag* current_tag_;
// Isolate list next pointer.
Isolate* next_;
diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc
index 525bfea..9e5fa8e 100644
--- a/runtime/vm/native_entry.cc
+++ b/runtime/vm/native_entry.cc
@@ -8,6 +8,7 @@
#include "vm/dart_api_impl.h"
#include "vm/dart_api_state.h"
+#include "vm/object_store.h"
#include "vm/tags.h"
@@ -42,6 +43,37 @@
}
+const uint8_t* NativeEntry::ResolveSymbolInLibrary(const Library& library,
+ uword pc) {
+ if (library.native_entry_symbol_resolver() == 0) {
+ // Cannot reverse lookup native entries.
+ return NULL;
+ }
+ Dart_NativeEntrySymbol symbol_resolver =
+ library.native_entry_symbol_resolver();
+ return symbol_resolver(reinterpret_cast<Dart_NativeFunction>(pc));
+}
+
+
+const uint8_t* NativeEntry::ResolveSymbol(uword pc) {
+ Isolate* isolate = Isolate::Current();
+ const GrowableObjectArray& libs =
+ GrowableObjectArray::Handle(isolate->object_store()->libraries());
+ ASSERT(!libs.IsNull());
+ intptr_t num_libs = libs.Length();
+ Library& lib = Library::Handle();
+ for (intptr_t i = 0; i < num_libs; i++) {
+ lib ^= libs.At(i);
+ ASSERT(!lib.IsNull());
+ const uint8_t* r = ResolveSymbolInLibrary(lib, pc);
+ if (r != NULL) {
+ return r;
+ }
+ }
+ return NULL;
+}
+
+
const ExternalLabel& NativeEntry::NativeCallWrapperLabel() {
return native_call_label;
}
diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h
index 6c98d3a..53fbb03 100644
--- a/runtime/vm/native_entry.h
+++ b/runtime/vm/native_entry.h
@@ -95,6 +95,9 @@
const String& function_name,
int number_of_arguments,
bool* auto_setup_scope);
+ static const uint8_t* ResolveSymbolInLibrary(const Library& library,
+ uword pc);
+ static const uint8_t* ResolveSymbol(uword pc);
static void NativeCallWrapper(Dart_NativeArguments args,
Dart_NativeFunction func);
static const ExternalLabel& NativeCallWrapperLabel();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index f81ec3d..d1ec93c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -9,6 +9,7 @@
#include "vm/assembler.h"
#include "vm/cpu.h"
#include "vm/bigint_operations.h"
+#include "vm/bit_vector.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/code_generator.h"
@@ -1141,6 +1142,25 @@
object_store->set_mirror_reference_class(cls);
RegisterPrivateClass(cls, Symbols::_MirrorReference(), lib);
+ // Pre-register the profiler library so we can place the vm class
+ // UserTag there rather than the core library.
+ lib = Library::LookupLibrary(Symbols::DartProfiler());
+ if (lib.IsNull()) {
+ lib = Library::NewLibraryHelper(Symbols::DartProfiler(), true);
+ lib.Register();
+ isolate->object_store()->set_bootstrap_library(ObjectStore::kProfiler,
+ lib);
+ }
+ ASSERT(!lib.IsNull());
+ ASSERT(lib.raw() == Library::ProfilerLibrary());
+
+ lib = Library::LookupLibrary(Symbols::DartProfiler());
+ ASSERT(!lib.IsNull());
+ cls = Class::New<UserTag>();
+ object_store->set_user_tag_class(cls);
+ RegisterPrivateClass(cls, Symbols::_UserTag(), lib);
+ pending_classes.Add(cls);
+
// Setup some default native field classes which can be extended for
// specifying native fields in dart classes.
Library::InitNativeWrappersLibrary(isolate);
@@ -1439,6 +1459,9 @@
cls = Class::New<MirrorReference>();
object_store->set_mirror_reference_class(cls);
+
+ cls = Class::New<UserTag>();
+ object_store->set_user_tag_class(cls);
}
@@ -8937,6 +8960,7 @@
result.raw_ptr()->exports_ = Object::empty_array().raw();
result.raw_ptr()->loaded_scripts_ = Array::null();
result.set_native_entry_resolver(NULL);
+ result.set_native_entry_symbol_resolver(NULL);
result.raw_ptr()->corelib_imported_ = true;
result.set_debuggable(false);
result.raw_ptr()->load_state_ = RawLibrary::kAllocated;
@@ -9189,6 +9213,11 @@
}
+RawLibrary* Library::ProfilerLibrary() {
+ return Isolate::Current()->object_store()->profiler_library();
+}
+
+
const char* Library::ToCString() const {
const char* kFormat = "Library:'%s'";
const String& name = String::Handle(url());
@@ -10157,6 +10186,9 @@
"%2" Pd " kind=%d scope=0x%04x begin=%" Pd " end=%" Pd " name=%s\n";
for (intptr_t i = 0; i < Length(); i++) {
String& var_name = String::Handle(GetName(i));
+ if (var_name.IsNull()) {
+ var_name = Symbols::Empty().raw();
+ }
RawLocalVarDescriptors::VarInfo info;
GetInfo(i, &info);
len += OS::SNPrint(NULL, 0, kFormat,
@@ -10171,6 +10203,9 @@
intptr_t num_chars = 0;
for (intptr_t i = 0; i < Length(); i++) {
String& var_name = String::Handle(GetName(i));
+ if (var_name.IsNull()) {
+ var_name = Symbols::Empty().raw();
+ }
RawLocalVarDescriptors::VarInfo info;
GetInfo(i, &info);
num_chars += OS::SNPrint((buffer + num_chars),
@@ -11168,25 +11203,52 @@
if (IsNull()) {
return "Context (Null)";
}
+ Zone* zone = Isolate::Current()->current_zone();
const Context& parent_ctx = Context::Handle(parent());
if (parent_ctx.IsNull()) {
- const char* kFormat = "Context num_variables:% " Pd "";
- intptr_t len = OS::SNPrint(NULL, 0, kFormat, num_variables()) + 1;
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
- OS::SNPrint(chars, len, kFormat, num_variables());
- return chars;
+ return zone->PrintToString("Context@%p num_variables:% " Pd "",
+ this->raw(), num_variables());
} else {
const char* parent_str = parent_ctx.ToCString();
- const char* kFormat = "Context num_variables:% " Pd " parent:{ %s }";
- intptr_t len = OS::SNPrint(NULL, 0, kFormat,
- num_variables(), parent_str) + 1;
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
- OS::SNPrint(chars, len, kFormat, num_variables(), parent_str);
- return chars;
+ return zone->PrintToString(
+ "Context@%p num_variables:% " Pd " parent:{ %s }",
+ this->raw(), num_variables(), parent_str);
}
}
+static void IndentN(int count) {
+ for (int i = 0; i < count; i++) {
+ OS::PrintErr(" ");
+ }
+}
+
+
+void Context::Dump(int indent) const {
+ if (IsNull()) {
+ IndentN(indent);
+ OS::PrintErr("Context@null\n");
+ return;
+ }
+
+ IndentN(indent);
+ OS::PrintErr("Context@%p vars(%" Pd ") {\n", this->raw(), num_variables());
+ Object& obj = Object::Handle();
+ for (intptr_t i = 0; i < num_variables(); i++) {
+ IndentN(indent + 2);
+ obj = At(i);
+ OS::PrintErr("[%" Pd "] = %s\n", i, obj.ToCString());
+ }
+
+ const Context& parent_ctx = Context::Handle(parent());
+ if (!parent_ctx.IsNull()) {
+ parent_ctx.Dump(indent + 2);
+ }
+ IndentN(indent);
+ OS::PrintErr("}\n");
+}
+
+
void Context::PrintToJSONStream(JSONStream* stream, bool ref) const {
Object::PrintToJSONStream(stream, ref);
}
@@ -12621,6 +12683,17 @@
}
+intptr_t* Instance::NativeFieldsDataAddr() const {
+ NoGCScope no_gc;
+ RawTypedData* native_fields =
+ reinterpret_cast<RawTypedData*>(*NativeFieldsAddr());
+ if (native_fields == TypedData::null()) {
+ return NULL;
+ }
+ return reinterpret_cast<intptr_t*>(native_fields->ptr()->data_);
+}
+
+
void Instance::SetNativeField(int index, intptr_t value) const {
ASSERT(IsValidNativeIndex(index));
Object& native_fields = Object::Handle(*NativeFieldsAddr());
@@ -18049,14 +18122,16 @@
// Traverse inlined frames.
for (InlinedFunctionsIterator it(code, pc); !it.Done(); it.Advance()) {
function = it.function();
- code = it.code();
- ASSERT(function.raw() == code.function());
- uword pc = it.pc();
- ASSERT(pc != 0);
- ASSERT(code.EntryPoint() <= pc);
- ASSERT(pc < (code.EntryPoint() + code.Size()));
- total_len += PrintOneStacktrace(
- isolate, &frame_strings, pc, function, code, *frame_index);
+ if (function.is_visible() || FLAG_verbose_stacktrace) {
+ code = it.code();
+ ASSERT(function.raw() == code.function());
+ uword pc = it.pc();
+ ASSERT(pc != 0);
+ ASSERT(code.EntryPoint() <= pc);
+ ASSERT(pc < (code.EntryPoint() + code.Size()));
+ total_len += PrintOneStacktrace(
+ isolate, &frame_strings, pc, function, code, *frame_index);
+ }
(*frame_index)++; // To account for inlined frames.
}
} else {
@@ -18266,4 +18341,129 @@
}
+void UserTag::MakeActive() const {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate != NULL);
+ isolate->set_current_tag(*this);
+}
+
+
+void UserTag::ClearActive() {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate != NULL);
+ isolate->clear_current_tag();
+}
+
+
+RawUserTag* UserTag::New(const String& label, Heap::Space space) {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate->object_store()->user_tag_class() != Class::null());
+ ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+ // Canonicalize by name.
+ UserTag& result = UserTag::Handle(FindTagInIsolate(isolate, label));
+ if (!result.IsNull()) {
+ // Tag already exists, return existing instance.
+ return result.raw();
+ }
+ if (TagTableIsFull(isolate)) {
+ const String& error = String::Handle(
+ String::NewFormatted("UserTag instance limit (%" Pd ") reached.",
+ UserTags::kMaxUserTags));
+ const Array& args = Array::Handle(Array::New(1));
+ args.SetAt(0, error);
+ Exceptions::ThrowByType(Exceptions::kUnsupported, args);
+ }
+ // No tag with label exists, create and register with isolate tag table.
+ {
+ RawObject* raw = Object::Allocate(UserTag::kClassId,
+ UserTag::InstanceSize(),
+ space);
+ NoGCScope no_gc;
+ result ^= raw;
+ }
+ result.set_label(label);
+ AddTagToIsolate(isolate, result);
+ return result.raw();
+}
+
+
+RawUserTag* UserTag::FindTagInIsolate(Isolate* isolate, const String& label) {
+ ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+ const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+ isolate, isolate->tag_table());
+ UserTag& other = UserTag::Handle(isolate);
+ String& tag_label = String::Handle(isolate);
+ for (intptr_t i = 0; i < tag_table.Length(); i++) {
+ other ^= tag_table.At(i);
+ ASSERT(!other.IsNull());
+ tag_label ^= other.label();
+ ASSERT(!tag_label.IsNull());
+ if (tag_label.Equals(label)) {
+ return other.raw();
+ }
+ }
+ return UserTag::null();
+}
+
+
+void UserTag::AddTagToIsolate(Isolate* isolate, const UserTag& tag) {
+ ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+ const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+ isolate, isolate->tag_table());
+ ASSERT(!TagTableIsFull(isolate));
+#if defined(DEBUG)
+ // Verify that no existing tag has the same tag id.
+ UserTag& other = UserTag::Handle(isolate);
+ for (intptr_t i = 0; i < tag_table.Length(); i++) {
+ other ^= tag_table.At(i);
+ ASSERT(!other.IsNull());
+ ASSERT(tag.tag() != other.tag());
+ }
+#endif
+ // Generate the UserTag tag id by taking the length of the isolate's
+ // tag table + kUserTagIdOffset.
+ uword tag_id = tag_table.Length() + UserTags::kUserTagIdOffset;
+ ASSERT(tag_id >= UserTags::kUserTagIdOffset);
+ ASSERT(tag_id < (UserTags::kUserTagIdOffset + UserTags::kMaxUserTags));
+ tag.set_tag(tag_id);
+ tag_table.Add(tag);
+}
+
+
+bool UserTag::TagTableIsFull(Isolate* isolate) {
+ ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+ const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+ isolate, isolate->tag_table());
+ ASSERT(tag_table.Length() <= UserTags::kMaxUserTags);
+ return tag_table.Length() == UserTags::kMaxUserTags;
+}
+
+
+RawUserTag* UserTag::FindTagById(uword tag_id) {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+ const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+ isolate, isolate->tag_table());
+ UserTag& tag = UserTag::Handle(isolate);
+ for (intptr_t i = 0; i < tag_table.Length(); i++) {
+ tag ^= tag_table.At(i);
+ if (tag.tag() == tag_id) {
+ return tag.raw();
+ }
+ }
+ return UserTag::null();
+}
+
+
+const char* UserTag::ToCString() const {
+ const String& tag_label = String::Handle(label());
+ return tag_label.ToCString();
+}
+
+
+void UserTag::PrintToJSONStream(JSONStream* stream, bool ref) const {
+ Instance::PrintToJSONStream(stream, ref);
+}
+
+
} // namespace dart
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5ba48aa..38296c7 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -18,6 +18,7 @@
#include "vm/os.h"
#include "vm/raw_object.h"
#include "vm/scanner.h"
+#include "vm/tags.h"
namespace dart {
@@ -690,8 +691,12 @@
raw_ptr()->handle_vtable_ = value;
}
+ static bool is_valid_id(intptr_t value) {
+ return RawObject::ClassIdTag::is_valid(value);
+ }
intptr_t id() const { return raw_ptr()->id_; }
void set_id(intptr_t value) const {
+ ASSERT(is_valid_id(value));
raw_ptr()->id_ = value;
}
@@ -2608,6 +2613,13 @@
void set_native_entry_resolver(Dart_NativeEntryResolver value) const {
raw_ptr()->native_entry_resolver_ = value;
}
+ Dart_NativeEntrySymbol native_entry_symbol_resolver() const {
+ return raw_ptr()->native_entry_symbol_resolver_;
+ }
+ void set_native_entry_symbol_resolver(
+ Dart_NativeEntrySymbol native_symbol_resolver) const {
+ raw_ptr()->native_entry_symbol_resolver_ = native_symbol_resolver;
+ }
RawError* Patch(const Script& script) const;
@@ -2646,6 +2658,7 @@
static RawLibrary* MirrorsLibrary();
static RawLibrary* NativeWrappersLibrary();
static RawLibrary* TypedDataLibrary();
+ static RawLibrary* ProfilerLibrary();
// Eagerly compile all classes and functions in the library.
static RawError* CompileAll();
@@ -3286,6 +3299,9 @@
StorePointer(&raw_ptr()->exception_handlers_, handlers.raw());
}
+ // TODO(turnidge): Consider dropping this function and making
+ // everybody use owner(). Currently this function is misused - even
+ // while generating the snapshot.
RawFunction* function() const {
return reinterpret_cast<RawFunction*>(raw_ptr()->owner_);
}
@@ -3457,6 +3473,8 @@
}
inline void SetAt(intptr_t context_index, const Instance& value) const;
+ void Dump(int indent = 0) const;
+
static const intptr_t kBytesPerElement = kWordSize;
static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
@@ -4015,6 +4033,7 @@
return ((index >= 0) && (index < clazz()->ptr()->num_native_fields_));
}
+ intptr_t* NativeFieldsDataAddr() const;
inline intptr_t GetNativeField(int index) const;
inline void GetNativeFields(uint16_t num_fields,
intptr_t* field_values) const;
@@ -4059,6 +4078,7 @@
RawObject** NativeFieldsAddr() const {
return FieldAddrAtOffset(sizeof(RawObject));
}
+
void SetFieldAtOffset(intptr_t offset, const Object& value) const {
StorePointer(FieldAddrAtOffset(offset), value.raw());
}
@@ -6642,6 +6662,46 @@
};
+class UserTag : public Instance {
+ public:
+ uword tag() const { return raw_ptr()->tag(); }
+ void set_tag(uword t) const {
+ ASSERT(t >= UserTags::kUserTagIdOffset);
+ ASSERT(t < UserTags::kUserTagIdOffset + UserTags::kMaxUserTags);
+ raw_ptr()->tag_ = t;
+ };
+ static intptr_t tag_offset() { return OFFSET_OF(RawUserTag, tag_); }
+
+ RawString* label() const {
+ return raw_ptr()->label_;
+ }
+
+ void MakeActive() const;
+ static void ClearActive();
+
+ static intptr_t InstanceSize() {
+ return RoundedAllocationSize(sizeof(RawUserTag));
+ }
+
+ static RawUserTag* New(const String& label,
+ Heap::Space space = Heap::kOld);
+
+ static bool TagTableIsFull(Isolate* isolate);
+ static RawUserTag* FindTagById(uword tag_id);
+
+ private:
+ static RawUserTag* FindTagInIsolate(Isolate* isolate, const String& label);
+ static void AddTagToIsolate(Isolate* isolate, const UserTag& tag);
+
+ void set_label(const String& tag_label) const {
+ StorePointer(&raw_ptr()->label_, tag_label.raw());
+ }
+
+ FINAL_HEAP_OBJECT_IMPLEMENTATION(UserTag, Instance);
+ friend class Class;
+};
+
+
// Breaking cycles and loops.
RawClass* Object::clazz() const {
uword raw_value = reinterpret_cast<uword>(raw_);
diff --git a/runtime/vm/object_arm64_test.cc b/runtime/vm/object_arm64_test.cc
index 77096ff..33e6df2 100644
--- a/runtime/vm/object_arm64_test.cc
+++ b/runtime/vm/object_arm64_test.cc
@@ -6,6 +6,51 @@
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM64)
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/object.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+// Generate a simple dart code sequence.
+// This is used to test Code and Instruction object creation.
+void GenerateIncrement(Assembler* assembler) {
+ __ movz(R0, 0, 0);
+ __ Push(R0);
+ __ add(R0, R0, Operand(1));
+ __ str(R0, Address(SP));
+ __ ldr(R1, Address(SP));
+ __ add(R1, R1, Operand(1));
+ __ Pop(R0);
+ __ mov(R0, R1);
+ __ ret();
+}
+
+
+// Generate a dart code sequence that embeds a string object in it.
+// This is used to test Embedded String objects in the instructions.
+void GenerateEmbedStringInCode(Assembler* assembler, const char* str) {
+ const String& string_object =
+ String::ZoneHandle(String::New(str, Heap::kOld));
+ __ PushPP(); // Save caller's pool pointer and load a new one here.
+ __ LoadPoolPointer(PP);
+ __ LoadObject(R0, string_object, PP);
+ __ PopPP(); // Restore caller's pool pointer.
+ __ ret();
+}
+
+
+// Generate a dart code sequence that embeds a smi object in it.
+// This is used to test Embedded Smi objects in the instructions.
+void GenerateEmbedSmiInCode(Assembler* assembler, intptr_t value) {
+ const Smi& smi_object = Smi::ZoneHandle(Smi::New(value));
+ const int64_t val = reinterpret_cast<int64_t>(smi_object.raw());
+ __ LoadImmediate(R0, val, kNoRegister);
+ __ ret();
+}
+
+} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index bc9e326..7dcc99f 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -53,6 +53,7 @@
jsregexp_class_(Class::null()),
weak_property_class_(Class::null()),
mirror_reference_class_(Class::null()),
+ user_tag_class_(Class::null()),
symbol_table_(Array::null()),
canonical_type_arguments_(Array::null()),
async_library_(Library::null()),
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 3656235..f18f203 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -30,6 +30,7 @@
kMath,
kMirrors,
kTypedData,
+ kProfiler,
};
~ObjectStore();
@@ -274,6 +275,13 @@
mirror_reference_class_ = value.raw();
}
+ RawClass* user_tag_class() const {
+ return user_tag_class_;
+ }
+ void set_user_tag_class(const Class& value) {
+ user_tag_class_ = value.raw();
+ }
+
RawArray* symbol_table() const { return symbol_table_; }
void set_symbol_table(const Array& value) { symbol_table_ = value.raw(); }
@@ -294,6 +302,7 @@
RawLibrary* math_library() const { return math_library_; }
RawLibrary* mirrors_library() const { return mirrors_library_; }
RawLibrary* typed_data_library() const { return typed_data_library_; }
+ RawLibrary* profiler_library() const { return profiler_library_; }
void set_bootstrap_library(BootstrapLibraryId index, const Library& value) {
switch (index) {
case kAsync:
@@ -323,6 +332,9 @@
case kTypedData:
typed_data_library_ = value.raw();
break;
+ case kProfiler:
+ profiler_library_ = value.raw();
+ break;
default:
UNREACHABLE();
}
@@ -475,6 +487,7 @@
RawClass* jsregexp_class_;
RawClass* weak_property_class_;
RawClass* mirror_reference_class_;
+ RawClass* user_tag_class_;
RawArray* symbol_table_;
RawArray* canonical_type_arguments_;
RawLibrary* async_library_;
@@ -489,6 +502,7 @@
RawLibrary* native_wrappers_library_;
RawLibrary* root_library_;
RawLibrary* typed_data_library_;
+ RawLibrary* profiler_library_;
RawGrowableObjectArray* libraries_;
RawGrowableObjectArray* pending_classes_;
RawGrowableObjectArray* pending_functions_;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 052b904..b6db312 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4,7 +4,6 @@
// TODO(zra): Remove when tests are ready to enable.
#include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
#include "vm/assembler.h"
#include "vm/bigint_operations.h"
@@ -2498,7 +2497,7 @@
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
- static_cast<int32_t>(entry_point), 0, 0, 0, 0));
+ static_cast<intptr_t>(entry_point), 0, 0, 0, 0));
#else
typedef intptr_t (*IncrementCode)();
retval = reinterpret_cast<IncrementCode>(entry_point)();
@@ -2523,7 +2522,7 @@
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
- static_cast<int32_t>(entry_point), 0, 0, 0, 0));
+ static_cast<intptr_t>(entry_point), 0, 0, 0, 0));
#else
typedef intptr_t (*IncrementCode)();
retval = reinterpret_cast<IncrementCode>(entry_point)();
@@ -2551,7 +2550,7 @@
uword retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<uword, int64_t>(Simulator::Current()->Call(
- static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+ static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef uword (*EmbedStringCode)();
retval = reinterpret_cast<EmbedStringCode>(instructions.EntryPoint())();
@@ -2578,7 +2577,7 @@
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
- static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+ static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef intptr_t (*EmbedSmiCode)();
retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
@@ -2600,7 +2599,7 @@
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
- static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+ static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef intptr_t (*EmbedSmiCode)();
retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
@@ -2993,6 +2992,9 @@
}
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
TEST_CASE(StackTraceFormat) {
const char* kScriptChars =
"void baz() {\n"
@@ -3053,6 +3055,7 @@
"#10 main (dart:test-lib:37:24)");
}
+#endif // !defined(TARGET_ARCH_ARM64)
TEST_CASE(WeakProperty_PreserveCrossGen) {
Isolate* isolate = Isolate::Current();
@@ -3461,6 +3464,9 @@
}
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
static RawFunction* GetFunction(const Class& cls, const char* name) {
const Function& result = Function::Handle(cls.LookupDynamicFunction(
String::Handle(String::New(name))));
@@ -3600,6 +3606,8 @@
EXPECT_EQ(func_x.raw(), func_x_from_index.raw());
}
+#endif // !defined(TARGET_ARCH_ARM64)
+
TEST_CASE(FindClosureIndex) {
// Allocate the class first.
@@ -3680,6 +3688,9 @@
}
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
static void PrintMetadata(const char* name, const Object& data) {
if (data.IsError()) {
OS::Print("Error in metadata evaluation for %s: '%s'\n",
@@ -3885,6 +3896,8 @@
EXPECT(!func_b.IsInlineable());
}
+#endif // !defined(TARGET_ARCH_ARM64)
+
TEST_CASE(SpecialClassesHaveEmptyArrays) {
ObjectStore* object_store = Isolate::Current()->object_store();
@@ -3917,6 +3930,9 @@
}
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
TEST_CASE(ToUserCString) {
const char* kScriptChars =
"var simple = 'simple';\n"
@@ -4012,6 +4028,6 @@
heap->IterateObjects(&verifier);
}
-} // namespace dart
-
#endif // !defined(TARGET_ARCH_ARM64)
+
+} // namespace dart
diff --git a/runtime/vm/pages.cc b/runtime/vm/pages.cc
index d3cab91..5ebe9b1 100644
--- a/runtime/vm/pages.cc
+++ b/runtime/vm/pages.cc
@@ -703,6 +703,11 @@
grow_heap_ = Utils::Maximum(capacity_growth_in_pages, heap_growth_rate_);
heap->RecordData(PageSpace::kPageGrowth, capacity_growth_in_pages);
}
+ // Limit shrinkage: allow growth by at least half the pages freed by GC.
+ intptr_t freed_pages =
+ (before.capacity_in_words - after.capacity_in_words) /
+ PageSpace::kPageSizeInWords;
+ grow_heap_ = Utils::Maximum(grow_heap_, freed_pages / 2);
heap->RecordData(PageSpace::kGarbageRatio, collected_garbage_ratio);
heap->RecordData(PageSpace::kGCTimeFraction,
garbage_collection_time_fraction);
diff --git a/runtime/vm/pages.h b/runtime/vm/pages.h
index 07dfac8..220cdaa 100644
--- a/runtime/vm/pages.h
+++ b/runtime/vm/pages.h
@@ -199,7 +199,8 @@
GrowthPolicy growth_policy = kControlGrowth);
bool NeedsGarbageCollection() const {
- return page_space_controller_.NeedsGarbageCollection(usage_);
+ return page_space_controller_.NeedsGarbageCollection(usage_) ||
+ NeedsExternalGC();
}
intptr_t UsedInWords() const { return usage_.used_in_words; }
@@ -242,7 +243,7 @@
return page_space_controller_.is_enabled();
}
- bool NeedExternalGC() {
+ bool NeedsExternalGC() const {
return UsedInWords() + ExternalInWords() > max_capacity_in_words_;
}
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 6645901..73cee00 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -1407,32 +1407,47 @@
no_args);
// Pass arguments 1..n to the closure call.
- ArgumentListNode* closure_args = new ArgumentListNode(token_pos);
+ ArgumentListNode* args = new ArgumentListNode(token_pos);
const Array& names = Array::Handle(Array::New(desc.NamedCount(), Heap::kOld));
// Positional parameters.
intptr_t i = 1;
for (; i < desc.PositionalCount(); ++i) {
- closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
+ args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
}
// Named parameters.
for (; i < desc.Count(); i++) {
- closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
+ args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
intptr_t index = i - desc.PositionalCount();
names.SetAt(index, String::Handle(desc.NameAt(index)));
}
- closure_args->set_names(names);
+ args->set_names(names);
- EnsureSavedCurrentContext();
- ClosureCallNode* closure_call = new ClosureCallNode(token_pos,
- getter_call,
- closure_args);
+ const Class& owner = Class::Handle(func.Owner());
+ ASSERT(!owner.IsNull());
+ AstNode* result = NULL;
+ if (owner.IsSignatureClass() && name.Equals(Symbols::Call())) {
+ EnsureSavedCurrentContext();
+ result = new ClosureCallNode(token_pos, getter_call, args);
+ } else {
+ result = BuildClosureCall(token_pos, getter_call, args);
+ }
- ReturnNode* return_node = new ReturnNode(token_pos, closure_call);
+ ReturnNode* return_node = new ReturnNode(token_pos, result);
current_block_->statements->Add(return_node);
return CloseBlock();
}
+AstNode* Parser::BuildClosureCall(intptr_t token_pos,
+ AstNode* closure,
+ ArgumentListNode* arguments) {
+ return new InstanceCallNode(token_pos,
+ closure,
+ Symbols::Call(),
+ arguments);
+}
+
+
void Parser::SkipBlock() {
ASSERT(CurrentToken() == Token::kLBRACE);
GrowableArray<Token::Kind> token_stack(8);
@@ -1901,7 +1916,7 @@
for (int i = 1; i < arguments->length(); i++) {
closure_arguments->Add(arguments->NodeAt(i));
}
- return new ClosureCallNode(supercall_pos, closure, closure_arguments);
+ return BuildClosureCall(supercall_pos, closure, closure_arguments);
}
if (is_no_such_method) {
arguments = BuildNoSuchMethodArguments(
@@ -6722,7 +6737,7 @@
EnsureSavedCurrentContext();
// Function literal in assert implies a call.
const intptr_t pos = condition->token_pos();
- condition = new ClosureCallNode(pos, condition, new ArgumentListNode(pos));
+ condition = BuildClosureCall(pos, condition, new ArgumentListNode(pos));
} else if (condition->IsConditionalExprNode()) {
ConditionalExprNode* cond_expr = condition->AsConditionalExprNode();
cond_expr->set_true_expr(InsertClosureCallNodes(cond_expr->true_expr()));
@@ -8241,12 +8256,12 @@
false,
Class::ZoneHandle(cls.raw()),
func_name);
- return new ClosureCallNode(call_pos, closure, arguments);
+ return BuildClosureCall(call_pos, closure, arguments);
}
} else {
EnsureSavedCurrentContext();
closure = GenerateStaticFieldLookup(field, call_pos);
- return new ClosureCallNode(call_pos, closure, arguments);
+ return BuildClosureCall(call_pos, closure, arguments);
}
// Could not resolve static method: throw a NoSuchMethodError.
return ThrowNoSuchMethodError(ident_pos,
@@ -8286,7 +8301,7 @@
ASSERT(CurrentToken() == Token::kLPAREN);
EnsureSavedCurrentContext();
ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
- return new ClosureCallNode(call_pos, closure, arguments);
+ return BuildClosureCall(call_pos, closure, arguments);
}
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index e1da304..d013abf 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -658,6 +658,10 @@
void AddEqualityNullCheck();
+ AstNode* BuildClosureCall(intptr_t token_pos,
+ AstNode* closure,
+ ArgumentListNode* arguments);
+
RawInstance* TryCanonicalize(const Instance& instance, intptr_t token_pos);
Isolate* isolate() const { return isolate_; }
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 24d3da9..b52c5b4 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -35,6 +35,8 @@
"Maximum number stack frames walked. Minimum 1. Maximum 255.");
DEFINE_FLAG(bool, profile_verify_stack_walk, false,
"Verify instruction addresses while walking the stack.");
+DEFINE_FLAG(bool, profile_native_stack, true,
+ "Use native stack in profiler.");
bool Profiler::initialized_ = false;
SampleBuffer* Profiler::sample_buffer_ = NULL;
@@ -564,9 +566,20 @@
PrintOverwrittenCode(&obj);
} else if (kind() == kTagCode) {
if (name() == NULL) {
- const char* tag_name = start() == 0 ? "root" : VMTag::TagName(start());
- ASSERT(tag_name != NULL);
- SetName(tag_name);
+ if (UserTags::IsUserTag(start())) {
+ const char* tag_name = UserTags::TagName(start());
+ ASSERT(tag_name != NULL);
+ SetName(tag_name);
+ } else if (VMTag::IsVMTag(start()) ||
+ VMTag::IsRuntimeEntryTag(start()) ||
+ VMTag::IsNativeEntryTag(start())) {
+ const char* tag_name = VMTag::TagName(start());
+ ASSERT(tag_name != NULL);
+ SetName(tag_name);
+ } else {
+ ASSERT(start() == 0);
+ SetName("root");
+ }
}
PrintTagCode(&obj);
} else {
@@ -926,7 +939,14 @@
min_time_ = timestamp;
}
// Make sure VM tag is created.
+ if (VMTag::IsNativeEntryTag(sample->vm_tag())) {
+ CreateTag(VMTag::kNativeTagId);
+ } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) {
+ CreateTag(VMTag::kRuntimeTagId);
+ }
CreateTag(sample->vm_tag());
+ // Make sure user tag is created.
+ CreateUserTag(sample->user_tag());
// Exclusive tick for bottom frame.
Tick(sample->At(0), true, timestamp);
// Inclusive tick for all frames.
@@ -962,6 +982,25 @@
region->set_creation_serial(visited());
}
+ void CreateUserTag(uword tag) {
+ if (tag == 0) {
+ // None set.
+ return;
+ }
+ intptr_t index = tag_code_table_->FindIndex(tag);
+ if (index >= 0) {
+ // Already created.
+ return;
+ }
+ CodeRegion* region = new CodeRegion(CodeRegion::kTagCode,
+ tag,
+ tag + 1,
+ 0);
+ index = tag_code_table_->InsertCodeRegion(region);
+ ASSERT(index >= 0);
+ region->set_creation_serial(visited());
+ }
+
void Tick(uword pc, bool exclusive, int64_t timestamp) {
CodeRegionTable::TickResult r;
intptr_t serial = exclusive ? -1 : visited();
@@ -1101,6 +1140,25 @@
root_->Tick();
CodeRegionTrieNode* current = root_;
if (use_tags()) {
+ intptr_t user_tag_index = FindTagIndex(sample->user_tag());
+ if (user_tag_index >= 0) {
+ current = current->GetChild(user_tag_index);
+ // Give the tag a tick.
+ current->Tick();
+ }
+ if (VMTag::IsNativeEntryTag(sample->vm_tag())) {
+ // Insert a dummy kNativeTagId node.
+ intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId);
+ current = current->GetChild(tag_index);
+ // Give the tag a tick.
+ current->Tick();
+ } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) {
+ // Insert a dummy kRuntimeTagId node.
+ intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId);
+ current = current->GetChild(tag_index);
+ // Give the tag a tick.
+ current->Tick();
+ }
intptr_t tag_index = FindTagIndex(sample->vm_tag());
current = current->GetChild(tag_index);
// Give the tag a tick.
@@ -1131,7 +1189,13 @@
private:
intptr_t FindTagIndex(uword tag) const {
+ if (tag == 0) {
+ return -1;
+ }
intptr_t index = tag_code_table_->FindIndex(tag);
+ if (index <= 0) {
+ return -1;
+ }
ASSERT(index >= 0);
ASSERT((tag_code_table_->At(index))->contains(tag));
return tag_code_table_offset_ + index;
@@ -1406,6 +1470,45 @@
return At(cursor);
}
+class ProfilerDartStackWalker : public ValueObject {
+ public:
+ explicit ProfilerDartStackWalker(Sample* sample)
+ : sample_(sample),
+ frame_iterator_() {
+ ASSERT(sample_ != NULL);
+ }
+
+ ProfilerDartStackWalker(Sample* sample, uword pc, uword fp, uword sp)
+ : sample_(sample),
+ frame_iterator_(fp, sp, pc) {
+ ASSERT(sample_ != NULL);
+ }
+
+ ProfilerDartStackWalker(Sample* sample, uword fp)
+ : sample_(sample),
+ frame_iterator_(fp) {
+ ASSERT(sample_ != NULL);
+ }
+
+ int walk() {
+ intptr_t frame_index = 0;
+ StackFrame* frame = frame_iterator_.NextFrame();
+ while (frame != NULL) {
+ sample_->SetAt(frame_index, frame->pc());
+ frame_index++;
+ if (frame_index >= FLAG_profile_depth) {
+ break;
+ }
+ frame = frame_iterator_.NextFrame();
+ }
+ return frame_index;
+ }
+
+ private:
+ Sample* sample_;
+ DartFrameIterator frame_iterator_;
+};
+
// Notes on stack frame walking:
//
// The sampling profiler will collect up to Sample::kNumStackFrames stack frames
@@ -1414,9 +1517,9 @@
// recent GCC versions with optimizing enabled) the stack walking code may
// fail (sometimes leading to a crash).
//
-class ProfilerSampleStackWalker : public ValueObject {
+class ProfilerNativeStackWalker : public ValueObject {
public:
- ProfilerSampleStackWalker(Sample* sample,
+ ProfilerNativeStackWalker(Sample* sample,
uword stack_lower,
uword stack_upper,
uword pc,
@@ -1431,13 +1534,11 @@
ASSERT(sample_ != NULL);
}
- int walk(Heap* heap, uword vm_tag) {
+ int walk(Heap* heap) {
const intptr_t kMaxStep = 0x1000; // 4K.
const bool kWalkStack = true; // Walk the stack.
// Always store the exclusive PC.
sample_->SetAt(0, original_pc_);
- // Always store the vm tag.
- sample_->set_vm_tag(vm_tag);
if (!kWalkStack) {
// Not walking the stack, only took exclusive sample.
return 1;
@@ -1466,6 +1567,9 @@
VerifyCodeAddress(heap, i, reinterpret_cast<uword>(pc));
}
sample_->SetAt(i, reinterpret_cast<uword>(pc));
+ if (fp == NULL) {
+ return i + 1;
+ }
if (!ValidFramePointer(fp)) {
return i + 1;
}
@@ -1473,9 +1577,18 @@
previous_fp = fp;
fp = CallerFP(fp);
intptr_t step = fp - previous_fp;
- if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) {
+ if (fp == NULL) {
+ return i + 1;
+ }
+ if ((step >= kMaxStep)) {
// Frame pointer step is too large.
+ return i + 1;
+ }
+ if ((fp <= previous_fp)) {
// Frame pointer did not move to a higher address.
+ return i + 1;
+ }
+ if (!ValidFramePointer(fp)) {
// Frame pointer is outside of isolate stack bounds.
return i + 1;
}
@@ -1534,9 +1647,11 @@
const InterruptedThreadState& state,
void* data) {
Isolate* isolate = reinterpret_cast<Isolate*>(data);
- if (isolate == NULL) {
+ if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) {
return;
}
+ ASSERT(isolate != Dart::vm_isolate());
+ ASSERT(isolate->stub_code() != NULL);
VMTagCounters* counters = isolate->vm_tag_counters();
ASSERT(counters != NULL);
counters->Increment(isolate->vm_tag());
@@ -1550,16 +1665,29 @@
}
Sample* sample = sample_buffer->ReserveSample();
sample->Init(isolate, OS::GetCurrentTimeMicros(), state.tid);
- uword stack_lower = 0;
- uword stack_upper = 0;
- isolate->GetStackBounds(&stack_lower, &stack_upper);
- if ((stack_lower == 0) || (stack_upper == 0)) {
- stack_lower = 0;
- stack_upper = 0;
+ sample->set_vm_tag(isolate->vm_tag());
+ sample->set_user_tag(isolate->user_tag());
+ if (FLAG_profile_native_stack) {
+ // Collect native and Dart frames.
+ uword stack_lower = 0;
+ uword stack_upper = 0;
+ isolate->GetStackBounds(&stack_lower, &stack_upper);
+ if ((stack_lower == 0) || (stack_upper == 0)) {
+ stack_lower = 0;
+ stack_upper = 0;
+ }
+ ProfilerNativeStackWalker stackWalker(sample, stack_lower, stack_upper,
+ state.pc, state.fp, state.sp);
+ stackWalker.walk(isolate->heap());
+ } else {
+ if (isolate->top_exit_frame_info() != 0) {
+ ProfilerDartStackWalker stackWalker(sample);
+ stackWalker.walk();
+ } else {
+ // TODO(johnmccutchan): Support collecting only Dart frames with
+ // ProfilerNativeStackWalker.
+ }
}
- ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper,
- state.pc, state.fp, state.sp);
- stackWalker.walk(isolate->heap(), isolate->vm_tag());
}
} // namespace dart
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index 64c60c4..149f382 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -112,6 +112,7 @@
tid_ = tid;
isolate_ = isolate;
vm_tag_ = VMTag::kInvalidTagId;
+ user_tag_ = UserTags::kNoUserTag;
for (intptr_t i = 0; i < kSampleFramesSize; i++) {
pcs_[i] = 0;
}
@@ -149,11 +150,19 @@
vm_tag_ = tag;
}
+ uword user_tag() const {
+ return user_tag_;
+ }
+ void set_user_tag(uword tag) {
+ user_tag_ = tag;
+ }
+
private:
int64_t timestamp_;
ThreadId tid_;
Isolate* isolate_;
uword vm_tag_;
+ uword user_tag_;
uword pcs_[kSampleFramesSize];
};
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index acf2554..986163b 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -372,6 +372,7 @@
code = fn.CurrentCode();
if (fn.HasCode() && // The function may not have code.
+ !fn.is_intrinsic() && // These may not increment the usage counter.
!code.is_optimized() &&
(fn.CurrentCode() == fn.unoptimized_code()) &&
!code.HasBreakpoint() &&
@@ -835,4 +836,14 @@
return MirrorReference::InstanceSize();
}
+
+intptr_t RawUserTag::VisitUserTagPointers(
+ RawUserTag* raw_obj, ObjectPointerVisitor* visitor) {
+ // Make sure that we got here with the tagged pointer as this.
+ ASSERT(raw_obj->IsHeapObject());
+ visitor->VisitPointers(raw_obj->from(), raw_obj->to());
+ return UserTag::InstanceSize();
+}
+
+
} // namespace dart
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index c63b502..b16d1d5 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -69,6 +69,7 @@
V(Float32x4) \
V(Int32x4) \
V(Float64x2) \
+ V(UserTag) \
#define CLASS_LIST_ARRAYS(V) \
V(Array) \
@@ -774,6 +775,7 @@
intptr_t num_imports_; // Number of entries in imports_.
intptr_t num_anonymous_; // Number of entries in anonymous_classes_.
Dart_NativeEntryResolver native_entry_resolver_; // Resolves natives.
+ Dart_NativeEntrySymbol native_entry_symbol_resolver_;
bool corelib_imported_;
bool debuggable_; // True if debugger can stop in library.
int8_t load_state_; // Of type LibraryState.
@@ -1580,6 +1582,29 @@
};
+// UserTag are used by the profiler to track Dart script state.
+class RawUserTag : public RawInstance {
+ RAW_HEAP_OBJECT_IMPLEMENTATION(UserTag);
+
+ RawObject** from() {
+ return reinterpret_cast<RawObject**>(&ptr()->label_);
+ }
+
+ RawString* label_;
+
+ RawObject** to() {
+ return reinterpret_cast<RawObject**>(&ptr()->label_);
+ }
+
+ // Isolate unique tag.
+ uword tag_;
+
+ friend class SnapshotReader;
+
+ public:
+ uword tag() const { return tag_; }
+};
+
// Class Id predicates.
inline bool RawObject::IsErrorClassId(intptr_t index) {
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index c4f063e..08e0ecb 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -2693,4 +2693,27 @@
}
}
+
+RawUserTag* UserTag::ReadFrom(SnapshotReader* reader,
+ intptr_t object_id,
+ intptr_t tags,
+ Snapshot::Kind kind) {
+ UNREACHABLE();
+ return UserTag::null();
+}
+
+
+void RawUserTag::WriteTo(SnapshotWriter* writer,
+ intptr_t object_id,
+ Snapshot::Kind kind) {
+ if (kind == Snapshot::kMessage) {
+ // We do not allow objects with native fields in an isolate message.
+ writer->SetWriteException(Exceptions::kArgument,
+ "Illegal argument in isolate message"
+ " : (object is a UserTag)");
+ } else {
+ UNREACHABLE();
+ }
+}
+
} // namespace dart
diff --git a/runtime/vm/runtime_entry.h b/runtime/vm/runtime_entry.h
index 3bb1dde..9f70b8d 100644
--- a/runtime/vm/runtime_entry.h
+++ b/runtime/vm/runtime_entry.h
@@ -9,6 +9,7 @@
#include "vm/assembler.h"
#include "vm/flags.h"
#include "vm/native_arguments.h"
+#include "vm/tags.h"
namespace dart {
@@ -28,7 +29,11 @@
function_(function),
argument_count_(argument_count),
is_leaf_(is_leaf),
- is_float_(is_float) { }
+ is_float_(is_float),
+ next_(NULL) {
+ // Strip off const for registration.
+ VMTag::RegisterRuntimeEntry(const_cast<RuntimeEntry*>(this));
+ }
~RuntimeEntry() {}
const char* name() const { return name_; }
@@ -41,12 +46,16 @@
// Generate code to call the runtime entry.
void Call(Assembler* assembler, intptr_t argument_count) const;
+ void set_next(const RuntimeEntry* next) { next_ = next; }
+ const RuntimeEntry* next() const { return next_; }
+
private:
const char* name_;
const RuntimeFunction function_;
const intptr_t argument_count_;
const bool is_leaf_;
const bool is_float_;
+ const RuntimeEntry* next_;
DISALLOW_COPY_AND_ASSIGN(RuntimeEntry);
};
diff --git a/runtime/vm/runtime_entry_arm64.cc b/runtime/vm/runtime_entry_arm64.cc
index d771682..6147834 100644
--- a/runtime/vm/runtime_entry_arm64.cc
+++ b/runtime/vm/runtime_entry_arm64.cc
@@ -15,8 +15,39 @@
#define __ assembler->
+// Generate code to call into the stub which will call the runtime
+// function. Input for the stub is as follows:
+// SP : points to the arguments and return value array.
+// R5 : address of the runtime function to call.
+// R4 : number of arguments to the call.
void RuntimeEntry::Call(Assembler* assembler, intptr_t argument_count) const {
- UNIMPLEMENTED();
+ // Compute the effective address. When running under the simulator,
+ // this is a redirection address that forces the simulator to call
+ // into the runtime system.
+ uword entry = GetEntryPoint();
+#if defined(USING_SIMULATOR)
+ // Redirection to leaf runtime calls supports a maximum of 8 arguments passed
+ // in registers.
+ ASSERT(argument_count >= 0);
+ ASSERT(!is_leaf() || (argument_count <= 8));
+ Simulator::CallKind call_kind =
+ is_leaf() ? (is_float() ? Simulator::kLeafFloatRuntimeCall
+ : Simulator::kLeafRuntimeCall)
+ : Simulator::kRuntimeCall;
+ entry =
+ Simulator::RedirectExternalReference(entry, call_kind, argument_count);
+#endif
+ if (is_leaf()) {
+ ASSERT(argument_count == this->argument_count());
+ ExternalLabel label(name(), entry);
+ __ BranchLink(&label, PP);
+ } else {
+ // Argument count is not checked here, but in the runtime entry for a more
+ // informative error message.
+ __ LoadImmediate(R5, entry, PP);
+ __ LoadImmediate(R4, argument_count, PP);
+ __ BranchLink(&StubCode::CallToRuntimeLabel(), PP);
+ }
}
} // namespace dart
diff --git a/runtime/vm/runtime_entry_test.cc b/runtime/vm/runtime_entry_test.cc
index 6e5720c..b0b04c5 100644
--- a/runtime/vm/runtime_entry_test.cc
+++ b/runtime/vm/runtime_entry_test.cc
@@ -2,10 +2,6 @@
// 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.
-// TODO(zra): Remove when tests are ready to enable.
-#include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
-
#include "vm/runtime_entry.h"
#include "vm/object.h"
@@ -63,5 +59,3 @@
END_LEAF_RUNTIME_ENTRY
} // namespace dart
-
-#endif // !defined(TARGET_ARCH_ARM64)
diff --git a/runtime/vm/scavenger.cc b/runtime/vm/scavenger.cc
index 0c43bec..ec28362 100644
--- a/runtime/vm/scavenger.cc
+++ b/runtime/vm/scavenger.cc
@@ -79,7 +79,6 @@
visited_count_(0),
handled_count_(0),
delayed_weak_stack_(),
- growth_policy_(PageSpace::kControlGrowth),
bytes_promoted_(0),
visiting_old_object_(NULL),
in_scavenge_pointer_(false) { }
@@ -200,32 +199,15 @@
//
// This object is a survivor of a previous scavenge. Attempt to promote
// the object.
- new_addr = heap_->TryAllocate(size, Heap::kOld, growth_policy_);
+ new_addr =
+ heap_->TryAllocate(size, Heap::kOld, PageSpace::kForceGrowth);
if (new_addr != 0) {
// If promotion succeeded then we need to remember it so that it can
// be traversed later.
scavenger_->PushToPromotedStack(new_addr);
bytes_promoted_ += size;
class_table->UpdateAllocatedOld(cid, size);
- } else if (!scavenger_->had_promotion_failure_) {
- // Signal a promotion failure and set the growth policy for
- // this, and all subsequent promotion allocations, to force
- // growth.
- scavenger_->had_promotion_failure_ = true;
- growth_policy_ = PageSpace::kForceGrowth;
- new_addr = heap_->TryAllocate(size, Heap::kOld, growth_policy_);
- if (new_addr != 0) {
- scavenger_->PushToPromotedStack(new_addr);
- bytes_promoted_ += size;
- class_table->UpdateAllocatedOld(cid, size);
- } else {
- // Promotion did not succeed. Copy into the to space
- // instead.
- new_addr = scavenger_->TryAllocate(size);
- class_table->UpdateLiveNew(cid, size);
- }
} else {
- ASSERT(growth_policy_ == PageSpace::kForceGrowth);
// Promotion did not succeed. Copy into the to space instead.
new_addr = scavenger_->TryAllocate(size);
class_table->UpdateLiveNew(cid, size);
@@ -258,7 +240,6 @@
typedef std::multimap<RawObject*, RawWeakProperty*> DelaySet;
DelaySet delay_set_;
GrowableArray<RawObject*> delayed_weak_stack_;
- PageSpace::GrowthPolicy growth_policy_;
// TODO(cshapiro): use this value to compute survival statistics for
// new space growth policy.
intptr_t bytes_promoted_;
@@ -518,16 +499,26 @@
while (queue != NULL) {
WeakReferenceSet* reference_set = WeakReferenceSet::Pop(&queue);
ASSERT(reference_set != NULL);
+ intptr_t num_keys = reference_set->num_keys();
+ intptr_t num_values = reference_set->num_values();
+ if ((num_keys == 1) && (num_values == 1) &&
+ reference_set->SingletonKeyEqualsValue()) {
+ // We do not have to process sets that have just one key/value pair
+ // and the key and value are identical.
+ continue;
+ }
bool is_unreachable = true;
// Test each key object for reachability. If a key object is
// reachable, all value objects should be scavenged.
- for (intptr_t k = 0; k < reference_set->num_keys(); ++k) {
+ for (intptr_t k = 0; k < num_keys; ++k) {
if (!IsUnreachable(reference_set->get_key(k))) {
- for (intptr_t v = 0; v < reference_set->num_values(); ++v) {
+ for (intptr_t v = 0; v < num_values; ++v) {
visitor->VisitPointer(reference_set->get_value(v));
}
is_unreachable = false;
- delete reference_set;
+ // Since we have found a key object that is reachable and all
+ // value objects have been marked we can break out of iterating
+ // this set and move on to the next set.
break;
}
}
@@ -542,17 +533,16 @@
ProcessToSpace(visitor);
} else {
// Break out of the loop if there has been no forward process.
+ // All key objects in the weak reference sets are unreachable
+ // so we reset the weak reference sets queue.
+ state->set_delayed_weak_reference_sets(NULL);
break;
}
}
- // Deallocate any unreachable references on the delay queue.
- if (state->delayed_weak_reference_sets() != NULL) {
- WeakReferenceSet* queue = state->delayed_weak_reference_sets();
- state->set_delayed_weak_reference_sets(NULL);
- while (queue != NULL) {
- delete WeakReferenceSet::Pop(&queue);
- }
- }
+ ASSERT(state->delayed_weak_reference_sets() == NULL);
+ // All weak reference sets are zone allocated and unmarked references which
+ // were on the delay queue will be freed when the zone is released in the
+ // epilog callback.
}
@@ -701,7 +691,6 @@
// Scavenging is not reentrant. Make sure that is the case.
ASSERT(!scavenging_);
scavenging_ = true;
- had_promotion_failure_ = false;
Isolate* isolate = Isolate::Current();
NoHandleScope no_handles(isolate);
diff --git a/runtime/vm/scavenger.h b/runtime/vm/scavenger.h
index 17d2857..f8d81e3 100644
--- a/runtime/vm/scavenger.h
+++ b/runtime/vm/scavenger.h
@@ -98,11 +98,6 @@
*end = to_->end();
}
- // Returns true if the last scavenge had a promotion failure.
- bool HadPromotionFailure() {
- return had_promotion_failure_;
- }
-
void WriteProtect(bool read_only);
void AddGCTime(int64_t micros) {
@@ -209,8 +204,6 @@
// Keep track whether a scavenge is currently running.
bool scavenging_;
- // Keep track whether the scavenge had a promotion failure.
- bool had_promotion_failure_;
int64_t gc_time_micros_;
intptr_t collections_;
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 5d5c246..11548e5 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -31,6 +31,8 @@
namespace dart {
DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests.");
+DECLARE_FLAG(bool, enable_type_checks);
+DECLARE_FLAG(bool, enable_asserts);
struct ResourcesEntry {
const char* path_;
@@ -249,6 +251,81 @@
}
+// These must be kept in sync with service/constants.dart
+#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
+#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
+
+
+static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code,
+ const String& name) {
+ const Array& list = Array::Handle(Array::New(4));
+ ASSERT(!list.IsNull());
+ const Integer& code_int = Integer::Handle(Integer::New(code));
+ const Integer& port_int = Integer::Handle(Integer::New(port_id));
+ const Object& send_port = Object::Handle(
+ DartLibraryCalls::NewSendPort(port_id));
+ ASSERT(!send_port.IsNull());
+ list.SetAt(0, code_int);
+ list.SetAt(1, port_int);
+ list.SetAt(2, send_port);
+ list.SetAt(3, name);
+ return list.raw();
+}
+
+
+class RegisterRunningIsolatesVisitor : public IsolateVisitor {
+ public:
+ explicit RegisterRunningIsolatesVisitor(Isolate* service_isolate)
+ : IsolateVisitor(),
+ register_function_(Function::Handle(service_isolate)),
+ service_isolate_(service_isolate) {
+ ASSERT(Isolate::Current() == service_isolate_);
+ // Get library.
+ const String& library_url = Symbols::DartVMService();
+ ASSERT(!library_url.IsNull());
+ const Library& library =
+ Library::Handle(Library::LookupLibrary(library_url));
+ ASSERT(!library.IsNull());
+ // Get function.
+ const String& function_name =
+ String::Handle(String::New("_registerIsolate"));
+ ASSERT(!function_name.IsNull());
+ register_function_ = library.LookupFunctionAllowPrivate(function_name);
+ ASSERT(!register_function_.IsNull());
+ }
+
+ virtual void VisitIsolate(Isolate* isolate) {
+ ASSERT(Isolate::Current() == service_isolate_);
+ if ((isolate == service_isolate_) ||
+ (isolate == Dart::vm_isolate())) {
+ // We do not register the service or vm isolate.
+ return;
+ }
+ // Setup arguments for call.
+ intptr_t port_id = isolate->main_port();
+ const Integer& port_int = Integer::Handle(Integer::New(port_id));
+ ASSERT(!port_int.IsNull());
+ const Object& send_port = Object::Handle(
+ DartLibraryCalls::NewSendPort(port_id));
+ ASSERT(!send_port.IsNull());
+ const String& name = String::Handle(String::New(isolate->name()));
+ ASSERT(!name.IsNull());
+ const Array& args = Array::Handle(Array::New(3));
+ ASSERT(!args.IsNull());
+ args.SetAt(0, port_int);
+ args.SetAt(1, send_port);
+ args.SetAt(2, name);
+ Object& r = Object::Handle(service_isolate_);
+ r = DartEntry::InvokeFunction(register_function_, args);
+ ASSERT(!r.IsError());
+ }
+
+ private:
+ Function& register_function_;
+ Isolate* service_isolate_;
+};
+
+
Isolate* Service::GetServiceIsolate(void* callback_data) {
if (service_isolate_ != NULL) {
// Already initialized, return service isolate.
@@ -326,34 +403,19 @@
ASSERT(port_ != ILLEGAL_PORT);
Dart_ExitScope();
}
+ {
+ // Register existing isolates.
+ StackZone zone(isolate);
+ HANDLESCOPE(isolate);
+ RegisterRunningIsolatesVisitor register_isolates(isolate);
+ Isolate::VisitIsolates(®ister_isolates);
+ }
Isolate::SetCurrent(NULL);
service_isolate_ = reinterpret_cast<Isolate*>(isolate);
return service_isolate_;
}
-// These must be kept in sync with service/constants.dart
-#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
-#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
-
-
-static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code,
- const String& name) {
- const Array& list = Array::Handle(Array::New(4));
- ASSERT(!list.IsNull());
- const Integer& code_int = Integer::Handle(Integer::New(code));
- const Integer& port_int = Integer::Handle(Integer::New(port_id));
- const Object& send_port = Object::Handle(
- DartLibraryCalls::NewSendPort(port_id));
- ASSERT(!send_port.IsNull());
- list.SetAt(0, code_int);
- list.SetAt(1, port_int);
- list.SetAt(2, send_port);
- list.SetAt(3, name);
- return list.raw();
-}
-
-
bool Service::SendIsolateStartupMessage() {
if (!IsRunning()) {
return false;
@@ -1250,6 +1312,7 @@
static bool HandleScriptsEnumerate(Isolate* isolate, JSONStream* js) {
JSONObject jsobj(js);
jsobj.AddProperty("type", "ScriptList");
+ jsobj.AddProperty("id", "scripts");
JSONArray members(&jsobj, "members");
const GrowableObjectArray& libs =
GrowableObjectArray::Handle(isolate->object_store()->libraries());
@@ -1768,7 +1831,8 @@
jsobj.AddProperty("id", "vm");
jsobj.AddProperty("architecture", CPU::Id());
jsobj.AddProperty("version", Version::String());
-
+ jsobj.AddProperty("assertsEnabled", FLAG_enable_asserts);
+ jsobj.AddProperty("typeChecksEnabled", FLAG_enable_type_checks);
int64_t start_time_micros = Dart::vm_isolate()->start_time();
int64_t uptime_micros = (OS::GetCurrentTimeMicros() - start_time_micros);
double seconds = (static_cast<double>(uptime_micros) /
diff --git a/runtime/vm/service/vmservice.dart b/runtime/vm/service/vmservice.dart
index 886268d..35fdb84 100644
--- a/runtime/vm/service/vmservice.dart
+++ b/runtime/vm/service/vmservice.dart
@@ -94,3 +94,8 @@
// Return the port we expect isolate startup and shutdown messages on.
return new VMService().receivePort;
}
+
+void _registerIsolate(int port_id, SendPort sp, String name) {
+ var service = new VMService();
+ service.runningIsolates.isolateStartup(port_id, sp, name);
+}
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index 5a462cf..b29af7f 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -35,6 +35,358 @@
#define SScanF sscanf // NOLINT
+// SimulatorSetjmpBuffer are linked together, and the last created one
+// is referenced by the Simulator. When an exception is thrown, the exception
+// runtime looks at where to jump and finds the corresponding
+// SimulatorSetjmpBuffer based on the stack pointer of the exception handler.
+// The runtime then does a Longjmp on that buffer to return to the simulator.
+class SimulatorSetjmpBuffer {
+ public:
+ int Setjmp() { return setjmp(buffer_); }
+ void Longjmp() {
+ // "This" is now the last setjmp buffer.
+ simulator_->set_last_setjmp_buffer(this);
+ longjmp(buffer_, 1);
+ }
+
+ explicit SimulatorSetjmpBuffer(Simulator* sim) {
+ simulator_ = sim;
+ link_ = sim->last_setjmp_buffer();
+ sim->set_last_setjmp_buffer(this);
+ sp_ = static_cast<uword>(sim->get_register(R31, R31IsSP));
+ native_sp_ = reinterpret_cast<uword>(&sim); // Current C++ stack pointer.
+ }
+
+ ~SimulatorSetjmpBuffer() {
+ ASSERT(simulator_->last_setjmp_buffer() == this);
+ simulator_->set_last_setjmp_buffer(link_);
+ }
+
+ SimulatorSetjmpBuffer* link() { return link_; }
+
+ uword sp() { return sp_; }
+ uword native_sp() { return native_sp_; }
+
+ private:
+ uword sp_;
+ uword native_sp_;
+ Simulator* simulator_;
+ SimulatorSetjmpBuffer* link_;
+ jmp_buf buffer_;
+
+ friend class Simulator;
+};
+
+
+// The SimulatorDebugger class is used by the simulator while debugging
+// simulated ARM64 code.
+class SimulatorDebugger {
+ public:
+ explicit SimulatorDebugger(Simulator* sim);
+ ~SimulatorDebugger();
+
+ void Stop(Instr* instr, const char* message);
+ void Debug();
+ char* ReadLine(const char* prompt);
+
+ private:
+ Simulator* sim_;
+
+ bool GetValue(char* desc, int64_t* value);
+ // TODO(zra): GetVValue for doubles.
+ // TODO(zra): Breakpoints.
+};
+
+
+SimulatorDebugger::SimulatorDebugger(Simulator* sim) {
+ sim_ = sim;
+}
+
+
+SimulatorDebugger::~SimulatorDebugger() {
+}
+
+void SimulatorDebugger::Stop(Instr* instr, const char* message) {
+ OS::Print("Simulator hit %s\n", message);
+ Debug();
+}
+
+
+static Register LookupCpuRegisterByName(const char* name) {
+ static const char* kNames[] = {
+ "r0", "r1", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27",
+ "r28", "r29", "r30",
+
+ "ip0", "ip1", "pp", "ctx", "fp", "lr", "sp", "zr",
+ };
+ static const Register kRegisters[] = {
+ R0, R1, R2, R3, R4, R5, R6, R7,
+ R8, R9, R10, R11, R12, R13, R14, R15,
+ R16, R17, R18, R19, R20, R21, R22, R23,
+ R24, R25, R26, R27, R28, R29, R30,
+
+ IP0, IP1, PP, CTX, FP, LR, SP, ZR,
+ };
+ ASSERT(ARRAY_SIZE(kNames) == ARRAY_SIZE(kRegisters));
+ for (unsigned i = 0; i < ARRAY_SIZE(kNames); i++) {
+ if (strcmp(kNames[i], name) == 0) {
+ return kRegisters[i];
+ }
+ }
+ return kNoRegister;
+}
+
+
+bool SimulatorDebugger::GetValue(char* desc, int64_t* value) {
+ Register reg = LookupCpuRegisterByName(desc);
+ if (reg != kNoRegister) {
+ *value = sim_->get_register(reg);
+ return true;
+ }
+ if (desc[0] == '*') {
+ int64_t addr;
+ if (GetValue(desc + 1, &addr)) {
+ if (Simulator::IsIllegalAddress(addr)) {
+ return false;
+ }
+ *value = *(reinterpret_cast<int64_t*>(addr));
+ return true;
+ }
+ }
+ if (strcmp("pc", desc) == 0) {
+ *value = sim_->get_pc();
+ return true;
+ }
+ bool retval = SScanF(desc, "0x%"Px64, value) == 1;
+ if (!retval) {
+ retval = SScanF(desc, "%"Px64, value) == 1;
+ }
+ return retval;
+}
+
+
+void SimulatorDebugger::Debug() {
+ intptr_t last_pc = -1;
+ bool done = false;
+
+#define COMMAND_SIZE 63
+#define ARG_SIZE 255
+
+#define STR(a) #a
+#define XSTR(a) STR(a)
+
+ char cmd[COMMAND_SIZE + 1];
+ char arg1[ARG_SIZE + 1];
+ char arg2[ARG_SIZE + 1];
+
+ // make sure to have a proper terminating character if reaching the limit
+ cmd[COMMAND_SIZE] = 0;
+ arg1[ARG_SIZE] = 0;
+ arg2[ARG_SIZE] = 0;
+
+ // TODO(zra): Undo all set breakpoints while running in the debugger shell.
+ // This will make them invisible to all commands.
+ // UndoBreakpoints();
+
+ while (!done) {
+ if (last_pc != sim_->get_pc()) {
+ last_pc = sim_->get_pc();
+ if (Simulator::IsIllegalAddress(last_pc)) {
+ OS::Print("pc is out of bounds: 0x%" Px "\n", last_pc);
+ } else {
+ Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize);
+ }
+ }
+ char* line = ReadLine("sim> ");
+ if (line == NULL) {
+ FATAL("ReadLine failed");
+ } else {
+ // Use sscanf to parse the individual parts of the command line. At the
+ // moment no command expects more than two parameters.
+ int args = SScanF(line,
+ "%" XSTR(COMMAND_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s",
+ cmd, arg1, arg2);
+ if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+ OS::Print("c/cont -- continue execution\n"
+ "disasm -- disassemble instrs at current pc location\n"
+ " other variants are:\n"
+ " disasm <address>\n"
+ " disasm <address> <number_of_instructions>\n"
+ " by default 10 instrs are disassembled\n"
+ "gdb -- transfer control to gdb\n"
+ "h/help -- print this help string\n"
+ "p/print <reg or value or *addr> -- print integer value\n"
+ "po/printobject <*reg or *addr> -- print object\n"
+ "si/stepi -- single step an instruction\n"
+ "q/quit -- Quit the debugger and exit the program\n");
+ } else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) {
+ OS::Print("Quitting\n");
+ OS::Exit(0);
+ } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
+ sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
+ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
+ // Execute the one instruction we broke at with breakpoints disabled.
+ sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
+ // Leave the debugger shell.
+ done = true;
+ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
+ if (args == 2) {
+ int64_t value;
+ if (GetValue(arg1, &value)) {
+ OS::Print("%s: %"Pu64" 0x%"Px64"\n", arg1, value, value);
+ } else {
+ OS::Print("%s unrecognized\n", arg1);
+ }
+ } else {
+ OS::Print("print <reg or value or *addr>\n");
+ }
+ } else if ((strcmp(cmd, "po") == 0) ||
+ (strcmp(cmd, "printobject") == 0)) {
+ if (args == 2) {
+ int64_t value;
+ // Make the dereferencing '*' optional.
+ if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
+ GetValue(arg1, &value)) {
+ if (Isolate::Current()->heap()->Contains(value)) {
+ OS::Print("%s: \n", arg1);
+#if defined(DEBUG)
+ const Object& obj = Object::Handle(
+ reinterpret_cast<RawObject*>(value));
+ obj.Print();
+#endif // defined(DEBUG)
+ } else {
+ OS::Print("0x%"Px64" is not an object reference\n", value);
+ }
+ } else {
+ OS::Print("%s unrecognized\n", arg1);
+ }
+ } else {
+ OS::Print("printobject <*reg or *addr>\n");
+ }
+ } else if (strcmp(cmd, "disasm") == 0) {
+ int64_t start = 0;
+ int64_t end = 0;
+ if (args == 1) {
+ start = sim_->get_pc();
+ end = start + (10 * Instr::kInstrSize);
+ } else if (args == 2) {
+ if (GetValue(arg1, &start)) {
+ // no length parameter passed, assume 10 instructions
+ if (Simulator::IsIllegalAddress(start)) {
+ // If start isn't a valid address, warn and use PC instead
+ OS::Print("First argument yields invalid address: 0x%"Px64"\n",
+ start);
+ OS::Print("Using PC instead");
+ start = sim_->get_pc();
+ }
+ end = start + (10 * Instr::kInstrSize);
+ }
+ } else {
+ int64_t length;
+ if (GetValue(arg1, &start) && GetValue(arg2, &length)) {
+ if (Simulator::IsIllegalAddress(start)) {
+ // If start isn't a valid address, warn and use PC instead
+ OS::Print("First argument yields invalid address: 0x%"Px64"\n",
+ start);
+ OS::Print("Using PC instead\n");
+ start = sim_->get_pc();
+ }
+ end = start + (length * Instr::kInstrSize);
+ }
+ }
+ Disassembler::Disassemble(start, end);
+ } else if (strcmp(cmd, "gdb") == 0) {
+ OS::Print("relinquishing control to gdb\n");
+ OS::DebugBreak();
+ OS::Print("regaining control from gdb\n");
+ } else {
+ OS::Print("Unknown command: %s\n", cmd);
+ }
+ }
+ delete[] line;
+ }
+
+ // TODO(zra): Add all the breakpoints back to stop execution and enter the
+ // debugger shell when hit.
+ // RedoBreakpoints();
+
+#undef COMMAND_SIZE
+#undef ARG_SIZE
+
+#undef STR
+#undef XSTR
+}
+
+
+char* SimulatorDebugger::ReadLine(const char* prompt) {
+ char* result = NULL;
+ char line_buf[256];
+ intptr_t offset = 0;
+ bool keep_going = true;
+ OS::Print("%s", prompt);
+ while (keep_going) {
+ if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
+ // fgets got an error. Just give up.
+ if (result != NULL) {
+ delete[] result;
+ }
+ return NULL;
+ }
+ intptr_t len = strlen(line_buf);
+ if (len > 1 &&
+ line_buf[len - 2] == '\\' &&
+ line_buf[len - 1] == '\n') {
+ // When we read a line that ends with a "\" we remove the escape and
+ // append the remainder.
+ line_buf[len - 2] = '\n';
+ line_buf[len - 1] = 0;
+ len -= 1;
+ } else if ((len > 0) && (line_buf[len - 1] == '\n')) {
+ // Since we read a new line we are done reading the line. This
+ // will exit the loop after copying this buffer into the result.
+ keep_going = false;
+ }
+ if (result == NULL) {
+ // Allocate the initial result and make room for the terminating '\0'
+ result = new char[len + 1];
+ if (result == NULL) {
+ // OOM, so cannot readline anymore.
+ return NULL;
+ }
+ } else {
+ // Allocate a new result with enough room for the new addition.
+ intptr_t new_len = offset + len + 1;
+ char* new_result = new char[new_len];
+ if (new_result == NULL) {
+ // OOM, free the buffer allocated so far and return NULL.
+ delete[] result;
+ return NULL;
+ } else {
+ // Copy the existing input into the new array and set the new
+ // array as the result.
+ memmove(new_result, result, offset);
+ delete[] result;
+ result = new_result;
+ }
+ }
+ // Copy the newly read line into the result.
+ memmove(result + offset, line_buf, len);
+ offset += len;
+ }
+ ASSERT(result != NULL);
+ result[offset] = '\0';
+ return result;
+}
+
+
Simulator::Simulator() {
// Setup simulator support first. Some of this information is needed to
// setup the architecture state.
@@ -80,6 +432,76 @@
}
+// When the generated code calls an external reference we need to catch that in
+// the simulator. The external reference will be a function compiled for the
+// host architecture. We need to call that function instead of trying to
+// execute it with the simulator. We do that by redirecting the external
+// reference to a svc (supervisor call) instruction that is handled by
+// the simulator. We write the original destination of the jump just at a known
+// offset from the svc instruction so the simulator knows what to call.
+class Redirection {
+ public:
+ uword address_of_hlt_instruction() {
+ return reinterpret_cast<uword>(&hlt_instruction_);
+ }
+
+ uword external_function() const { return external_function_; }
+
+ Simulator::CallKind call_kind() const { return call_kind_; }
+
+ int argument_count() const { return argument_count_; }
+
+ static Redirection* Get(uword external_function,
+ Simulator::CallKind call_kind,
+ int argument_count) {
+ Redirection* current;
+ for (current = list_; current != NULL; current = current->next_) {
+ if (current->external_function_ == external_function) return current;
+ }
+ return new Redirection(external_function, call_kind, argument_count);
+ }
+
+ static Redirection* FromHltInstruction(Instr* hlt_instruction) {
+ char* addr_of_hlt = reinterpret_cast<char*>(hlt_instruction);
+ char* addr_of_redirection =
+ addr_of_hlt - OFFSET_OF(Redirection, hlt_instruction_);
+ return reinterpret_cast<Redirection*>(addr_of_redirection);
+ }
+
+ private:
+ static const int32_t kRedirectInstruction = Instr::kRedirectInstruction;
+ Redirection(uword external_function,
+ Simulator::CallKind call_kind,
+ int argument_count)
+ : external_function_(external_function),
+ call_kind_(call_kind),
+ argument_count_(argument_count),
+ hlt_instruction_(kRedirectInstruction),
+ next_(list_) {
+ list_ = this;
+ }
+
+ uword external_function_;
+ Simulator::CallKind call_kind_;
+ int argument_count_;
+ uint32_t hlt_instruction_;
+ Redirection* next_;
+ static Redirection* list_;
+};
+
+
+Redirection* Redirection::list_ = NULL;
+
+
+uword Simulator::RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count) {
+ Redirection* redirection =
+ Redirection::Get(function, call_kind, argument_count);
+ return redirection->address_of_hlt_instruction();
+}
+
+
// Get the active Simulator for the current isolate.
Simulator* Simulator::Current() {
Simulator* simulator = Isolate::Current()->simulator();
@@ -95,7 +517,6 @@
void Simulator::set_register(Register reg, int64_t value, R31Type r31t) {
// register is in range, and if it is R31, a mode is specified.
ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
- ASSERT((reg != R31) || (r31t != R31IsUndef));
if ((reg != R31) || (r31t != R31IsZR)) {
registers_[reg] = value;
}
@@ -105,7 +526,6 @@
// Get the register from the architecture state.
int64_t Simulator::get_register(Register reg, R31Type r31t) const {
ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
- ASSERT((reg != R31) || (r31t != R31IsUndef));
if ((reg == R31) && (r31t == R31IsZR)) {
return 0;
} else {
@@ -116,7 +536,6 @@
void Simulator::set_wregister(Register reg, int32_t value, R31Type r31t) {
ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
- ASSERT((reg != R31) || (r31t != R31IsUndef));
// When setting in W mode, clear the high bits.
if ((reg != R31) || (r31t != R31IsZR)) {
registers_[reg] = Utils::LowHighTo64Bits(static_cast<uint32_t>(value), 0);
@@ -127,7 +546,6 @@
// Get the register from the architecture state.
int32_t Simulator::get_wregister(Register reg, R31Type r31t) const {
ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
- ASSERT((reg != R31) || (r31t != R31IsUndef));
if ((reg == R31) && (r31t == R31IsZR)) {
return 0;
} else {
@@ -139,23 +557,32 @@
// Raw access to the PC register.
void Simulator::set_pc(int64_t value) {
pc_modified_ = true;
+ last_pc_ = pc_;
pc_ = value;
}
-// Raw access to the PC register without the special adjustment when reading.
+// Raw access to the pc.
int64_t Simulator::get_pc() const {
return pc_;
}
+int64_t Simulator::get_last_pc() const {
+ return last_pc_;
+}
+
+
void Simulator::HandleIllegalAccess(uword addr, Instr* instr) {
uword fault_pc = get_pc();
+ uword last_pc = get_last_pc();
// TODO(zra): drop into debugger.
char buffer[128];
snprintf(buffer, sizeof(buffer),
- "illegal memory access at 0x%" Px ", pc=0x%" Px "\n",
- addr, fault_pc);
+ "illegal memory access at 0x%" Px ", pc=0x%" Px ", last_pc=0x%" Px"\n",
+ addr, fault_pc, last_pc);
+ SimulatorDebugger dbg(this);
+ dbg.Stop(instr, buffer);
// The debugger will return control in non-interactive mode.
FATAL("Cannot continue execution after illegal memory access.");
}
@@ -168,7 +595,8 @@
char buffer[64];
snprintf(buffer, sizeof(buffer),
"unaligned %s at 0x%" Px ", pc=%p\n", msg, addr, instr);
- // TODO(zra): Drop into the simulator debugger when it exists.
+ SimulatorDebugger dbg(this);
+ dbg.Stop(instr, buffer);
// The debugger will not be able to single step past this instruction, but
// it will be possible to disassemble the code and inspect registers.
FATAL("Cannot continue execution after unaligned access.");
@@ -178,7 +606,8 @@
void Simulator::UnimplementedInstruction(Instr* instr) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "Unimplemented instruction: pc=%p\n", instr);
- // TODO(zra): drop into debugger.
+ SimulatorDebugger dbg(this);
+ dbg.Stop(instr, buffer);
FATAL("Cannot continue execution after unimplemented instruction.");
}
@@ -528,6 +957,22 @@
}
+void Simulator::DecodePCRel(Instr* instr) {
+ const int op = instr->Bit(31);
+ if (op == 0) {
+ // Format(instr, "adr 'rd, 'pcrel")
+ const Register rd = instr->RdField();
+ const int64_t immhi = instr->SImm19Field();
+ const int64_t immlo = instr->Bits(29, 2);
+ const int64_t off = (immhi << 2) | immlo;
+ const int64_t dest = get_pc() + off;
+ set_register(rd, dest, instr->RdMode());
+ } else {
+ UnimplementedInstruction(instr);
+ }
+}
+
+
void Simulator::DecodeDPImmediate(Instr* instr) {
if (instr->IsMoveWideOp()) {
DecodeMoveWide(instr);
@@ -535,14 +980,217 @@
DecodeAddSubImm(instr);
} else if (instr->IsLogicalImmOp()) {
DecodeLogicalImm(instr);
+ } else if (instr->IsPCRelOp()) {
+ DecodePCRel(instr);
} else {
UnimplementedInstruction(instr);
}
}
+void Simulator::DecodeCompareAndBranch(Instr* instr) {
+ const int op = instr->Bit(24);
+ const Register rt = instr->RtField();
+ const int64_t imm19 = instr->SImm19Field();
+ const int64_t dest = get_pc() + (imm19 << 2);
+ const int64_t mask = instr->SFField() == 1 ? kXRegMask : kWRegMask;
+ const int64_t rt_val = get_register(rt, R31IsZR) & mask;
+ if (op == 0) {
+ // Format(instr, "cbz'sf 'rt, 'dest19");
+ if (rt_val == 0) {
+ set_pc(dest);
+ }
+ } else {
+ // Format(instr, "cbnz'sf 'rt, 'dest19");
+ if (rt_val != 0) {
+ set_pc(dest);
+ }
+ }
+}
+
+
+bool Simulator::ConditionallyExecute(Instr* instr) {
+ switch (instr->ConditionField()) {
+ case EQ: return z_flag_;
+ case NE: return !z_flag_;
+ case CS: return c_flag_;
+ case CC: return !c_flag_;
+ case MI: return n_flag_;
+ case PL: return !n_flag_;
+ case VS: return v_flag_;
+ case VC: return !v_flag_;
+ case HI: return c_flag_ && !z_flag_;
+ case LS: return !c_flag_ || z_flag_;
+ case GE: return n_flag_ == v_flag_;
+ case LT: return n_flag_ != v_flag_;
+ case GT: return !z_flag_ && (n_flag_ == v_flag_);
+ case LE: return z_flag_ || (n_flag_ != v_flag_);
+ case AL: return true;
+ default: UNREACHABLE();
+ }
+ return false;
+}
+
+
+void Simulator::DecodeConditionalBranch(Instr* instr) {
+ // Format(instr, "b'cond 'dest19");
+ if ((instr->Bit(24) != 0) || (instr->Bit(4) != 0)) {
+ UnimplementedInstruction(instr);
+ }
+ const int64_t imm19 = instr->SImm19Field();
+ const int64_t dest = get_pc() + (imm19 << 2);
+ if (ConditionallyExecute(instr)) {
+ set_pc(dest);
+ }
+}
+
+
+// Calls into the Dart runtime are based on this interface.
+typedef void (*SimulatorRuntimeCall)(NativeArguments arguments);
+
+// Calls to leaf Dart runtime functions are based on this interface.
+typedef int32_t (*SimulatorLeafRuntimeCall)(
+ int64_t r0, int64_t r1, int64_t r2, int64_t r3,
+ int64_t r4, int64_t r5, int64_t r6, int64_t r7);
+
+// Calls to leaf float Dart runtime functions are based on this interface.
+typedef double (*SimulatorLeafFloatRuntimeCall)(
+ double d0, double d1, double d2, double d3,
+ double d4, double d5, double d6, double d7);
+
+// Calls to native Dart functions are based on this interface.
+typedef void (*SimulatorBootstrapNativeCall)(NativeArguments* arguments);
+typedef void (*SimulatorNativeCall)(NativeArguments* arguments, uword target);
+
+
+void Simulator::DoRedirectedCall(Instr* instr) {
+ SimulatorSetjmpBuffer buffer(this);
+ if (!setjmp(buffer.buffer_)) {
+ int64_t saved_lr = get_register(LR);
+ Redirection* redirection = Redirection::FromHltInstruction(instr);
+ uword external = redirection->external_function();
+ if (FLAG_trace_sim) {
+ OS::Print("Call to host function at 0x%" Pd "\n", external);
+ }
+
+ if ((redirection->call_kind() == kRuntimeCall) ||
+ (redirection->call_kind() == kBootstrapNativeCall) ||
+ (redirection->call_kind() == kNativeCall)) {
+ // Set the top_exit_frame_info of this simulator to the native stack.
+ set_top_exit_frame_info(reinterpret_cast<uword>(&buffer));
+ }
+ if (redirection->call_kind() == kRuntimeCall) {
+ NativeArguments arguments;
+ ASSERT(sizeof(NativeArguments) == 4*kWordSize);
+ arguments.isolate_ = reinterpret_cast<Isolate*>(get_register(R0));
+ arguments.argc_tag_ = get_register(R1);
+ arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(R2));
+ arguments.retval_ = reinterpret_cast<RawObject**>(get_register(R3));
+ SimulatorRuntimeCall target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ target(arguments);
+ set_register(R0, icount_); // Zap result register from void function.
+ set_register(R1, icount_);
+ } else if (redirection->call_kind() == kLeafRuntimeCall) {
+ ASSERT((0 <= redirection->argument_count()) &&
+ (redirection->argument_count() <= 8));
+ int64_t r0 = get_register(R0);
+ int64_t r1 = get_register(R1);
+ int64_t r2 = get_register(R2);
+ int64_t r3 = get_register(R3);
+ int64_t r4 = get_register(R4);
+ int64_t r5 = get_register(R5);
+ int64_t r6 = get_register(R6);
+ int64_t r7 = get_register(R7);
+ SimulatorLeafRuntimeCall target =
+ reinterpret_cast<SimulatorLeafRuntimeCall>(external);
+ r0 = target(r0, r1, r2, r3, r4, r5, r6, r7);
+ set_register(R0, r0); // Set returned result from function.
+ set_register(R1, icount_); // Zap unused result register.
+ } else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
+ // TODO(zra): leaf float runtime calls.
+ UNIMPLEMENTED();
+ } else if (redirection->call_kind() == kBootstrapNativeCall) {
+ NativeArguments* arguments;
+ arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
+ SimulatorBootstrapNativeCall target =
+ reinterpret_cast<SimulatorBootstrapNativeCall>(external);
+ target(arguments);
+ set_register(R0, icount_); // Zap result register from void function.
+ } else {
+ ASSERT(redirection->call_kind() == kNativeCall);
+ NativeArguments* arguments;
+ arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
+ uword target_func = get_register(R1);
+ SimulatorNativeCall target =
+ reinterpret_cast<SimulatorNativeCall>(external);
+ target(arguments, target_func);
+ set_register(R0, icount_); // Zap result register from void function.
+ set_register(R1, icount_);
+ }
+ set_top_exit_frame_info(0);
+
+ // Zap caller-saved registers, since the actual runtime call could have
+ // used them.
+ set_register(R2, icount_);
+ set_register(R3, icount_);
+ set_register(R4, icount_);
+ set_register(R5, icount_);
+ set_register(R6, icount_);
+ set_register(R7, icount_);
+ set_register(R8, icount_);
+ set_register(R9, icount_);
+ set_register(R10, icount_);
+ set_register(R11, icount_);
+ set_register(R12, icount_);
+ set_register(R13, icount_);
+ set_register(R14, icount_);
+ set_register(R15, icount_);
+ set_register(IP0, icount_);
+ set_register(IP1, icount_);
+ set_register(R18, icount_);
+ set_register(LR, icount_);
+
+ // TODO(zra): Zap caller-saved fpu registers.
+
+ // Return.
+ set_pc(saved_lr);
+ } else {
+ // Coming via long jump from a throw. Continue to exception handler.
+ set_top_exit_frame_info(0);
+ }
+}
+
+
void Simulator::DecodeExceptionGen(Instr* instr) {
- UnimplementedInstruction(instr);
+ if ((instr->Bits(0, 2) == 1) && (instr->Bits(2, 3) == 0) &&
+ (instr->Bits(21, 3) == 0)) {
+ // Format(instr, "svc 'imm16");
+ UnimplementedInstruction(instr);
+ } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
+ (instr->Bits(21, 3) == 1)) {
+ // Format(instr, "brk 'imm16");
+ UnimplementedInstruction(instr);
+ } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
+ (instr->Bits(21, 3) == 2)) {
+ // Format(instr, "hlt 'imm16");
+ uint16_t imm = static_cast<uint16_t>(instr->Imm16Field());
+ if (imm == kImmExceptionIsDebug) {
+ SimulatorDebugger dbg(this);
+ const char* message = *reinterpret_cast<const char**>(
+ reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
+ set_pc(get_pc() + Instr::kInstrSize);
+ dbg.Stop(instr, message);
+ } else if (imm == kImmExceptionIsPrintf) {
+ const char* message = *reinterpret_cast<const char**>(
+ reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
+ OS::Print("Simulator hit: %s", message);
+ } else if (imm == kImmExceptionIsRedirectedCall) {
+ DoRedirectedCall(instr);
+ } else {
+ UnimplementedInstruction(instr);
+ }
+ }
}
@@ -561,10 +1209,59 @@
}
+void Simulator::DecodeTestAndBranch(Instr* instr) {
+ const int op = instr->Bit(24);
+ const int bitpos = instr->Bits(19, 4) | (instr->Bit(31) << 5);
+ const int64_t imm14 = instr->SImm14Field();
+ const int64_t dest = get_pc() + (imm14 << 2);
+ const Register rt = instr->RtField();
+ const int64_t rt_val = get_register(rt, R31IsZR);
+ if (op == 0) {
+ // Format(instr, "tbz'sf 'rt, 'bitpos, 'dest14");
+ if ((rt_val & (1 << bitpos)) == 0) {
+ set_pc(dest);
+ }
+ } else {
+ // Format(instr, "tbnz'sf 'rt, 'bitpos, 'dest14");
+ if ((rt_val & (1 << bitpos)) != 0) {
+ set_pc(dest);
+ }
+ }
+}
+
+
+void Simulator::DecodeUnconditionalBranch(Instr* instr) {
+ const bool link = instr->Bit(31) == 1;
+ const int64_t imm26 = instr->SImm26Field();
+ const int64_t dest = get_pc() + (imm26 << 2);
+ const int64_t ret = get_pc() + Instr::kInstrSize;
+ set_pc(dest);
+ if (link) {
+ set_register(LR, ret);
+ }
+}
+
+
void Simulator::DecodeUnconditionalBranchReg(Instr* instr) {
if ((instr->Bits(0, 5) == 0) && (instr->Bits(10, 6) == 0) &&
(instr->Bits(16, 5) == 0x1f)) {
switch (instr->Bits(21, 4)) {
+ case 0: {
+ // Format(instr, "br 'rn");
+ const Register rn = instr->RnField();
+ const int64_t dest = get_register(rn, instr->RnMode());
+ set_pc(dest);
+ break;
+ }
+ case 1: {
+ // Format(instr, "blr 'rn");
+ const Register rn = instr->RnField();
+ const int64_t dest = get_register(rn, instr->RnMode());
+ const int64_t ret = get_pc() + Instr::kInstrSize;
+ set_pc(dest);
+ set_register(LR, ret);
+ break;
+ }
case 2: {
// Format(instr, "ret 'rn");
const Register rn = instr->RnField();
@@ -583,10 +1280,18 @@
void Simulator::DecodeCompareBranch(Instr* instr) {
- if (instr->IsExceptionGenOp()) {
+ if (instr->IsCompareAndBranchOp()) {
+ DecodeCompareAndBranch(instr);
+ } else if (instr->IsConditionalBranchOp()) {
+ DecodeConditionalBranch(instr);
+ } else if (instr->IsExceptionGenOp()) {
DecodeExceptionGen(instr);
} else if (instr->IsSystemOp()) {
DecodeSystem(instr);
+ } else if (instr->IsTestAndBranchOp()) {
+ DecodeTestAndBranch(instr);
+ } else if (instr->IsUnconditionalBranchOp()) {
+ DecodeUnconditionalBranch(instr);
} else if (instr->IsUnconditionalBranchRegOp()) {
DecodeUnconditionalBranchReg(instr);
} else {
@@ -616,6 +1321,12 @@
const uint32_t imm12 = static_cast<uint32_t>(instr->Imm12Field());
const uint32_t offset = imm12 << size;
address = rn_val + offset;
+ } else if (instr->Bits(10, 2) == 0) {
+ // addr = rn + signed 9-bit immediate offset.
+ wb = false;
+ const int64_t offset = static_cast<int64_t>(instr->SImm9Field());
+ address = rn_val + offset;
+ wb_address = rn_val;
} else if (instr->Bit(10) == 1) {
// addr = rn + signed 9-bit immediate offset.
wb = true;
@@ -740,9 +1451,32 @@
}
+void Simulator::DecodeLoadRegLiteral(Instr* instr) {
+ if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
+ (instr->Bits(24, 3) != 0)) {
+ UnimplementedInstruction(instr);
+ }
+
+ const Register rt = instr->RtField();
+ const int64_t off = instr->SImm19Field() << 2;
+ const int64_t pc = reinterpret_cast<int64_t>(instr);
+ const int64_t address = pc + off;
+ const int64_t val = ReadX(address, instr);
+ if (instr->Bit(30)) {
+ // Format(instr, "ldrx 'rt, 'pcldr");
+ set_register(rt, val, R31IsZR);
+ } else {
+ // Format(instr, "ldrw 'rt, 'pcldr");
+ set_wregister(rt, static_cast<int32_t>(val), R31IsZR);
+ }
+}
+
+
void Simulator::DecodeLoadStore(Instr* instr) {
if (instr->IsLoadStoreRegOp()) {
DecodeLoadStoreReg(instr);
+ } else if (instr->IsLoadRegLiteralOp()) {
+ DecodeLoadRegLiteral(instr);
} else {
UnimplementedInstruction(instr);
}
@@ -811,6 +1545,7 @@
break;
default:
UNREACHABLE();
+ break;
}
int64_t mask = (reg_size == kXRegSizeInBits) ? kXRegMask : kWRegMask;
return (value << amount) & mask;
@@ -837,39 +1572,43 @@
void Simulator::DecodeAddSubShiftExt(Instr* instr) {
- switch (instr->Bit(30)) {
- case 0: {
- // Format(instr, "add'sf's 'rd, 'rn, 'shift_op");
- const Register rd = instr->RdField();
- const Register rn = instr->RnField();
- const int64_t rm_val = DecodeShiftExtendOperand(instr);
- if (instr->SFField()) {
- // 64-bit add.
- const int64_t rn_val = get_register(rn, instr->RnMode());
- const int64_t alu_out = rn_val + rm_val;
- set_register(rd, alu_out, instr->RdMode());
- if (instr->HasS()) {
- SetNZFlagsX(alu_out);
- SetCFlag(CarryFromX(rn_val, rm_val));
- SetVFlag(OverflowFromX(alu_out, rn_val, rm_val, true));
- }
- } else {
- // 32-bit add.
- const int32_t rn_val = get_wregister(rn, instr->RnMode());
- const int32_t rm_val32 = static_cast<int32_t>(rm_val & kWRegMask);
- const int32_t alu_out = rn_val + rm_val32;
- set_wregister(rd, alu_out, instr->RdMode());
- if (instr->HasS()) {
- SetNZFlagsW(alu_out);
- SetCFlag(CarryFromW(rn_val, rm_val32));
- SetVFlag(OverflowFromW(alu_out, rn_val, rm_val32, true));
- }
- }
- break;
+ // Format(instr, "add'sf's 'rd, 'rn, 'shift_op");
+ // also, sub, cmp, etc.
+ const bool subtract = instr->Bit(30) == 1;
+ const Register rd = instr->RdField();
+ const Register rn = instr->RnField();
+ const int64_t rm_val = DecodeShiftExtendOperand(instr);
+ if (instr->SFField()) {
+ // 64-bit add.
+ const int64_t rn_val = get_register(rn, instr->RnMode());
+ int64_t alu_out = 0;
+ if (subtract) {
+ alu_out = rn_val - rm_val;
+ } else {
+ alu_out = rn_val + rm_val;
}
- default:
- UnimplementedInstruction(instr);
- break;
+ set_register(rd, alu_out, instr->RdMode());
+ if (instr->HasS()) {
+ SetNZFlagsX(alu_out);
+ SetCFlag(CarryFromX(rn_val, rm_val));
+ SetVFlag(OverflowFromX(alu_out, rn_val, rm_val, !subtract));
+ }
+ } else {
+ // 32-bit add.
+ const int32_t rn_val = get_wregister(rn, instr->RnMode());
+ const int32_t rm_val32 = static_cast<int32_t>(rm_val & kWRegMask);
+ int32_t alu_out = 0;
+ if (subtract) {
+ alu_out = rn_val - rm_val32;
+ } else {
+ alu_out = rn_val + rm_val32;
+ }
+ set_wregister(rd, alu_out, instr->RdMode());
+ if (instr->HasS()) {
+ SetNZFlagsW(alu_out);
+ SetCFlag(CarryFromW(rn_val, rm_val32));
+ SetVFlag(OverflowFromW(alu_out, rn_val, rm_val32, !subtract));
+ }
}
}
@@ -938,11 +1677,156 @@
}
+static int64_t divide64(int64_t top, int64_t bottom, bool signd) {
+ // ARM64 does not trap on integer division by zero. The destination register
+ // is instead set to 0.
+ if (bottom == 0) {
+ return 0;
+ }
+
+ if (signd) {
+ // INT_MIN / -1 = INT_MIN.
+ if ((top == static_cast<int64_t>(0x8000000000000000LL)) &&
+ (bottom == static_cast<int64_t>(0xffffffffffffffffLL))) {
+ return static_cast<int64_t>(0x8000000000000000LL);
+ } else {
+ return top / bottom;
+ }
+ } else {
+ const uint64_t utop = static_cast<uint64_t>(top);
+ const uint64_t ubottom = static_cast<uint64_t>(bottom);
+ return static_cast<int64_t>(utop / ubottom);
+ }
+}
+
+
+static int32_t divide32(int32_t top, int32_t bottom, bool signd) {
+ // ARM64 does not trap on integer division by zero. The destination register
+ // is instead set to 0.
+ if (bottom == 0) {
+ return 0;
+ }
+
+ if (signd) {
+ // INT_MIN / -1 = INT_MIN.
+ if ((top == static_cast<int32_t>(0x80000000)) &&
+ (bottom == static_cast<int32_t>(0xffffffff))) {
+ return static_cast<int32_t>(0x80000000);
+ } else {
+ return top / bottom;
+ }
+ } else {
+ const uint32_t utop = static_cast<uint32_t>(top);
+ const uint32_t ubottom = static_cast<uint32_t>(bottom);
+ return static_cast<int32_t>(utop / ubottom);
+ }
+}
+
+
+void Simulator::DecodeMiscDP2Source(Instr* instr) {
+ if (instr->Bit(29) != 0) {
+ UnimplementedInstruction(instr);
+ }
+
+ const Register rd = instr->RdField();
+ const Register rn = instr->RnField();
+ const Register rm = instr->RmField();
+ const int op = instr->Bits(10, 6);
+ const int64_t rn_val64 = get_register(rn, R31IsZR);
+ const int64_t rm_val64 = get_register(rm, R31IsZR);
+ const int32_t rn_val32 = get_wregister(rn, R31IsZR);
+ const int32_t rm_val32 = get_wregister(rm, R31IsZR);
+ switch (op) {
+ case 2:
+ case 3: {
+ // Format(instr, "udiv'sf 'rd, 'rn, 'rm");
+ // Format(instr, "sdiv'sf 'rd, 'rn, 'rm");
+ const bool signd = instr->Bit(10) == 1;
+ if (instr->SFField() == 1) {
+ set_register(rd, divide64(rn_val64, rm_val64, signd), R31IsZR);
+ } else {
+ set_wregister(rd, divide32(rn_val32, rm_val32, signd), R31IsZR);
+ }
+ break;
+ }
+ case 8: {
+ // Format(instr, "lsl'sf 'rd, 'rn, 'rm");
+ if (instr->SFField() == 1) {
+ const int64_t alu_out = rn_val64 << (rm_val64 & (kXRegSizeInBits - 1));
+ set_register(rd, alu_out, R31IsZR);
+ } else {
+ const int32_t alu_out = rn_val32 << (rm_val32 & (kXRegSizeInBits - 1));
+ set_wregister(rd, alu_out, R31IsZR);
+ }
+ break;
+ }
+ case 9: {
+ // Format(instr, "lsr'sf 'rd, 'rn, 'rm");
+ if (instr->SFField() == 1) {
+ const uint64_t rn_u64 = static_cast<uint64_t>(rn_val64);
+ const int64_t alu_out = rn_u64 >> (rm_val64 & (kXRegSizeInBits - 1));
+ set_register(rd, alu_out, R31IsZR);
+ } else {
+ const uint32_t rn_u32 = static_cast<uint32_t>(rn_val32);
+ const int32_t alu_out = rn_u32 >> (rm_val32 & (kXRegSizeInBits - 1));
+ set_wregister(rd, alu_out, R31IsZR);
+ }
+ break;
+ }
+ case 10: {
+ // Format(instr, "asr'sf 'rd, 'rn, 'rm");
+ if (instr->SFField() == 1) {
+ const int64_t alu_out = rn_val64 >> (rm_val64 & (kXRegSizeInBits - 1));
+ set_register(rd, alu_out, R31IsZR);
+ } else {
+ const int32_t alu_out = rn_val32 >> (rm_val32 & (kXRegSizeInBits - 1));
+ set_wregister(rd, alu_out, R31IsZR);
+ }
+ break;
+ }
+ default:
+ UnimplementedInstruction(instr);
+ break;
+ }
+}
+
+
+void Simulator::DecodeMiscDP3Source(Instr* instr) {
+ if ((instr->Bits(29, 2) == 0) && (instr->Bits(21, 3) == 0) &&
+ (instr->Bit(15) == 0)) {
+ // Format(instr, "madd'sf 'rd, 'rn, 'rm, 'ra");
+ const Register rd = instr->RdField();
+ const Register rn = instr->RnField();
+ const Register rm = instr->RmField();
+ const Register ra = instr->RaField();
+ if (instr->SFField() == 1) {
+ const int64_t rn_val = get_register(rn, R31IsZR);
+ const int64_t rm_val = get_register(rm, R31IsZR);
+ const int64_t ra_val = get_register(ra, R31IsZR);
+ const int64_t alu_out = ra_val + (rn_val * rm_val);
+ set_register(rd, alu_out, R31IsZR);
+ } else {
+ const int32_t rn_val = get_wregister(rn, R31IsZR);
+ const int32_t rm_val = get_wregister(rm, R31IsZR);
+ const int32_t ra_val = get_wregister(ra, R31IsZR);
+ const int32_t alu_out = ra_val + (rn_val * rm_val);
+ set_wregister(rd, alu_out, R31IsZR);
+ }
+ } else {
+ UnimplementedInstruction(instr);
+ }
+}
+
+
void Simulator::DecodeDPRegister(Instr* instr) {
if (instr->IsAddSubShiftExtOp()) {
DecodeAddSubShiftExt(instr);
} else if (instr->IsLogicalShiftOp()) {
DecodeLogicalShift(instr);
+ } else if (instr->IsMiscDP2SourceOp()) {
+ DecodeMiscDP2Source(instr);
+ } else if (instr->IsMiscDP3SourceOp()) {
+ DecodeMiscDP3Source(instr);
} else {
UnimplementedInstruction(instr);
}
@@ -1113,7 +1997,7 @@
set_register(R28, r28_val);
set_register(R29, r29_val);
- // Restore the SP register and return R1:R0.
+ // Restore the SP register and return R0.
set_register(R31, sp_before_call, R31IsSP);
int64_t return_value;
return_value = get_register(R0);
diff --git a/runtime/vm/simulator_arm64.h b/runtime/vm/simulator_arm64.h
index 6c32c1b..1f204f2 100644
--- a/runtime/vm/simulator_arm64.h
+++ b/runtime/vm/simulator_arm64.h
@@ -22,6 +22,7 @@
namespace dart {
class Isolate;
+class SimulatorSetjmpBuffer;
class Simulator {
public:
@@ -35,12 +36,18 @@
static Simulator* Current();
// Accessors for register state.
- void set_register(Register reg, int64_t value, R31Type r31t = R31IsUndef);
- int64_t get_register(Register reg, R31Type r31t = R31IsUndef) const;
- void set_wregister(Register reg, int32_t value, R31Type r31t = R31IsUndef);
- int32_t get_wregister(Register reg, R31Type r31t = R31IsUndef) const;
+ // The default value for R31Type has to be R31IsSP because get_register is
+ // accessed from architecture independent code through SPREG without
+ // specifying the type. We also can't translate a dummy value for SPREG into
+ // a real value because the architecture independent code expects SPREG to
+ // be a real register value.
+ void set_register(Register reg, int64_t value, R31Type r31t = R31IsSP);
+ int64_t get_register(Register reg, R31Type r31t = R31IsSP) const;
+ void set_wregister(Register reg, int32_t value, R31Type r31t = R31IsSP);
+ int32_t get_wregister(Register reg, R31Type r31t = R31IsSP) const;
int64_t get_pc() const;
+ int64_t get_last_pc() const;
void set_pc(int64_t pc);
// Accessor to the internal simulator stack top.
@@ -65,6 +72,18 @@
int64_t parameter2,
int64_t parameter3);
+ // Runtime and native call support.
+ enum CallKind {
+ kRuntimeCall,
+ kLeafRuntimeCall,
+ kLeafFloatRuntimeCall,
+ kBootstrapNativeCall,
+ kNativeCall
+ };
+ static uword RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count);
+
void Longjmp(uword pc,
uword sp,
uword fp,
@@ -91,11 +110,13 @@
bool v_flag_;
// Simulator support.
+ int64_t last_pc_;
int64_t pc_;
char* stack_;
bool pc_modified_;
intptr_t icount_;
static int64_t flag_stop_sim_at_;
+ SimulatorSetjmpBuffer* last_setjmp_buffer_;
uword top_exit_frame_info_;
// Registered breakpoints.
@@ -160,6 +181,10 @@
int64_t DecodeShiftExtendOperand(Instr* instr);
+ bool ConditionallyExecute(Instr* instr);
+
+ void DoRedirectedCall(Instr* instr);
+
// Decode instructions.
void InstructionDecode(Instr* instr);
#define DECODE_OP(op) \
@@ -170,6 +195,16 @@
// Executes ARM64 instructions until the PC reaches kEndSimulatingPC.
void Execute();
+ // Longjmp support for exceptions.
+ SimulatorSetjmpBuffer* last_setjmp_buffer() {
+ return last_setjmp_buffer_;
+ }
+ void set_last_setjmp_buffer(SimulatorSetjmpBuffer* buffer) {
+ last_setjmp_buffer_ = buffer;
+ }
+
+ friend class SimulatorDebugger;
+ friend class SimulatorSetjmpBuffer;
DISALLOW_COPY_AND_ASSIGN(Simulator);
};
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 9fef5bf..4f0a74f 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -32,7 +32,7 @@
static bool IsObjectStoreClassId(intptr_t class_id) {
// Check if this is a class which is stored in the object store.
return (class_id == kObjectCid ||
- (class_id >= kInstanceCid && class_id <= kFloat64x2Cid) ||
+ (class_id >= kInstanceCid && class_id <= kUserTagCid) ||
class_id == kArrayCid ||
class_id == kImmutableArrayCid ||
RawObject::IsStringClassId(class_id) ||
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 327cbeb..22c96cc 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -554,6 +554,7 @@
friend class RawTypeArguments;
friend class RawMirrorReference;
friend class SnapshotWriterVisitor;
+ friend class RawUserTag;
DISALLOW_COPY_AND_ASSIGN(SnapshotWriter);
};
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 01e14b2..7fb5ab0 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -2,9 +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.
-// TODO(zra): Remove when tests are ready to enable.
#include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
#include "include/dart_debugger_api.h"
#include "platform/assert.h"
@@ -836,6 +834,9 @@
};
+// TODO(zra): Remove when tests are ready to enable.
+#if !defined(TARGET_ARCH_ARM64)
+
static void GenerateSourceAndCheck(const Script& script) {
// Check if we are able to generate the source from the token stream.
// Rescan this source and compare the token stream to see if they are
@@ -2718,6 +2719,6 @@
Dart_ExitScope();
}
-} // namespace dart
-
#endif // !defined(TARGET_ARCH_ARM64)
+
+} // namespace dart
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 582d4fa..3915563 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -26,8 +26,30 @@
}
-void StackFrame::Print() const {
- OS::Print("[%-8s : sp(%#" Px ") ]\n", GetName(), sp());
+const char* StackFrame::ToCString() const {
+ Zone* zone = Isolate::Current()->current_zone();
+ if (IsDartFrame()) {
+ const Code& code = Code::Handle(LookupDartCode());
+ ASSERT(!code.IsNull());
+ const Object& owner = Object::Handle(code.owner());
+ ASSERT(!owner.IsNull());
+ if (owner.IsFunction()) {
+ const Function& function = Function::Cast(owner);
+ return zone->PrintToString(
+ "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ") %s ]",
+ GetName(), sp(), fp(), pc(),
+ function.ToFullyQualifiedCString());
+ } else {
+ return zone->PrintToString(
+ "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ") %s ]",
+ GetName(), sp(), fp(), pc(),
+ owner.ToCString());
+ }
+ } else {
+ return zone->PrintToString(
+ "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ")]",
+ GetName(), sp(), fp(), pc());
+ }
}
diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h
index 2727eee..5b4d6b4 100644
--- a/runtime/vm/stack_frame.h
+++ b/runtime/vm/stack_frame.h
@@ -56,8 +56,7 @@
// Visit objects in the frame.
virtual void VisitObjectPointers(ObjectPointerVisitor* visitor);
- // Print a frame.
- virtual void Print() const;
+ const char* ToCString() const;
// Check validity of a frame, used for assertion purposes.
virtual bool IsValid() const;
@@ -241,7 +240,9 @@
public:
DartFrameIterator() : frames_(StackFrameIterator::kDontValidateFrames) { }
explicit DartFrameIterator(uword last_fp)
- : frames_(last_fp, StackFrameIterator::kDontValidateFrames) {}
+ : frames_(last_fp, StackFrameIterator::kDontValidateFrames) { }
+ DartFrameIterator(uword fp, uword sp, uword pc)
+ : frames_(fp, sp, pc, StackFrameIterator::kDontValidateFrames) { }
// Get next dart frame.
StackFrame* NextFrame() {
StackFrame* frame = frames_.NextFrame();
diff --git a/runtime/vm/stack_frame_arm64.h b/runtime/vm/stack_frame_arm64.h
index 49c7d29..b518746 100644
--- a/runtime/vm/stack_frame_arm64.h
+++ b/runtime/vm/stack_frame_arm64.h
@@ -7,27 +7,44 @@
namespace dart {
-// TODO(zra):
-// These are the values for ARM. Fill in the values for ARM64 as they are
-// needed.
+/* ARM64 Dart Frame Layout
+ | | <- TOS
+Callee frame | ... |
+ | saved PP |
+ | callee's PC marker |
+ | saved FP | (FP of current frame)
+ | saved PC | (PC of current frame)
+ +--------------------+
+Current frame | ... T| <- SP of current frame
+ | first local T|
+ | caller's PP T|
+ | PC marker | (current frame's code entry + offset)
+ | caller's FP | <- FP of current frame
+ | caller's LR | (PC of caller frame)
+ +--------------------+
+Caller frame | last parameter | <- SP of caller frame
+ | ... |
+
+ T against a slot indicates it needs to be traversed during GC.
+*/
static const int kDartFrameFixedSize = 4; // PP, FP, LR, PC marker.
-static const int kSavedPcSlotFromSp = -2;
+static const int kSavedPcSlotFromSp = -1;
-static const int kFirstObjectSlotFromFp = -1; // Used by GC to traverse stack.
+static const int kFirstObjectSlotFromFp = -2; // Used by GC to traverse stack.
-static const int kFirstLocalSlotFromFp = -2;
-static const int kSavedCallerPpSlotFromFp = -1;
+static const int kFirstLocalSlotFromFp = -3;
+static const int kSavedCallerPpSlotFromFp = -2;
static const int kSavedCallerFpSlotFromFp = 0;
static const int kSavedCallerPcSlotFromFp = 1;
-static const int kPcMarkerSlotFromFp = 2;
-static const int kParamEndSlotFromFp = 2; // One slot past last parameter.
-static const int kCallerSpSlotFromFp = 3;
+static const int kPcMarkerSlotFromFp = -1;
+static const int kParamEndSlotFromFp = 1; // One slot past last parameter.
+static const int kCallerSpSlotFromFp = 2;
// Entry and exit frame layout.
-static const int kSavedContextSlotFromEntryFp = -27;
-static const int kExitLinkSlotFromEntryFp = -26;
-static const int kSavedVMTagSlotFromEntryFp = -25;
+static const int kSavedContextSlotFromEntryFp = -14;
+static const int kExitLinkSlotFromEntryFp = -13;
+static const int kSavedVMTagSlotFromEntryFp = -12;
} // namespace dart
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index fd297de..3047ab5 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -51,12 +51,9 @@
void StubCode::InitOnce() {
- // TODO(zra): ifndef to be removed when ARM64 port is ready.
-#if !defined(TARGET_ARCH_ARM64)
// Generate all the stubs.
Code& code = Code::Handle();
VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
-#endif
}
@@ -70,12 +67,9 @@
void StubCode::Init(Isolate* isolate) {
- // TODO(zra): ifndef to be removed when ARM64 port is ready.
-#if !defined(TARGET_ARCH_ARM64)
StubCode* stubs = new StubCode();
isolate->set_stub_code(stubs);
stubs->GenerateFor(isolate);
-#endif
}
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index 19f41a2..17acb6d 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -28,7 +28,6 @@
V(AllocateArray) \
V(CallNoSuchMethodFunction) \
V(CallStaticFunction) \
- V(CallClosureFunction) \
V(FixCallersTarget) \
V(Deoptimize) \
V(DeoptimizeLazy) \
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index ae9785e..25d1270 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -70,8 +70,7 @@
#endif
// Mark that the isolate is executing VM code.
- __ LoadImmediate(R6, VMTag::kVMTagId);
- __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+ __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
// Reserve space for arguments and align frame before entering C++ world.
// NativeArguments are passed in registers.
@@ -185,8 +184,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ LoadImmediate(R6, VMTag::kRuntimeNativeTagId);
- __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+ __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
// Reserve space for the native arguments structure passed on the stack (the
// outgoing pointer parameter to the native arguments structure is passed in
@@ -307,8 +305,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ LoadImmediate(R6, VMTag::kRuntimeNativeTagId);
- __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+ __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
// Reserve space for the native arguments structure passed on the stack (the
// outgoing pointer parameter to the native arguments structure is passed in
@@ -729,83 +726,6 @@
}
-// Input parameters:
-// LR: return address.
-// SP: address of last argument.
-// R4: arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-// called, the stub accesses the closure from this location directly
-// when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- // Load num_args.
- __ ldr(R0, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
- __ sub(R0, R0, ShifterOperand(Smi::RawValue(1)));
- // Load closure object in R1.
- __ ldr(R1, Address(SP, R0, LSL, 1)); // R0 (num_args - 1) is a Smi.
-
- // Verify that R1 is a closure by checking its class.
- Label not_closure;
- __ LoadImmediate(R8, reinterpret_cast<intptr_t>(Object::null()));
- __ cmp(R1, ShifterOperand(R8));
- // Not a closure, but null object.
- __ b(¬_closure, EQ);
- __ tst(R1, ShifterOperand(kSmiTagMask));
- __ b(¬_closure, EQ); // Not a closure, but a smi.
- // Verify that the class of the object is a closure class by checking that
- // class.signature_function() is not null.
- __ LoadClass(R0, R1, R2);
- __ ldr(R0, FieldAddress(R0, Class::signature_function_offset()));
- __ cmp(R0, ShifterOperand(R8)); // R8 is raw null.
- // Actual class is not a closure class.
- __ b(¬_closure, EQ);
-
- // R0 is just the signature function. Load the actual closure function.
- __ ldr(R0, FieldAddress(R1, Closure::function_offset()));
-
- // Load closure context in CTX; note that CTX has already been preserved.
- __ ldr(CTX, FieldAddress(R1, Closure::context_offset()));
-
- // R4: Arguments descriptor.
- // R0: Function.
- __ ldr(R2, FieldAddress(R0, Function::code_offset()));
-
- // R2: code.
- // R5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
- __ LoadImmediate(R5, 0);
- __ ldr(R2, FieldAddress(R2, Code::instructions_offset()));
- __ AddImmediate(R2, Instructions::HeaderSize() - kHeapObjectTag);
- __ bx(R2);
-
- __ Bind(¬_closure);
- // Call runtime to attempt to resolve and invoke a call method on a
- // non-closure object, passing the non-closure object and its arguments array,
- // returning here.
- // If no call method exists, throw a NoSuchMethodError.
- // R1: non-closure object.
- // R4: arguments descriptor array.
-
- // Create a stub frame as we are pushing some objects on the stack before
- // calling into the runtime.
- __ EnterStubFrame();
-
- // Setup space on stack for result from error reporting.
- __ PushList((1 << R4) | (1 << R8)); // Arguments descriptor and raw null.
-
- // Load smi-tagged arguments array length, including the non-closure.
- __ ldr(R2, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
- PushArgumentsArray(assembler);
-
- __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
- // Remove arguments.
- __ Drop(2);
- __ Pop(R0); // Get result into R0.
-
- // Remove the stub frame as we are about to return.
- __ LeaveStubFrame();
- __ Ret();
-}
-
-
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// LR : points to return address.
diff --git a/runtime/vm/stub_code_arm64.cc b/runtime/vm/stub_code_arm64.cc
index 168e5ea..7d601c4 100644
--- a/runtime/vm/stub_code_arm64.cc
+++ b/runtime/vm/stub_code_arm64.cc
@@ -21,95 +21,333 @@
namespace dart {
+// Input parameters:
+// LR : return address.
+// SP : address of last argument in argument array.
+// SP + 8*R4 - 8 : address of first argument in argument array.
+// SP + 8*R4 : address of return value.
+// R5 : address of the runtime function to call.
+// R4 : number of arguments to the call.
void StubCode::GenerateCallToRuntimeStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ const intptr_t isolate_offset = NativeArguments::isolate_offset();
+ const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
+ const intptr_t argv_offset = NativeArguments::argv_offset();
+ const intptr_t retval_offset = NativeArguments::retval_offset();
+ const intptr_t exitframe_last_param_slot_from_fp = 1;
+
+ __ SetPrologueOffset();
+ __ Comment("CallToRuntimeStub");
+ __ EnterFrame(0);
+
+ // Load current Isolate pointer from Context structure into A0.
+ __ LoadFieldFromOffset(R0, CTX, Context::isolate_offset());
+
+ // Save exit frame information to enable stack walking as we are about
+ // to transition to Dart VM C++ code.
+ __ StoreToOffset(SP, R0, Isolate::top_exit_frame_info_offset());
+
+ // Save current Context pointer into Isolate structure.
+ __ StoreToOffset(CTX, R0, Isolate::top_context_offset());
+
+ // Cache Isolate pointer into CTX while executing runtime code.
+ __ mov(CTX, R0);
+
+#if defined(DEBUG)
+ { Label ok;
+ // Check that we are always entering from Dart code.
+ __ LoadFromOffset(R8, R0, Isolate::vm_tag_offset());
+ __ CompareImmediate(R8, VMTag::kScriptTagId, kNoRegister);
+ __ b(&ok, EQ);
+ __ Stop("Not coming from Dart code.");
+ __ Bind(&ok);
+ }
+#endif
+
+ // Mark that the isolate is executing VM code.
+ __ StoreToOffset(R5, R0, Isolate::vm_tag_offset());
+
+ // Reserve space for arguments and align frame before entering C++ world.
+ // NativeArguments are passed in registers.
+ __ Comment("align stack");
+ ASSERT(sizeof(NativeArguments) == 4 * kWordSize);
+ __ ReserveAlignedFrameSpace(4 * kWordSize); // Reserve space for arguments.
+
+ // Pass NativeArguments structure by value and call runtime.
+ // Registers R0, R1, R2, and R3 are used.
+
+ ASSERT(isolate_offset == 0 * kWordSize);
+ // Set isolate in NativeArgs: R0 already contains CTX.
+
+ // There are no runtime calls to closures, so we do not need to set the tag
+ // bits kClosureFunctionBit and kInstanceFunctionBit in argc_tag_.
+ ASSERT(argc_tag_offset == 1 * kWordSize);
+ __ mov(R1, R4); // Set argc in NativeArguments.
+
+ ASSERT(argv_offset == 2 * kWordSize);
+ __ add(R2, ZR, Operand(R4, LSL, 3));
+ __ add(R2, FP, Operand(R2)); // Compute argv.
+ // Set argv in NativeArguments.
+ __ AddImmediate(R2, R2, exitframe_last_param_slot_from_fp * kWordSize,
+ kNoRegister);
+
+ ASSERT(retval_offset == 3 * kWordSize);
+ __ AddImmediate(R3, R2, kWordSize, kNoRegister);
+
+ // TODO(zra): Check that the ABI allows calling through this register.
+ __ blr(R5);
+
+ // Retval is next to 1st argument.
+ __ Comment("CallToRuntimeStub return");
+
+ // Mark that the isolate is executing Dart code.
+ __ LoadImmediate(R2, VMTag::kScriptTagId, kNoRegister);
+ __ StoreToOffset(R2, CTX, Isolate::vm_tag_offset());
+
+ // Reset exit frame information in Isolate structure.
+ __ StoreToOffset(ZR, CTX, Isolate::top_exit_frame_info_offset());
+
+ // Load Context pointer from Isolate structure into A2.
+ __ LoadFromOffset(R2, CTX, Isolate::top_context_offset());
+
+ // Load null.
+ __ LoadObject(TMP, Object::null_object(), PP);
+
+ // Reset Context pointer in Isolate structure.
+ __ StoreToOffset(TMP, CTX, Isolate::top_context_offset());
+
+ // Cache Context pointer into CTX while executing Dart code.
+ __ mov(CTX, R2);
+
+ __ LeaveFrame();
+ __ ret();
}
void StubCode::GeneratePrintStopMessageStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GeneratePrintStopMessageStub");
}
void StubCode::GenerateCallNativeCFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateCallNativeCFunctionStub");
}
void StubCode::GenerateCallBootstrapCFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateCallBootstrapCFunctionStub");
}
void StubCode::GenerateCallStaticFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateCallStaticFunctionStub");
}
void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateFixCallersTargetStub");
}
void StubCode::GenerateDeoptimizeLazyStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateDeoptimizeLazyStub");
}
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateDeoptimizeStub");
}
void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateMegamorphicMissStub");
}
void StubCode::GenerateAllocateArrayStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateAllocateArrayStub");
}
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
-}
-
-
+// Called when invoking Dart code from C++ (VM code).
+// Input parameters:
+// LR : points to return address.
+// R0 : entrypoint of the Dart function to call.
+// R1 : arguments descriptor array.
+// R2 : arguments array.
+// R3 : new context containing the current isolate pointer.
void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Comment("InvokeDartCodeStub");
+ __ EnterFrame(0);
+
+ // The new context, saved vm tag, the top exit frame, and the old context.
+ // const intptr_t kPreservedContextSlots = 4;
+ const intptr_t kNewContextOffsetFromFp =
+ -(1 + kAbiPreservedCpuRegCount) * kWordSize;
+ // const intptr_t kPreservedRegSpace =
+ // kWordSize * (kAbiPreservedCpuRegCount + kPreservedContextSlots);
+
+ // Save the callee-saved registers.
+ for (int i = R19; i <= R28; i++) {
+ const Register r = static_cast<Register>(i);
+ // We use str instead of the Push macro because we will be pushing the PP
+ // register when it is not holding a pool-pointer since we are coming from
+ // C++ code.
+ __ str(r, Address(SP, -1 * kWordSize, Address::PreIndex));
+ }
+
+ // TODO(zra): Save the bottom 64-bits of callee-saved floating point
+ // registers.
+
+ // Push new context.
+ __ Push(R3);
+
+ // We now load the pool pointer(PP) as we are about to invoke dart code and we
+ // could potentially invoke some intrinsic functions which need the PP to be
+ // set up.
+ __ LoadPoolPointer(PP);
+
+ // The new Context structure contains a pointer to the current Isolate
+ // structure. Cache the Context pointer in the CTX register so that it is
+ // available in generated code and calls to Isolate::Current() need not be
+ // done. The assumption is that this register will never be clobbered by
+ // compiled or runtime stub code.
+
+ // Cache the new Context pointer into CTX while executing Dart code.
+ __ LoadFromOffset(CTX, R3, VMHandles::kOffsetOfRawPtrInHandle);
+
+ // Load Isolate pointer from Context structure into temporary register R4.
+ __ LoadFieldFromOffset(R5, CTX, Context::isolate_offset());
+
+ // Save the current VMTag on the stack.
+ ASSERT(kSavedVMTagSlotFromEntryFp == -12);
+ __ LoadFromOffset(R4, R5, Isolate::vm_tag_offset());
+ __ Push(R4);
+
+ // Mark that the isolate is executing Dart code.
+ __ LoadImmediate(R6, VMTag::kScriptTagId, PP);
+ __ StoreToOffset(R6, R5, Isolate::vm_tag_offset());
+
+ // Save the top exit frame info. Use R6 as a temporary register.
+ // StackFrameIterator reads the top exit frame info saved in this frame.
+ __ LoadFromOffset(R6, R5, Isolate::top_exit_frame_info_offset());
+ __ StoreToOffset(ZR, R5, Isolate::top_exit_frame_info_offset());
+
+ // Save the old Context pointer. Use R4 as a temporary register.
+ // Note that VisitObjectPointers will find this saved Context pointer during
+ // GC marking, since it traverses any information between SP and
+ // FP - kExitLinkSlotFromEntryFp.
+ // EntryFrame::SavedContext reads the context saved in this frame.
+ __ LoadFromOffset(R4, R5, Isolate::top_context_offset());
+
+ // The constants kSavedContextSlotFromEntryFp and
+ // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
+ ASSERT(kExitLinkSlotFromEntryFp == -13);
+ ASSERT(kSavedContextSlotFromEntryFp == -14);
+ __ Push(R6);
+ __ Push(R4);
+
+ // Load arguments descriptor array into R4, which is passed to Dart code.
+ __ LoadFromOffset(R4, R1, VMHandles::kOffsetOfRawPtrInHandle);
+
+ // Load number of arguments into S5.
+ __ LoadFieldFromOffset(R5, R4, ArgumentsDescriptor::count_offset());
+ __ SmiUntag(R5);
+
+ // Compute address of 'arguments array' data area into R2.
+ __ LoadFromOffset(R2, R2, VMHandles::kOffsetOfRawPtrInHandle);
+ __ AddImmediate(R2, R2, Array::data_offset() - kHeapObjectTag, PP);
+
+ // Set up arguments for the Dart call.
+ Label push_arguments;
+ Label done_push_arguments;
+ __ cmp(R5, Operand(0));
+ __ b(&done_push_arguments, EQ); // check if there are arguments.
+ __ LoadImmediate(R1, 0, PP);
+ __ Bind(&push_arguments);
+ __ ldr(R3, Address(R2));
+ __ Push(R3);
+ __ add(R1, R1, Operand(1));
+ __ add(R2, R2, Operand(kWordSize));
+ __ cmp(R1, Operand(R5));
+ __ b(&push_arguments, LT);
+ __ Bind(&done_push_arguments);
+
+ // Call the Dart code entrypoint.
+ __ blr(R0); // R4 is the arguments descriptor array.
+ __ Comment("InvokeDartCodeStub return");
+
+ // Read the saved new Context pointer.
+ __ LoadFromOffset(CTX, FP, kNewContextOffsetFromFp);
+ __ LoadFromOffset(CTX, CTX, VMHandles::kOffsetOfRawPtrInHandle);
+
+ // Get rid of arguments pushed on the stack.
+ __ AddImmediate(SP, FP, kSavedContextSlotFromEntryFp * kWordSize, PP);
+
+ // Load Isolate pointer from Context structure into CTX. Drop Context.
+ __ LoadFieldFromOffset(CTX, CTX, Context::isolate_offset());
+
+ // Restore the current VMTag from the stack.
+ __ ldr(R4, Address(SP, 2 * kWordSize));
+ __ StoreToOffset(R4, CTX, Isolate::vm_tag_offset());
+
+ // Restore the saved Context pointer into the Isolate structure.
+ // Uses R4 as a temporary register for this.
+ // Restore the saved top exit frame info back into the Isolate structure.
+ // Uses R6 as a temporary register for this.
+ __ Pop(R4);
+ __ Pop(R6);
+ __ StoreToOffset(R4, CTX, Isolate::top_context_offset());
+ __ StoreToOffset(R6, CTX, Isolate::top_exit_frame_info_offset());
+
+ __ Pop(R3);
+ __ Pop(R4);
+
+ // Restore C++ ABI callee-saved registers.
+ for (int i = R28; i >= R19; i--) {
+ Register r = static_cast<Register>(i);
+ // We use ldr instead of the Pop macro because we will be popping the PP
+ // register when it is not holding a pool-pointer since we are returning to
+ // C++ code.
+ __ ldr(r, Address(SP, 1 * kWordSize, Address::PostIndex));
+ }
+
+ // TODO(zra): Restore callee-saved fpu registers.
+
+ // Restore the frame pointer and return.
+ __ LeaveFrame();
+ __ ret();
}
void StubCode::GenerateAllocateContextStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateAllocateContextStub");
}
void StubCode::GenerateUpdateStoreBufferStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateUpdateStoreBufferStub");
}
void StubCode::GenerateAllocationStubForClass(Assembler* assembler,
const Class& cls) {
- UNIMPLEMENTED();
+ __ Stop("GenerateAllocationStubForClass");
}
void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateCallNoSuchMethodFunctionStub");
}
void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateOptimizedUsageCounterIncrement");
}
void StubCode::GenerateUsageCounterIncrement(Assembler* assembler,
Register temp_reg) {
- UNIMPLEMENTED();
+ __ Stop("GenerateUsageCounterIncrement");
}
@@ -117,105 +355,105 @@
Assembler* assembler,
intptr_t num_args,
const RuntimeEntry& handle_ic_miss) {
- UNIMPLEMENTED();
+ __ Stop("GenerateNArgsCheckInlineCacheStub");
}
void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateOneArgCheckInlineCacheStub");
}
void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateTwoArgsCheckInlineCacheStub");
}
void StubCode::GenerateThreeArgsCheckInlineCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateThreeArgsCheckInlineCacheStub");
}
void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub(
Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateOneArgOptimizedCheckInlineCacheStub");
}
void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub(
Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateTwoArgsOptimizedCheckInlineCacheStub");
}
void StubCode::GenerateThreeArgsOptimizedCheckInlineCacheStub(
Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateThreeArgsOptimizedCheckInlineCacheStub");
}
void StubCode::GenerateClosureCallInlineCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateClosureCallInlineCacheStub");
}
void StubCode::GenerateMegamorphicCallStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateMegamorphicCallStub");
}
void StubCode::GenerateZeroArgsUnoptimizedStaticCallStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateZeroArgsUnoptimizedStaticCallStub");
}
void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateTwoArgsUnoptimizedStaticCallStub");
}
void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateLazyCompileStub");
}
void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateBreakpointRuntimeStub");
}
void StubCode::GenerateDebugStepCheckStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateDebugStepCheckStub");
}
void StubCode::GenerateSubtype1TestCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateSubtype1TestCacheStub");
}
void StubCode::GenerateSubtype2TestCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateSubtype2TestCacheStub");
}
void StubCode::GenerateSubtype3TestCacheStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateSubtype3TestCacheStub");
}
void StubCode::GenerateGetStackPointerStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateGetStackPointerStub");
}
void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateJumpToExceptionHandlerStub");
}
void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateOptimizeFunctionStub");
}
@@ -224,19 +462,19 @@
const Register right,
const Register temp,
const Register unused) {
- UNIMPLEMENTED();
+ __ Stop("GenerateIdenticalWithNumberCheckStub");
}
void StubCode::GenerateUnoptimizedIdenticalWithNumberCheckStub(
Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateUnoptimizedIdenticalWithNumberCheckStub");
}
void StubCode::GenerateOptimizedIdenticalWithNumberCheckStub(
Assembler* assembler) {
- UNIMPLEMENTED();
+ __ Stop("GenerateOptimizedIdenticalWithNumberCheckStub");
}
} // namespace dart
diff --git a/runtime/vm/stub_code_arm64_test.cc b/runtime/vm/stub_code_arm64_test.cc
index 7c2fe8f..fa12a56 100644
--- a/runtime/vm/stub_code_arm64_test.cc
+++ b/runtime/vm/stub_code_arm64_test.cc
@@ -5,6 +5,111 @@
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM64)
-// TODO(zra): Port these tests.
+#include "vm/isolate.h"
+#include "vm/dart_entry.h"
+#include "vm/native_entry.h"
+#include "vm/native_entry_test.h"
+#include "vm/object.h"
+#include "vm/runtime_entry.h"
+#include "vm/stub_code.h"
+#include "vm/symbols.h"
+#include "vm/unit_test.h"
+
+#define __ assembler->
+
+namespace dart {
+
+DECLARE_RUNTIME_ENTRY(TestSmiSub);
+DECLARE_LEAF_RUNTIME_ENTRY(RawObject*, TestLeafSmiAdd, RawObject*, RawObject*);
+
+
+static Function* CreateFunction(const char* name) {
+ const String& class_name = String::Handle(Symbols::New("ownerClass"));
+ const Script& script = Script::Handle();
+ const Class& owner_class =
+ Class::Handle(Class::New(class_name, script, Scanner::kNoSourcePos));
+ const Library& lib = Library::Handle(Library::New(class_name));
+ owner_class.set_library(lib);
+ const String& function_name = String::ZoneHandle(Symbols::New(name));
+ Function& function = Function::ZoneHandle(
+ Function::New(function_name, RawFunction::kRegularFunction,
+ true, false, false, false, false, owner_class, 0));
+ return &function;
+}
+
+
+// Test calls to stub code which calls into the runtime.
+static void GenerateCallToCallRuntimeStub(Assembler* assembler,
+ int value1, int value2) {
+ const int argc = 2;
+ const Smi& smi1 = Smi::ZoneHandle(Smi::New(value1));
+ const Smi& smi2 = Smi::ZoneHandle(Smi::New(value2));
+ const Object& result = Object::ZoneHandle();
+ const Context& context = Context::ZoneHandle(Context::New(0, Heap::kOld));
+ ASSERT(context.isolate() == Isolate::Current());
+ __ EnterDartFrame(0);
+ __ LoadObject(CTX, context, PP);
+ __ PushObject(result, PP); // Push Null object for return value.
+ __ PushObject(smi1, PP); // Push argument 1 smi1.
+ __ PushObject(smi2, PP); // Push argument 2 smi2.
+ ASSERT(kTestSmiSubRuntimeEntry.argument_count() == argc);
+ __ CallRuntime(kTestSmiSubRuntimeEntry, argc); // Call SmiSub runtime func.
+ __ add(SP, SP, Operand(argc * kWordSize));
+ __ Pop(R0); // Pop return value from return slot.
+ __ LeaveDartFrame();
+ __ ret();
+}
+
+
+TEST_CASE(CallRuntimeStubCode) {
+ extern const Function& RegisterFakeFunction(const char* name,
+ const Code& code);
+ const int value1 = 10;
+ const int value2 = 20;
+ const char* kName = "Test_CallRuntimeStubCode";
+ Assembler _assembler_;
+ GenerateCallToCallRuntimeStub(&_assembler_, value1, value2);
+ const Code& code = Code::Handle(Code::FinalizeCode(
+ *CreateFunction("Test_CallRuntimeStubCode"), &_assembler_));
+ const Function& function = RegisterFakeFunction(kName, code);
+ Smi& result = Smi::Handle();
+ result ^= DartEntry::InvokeFunction(function, Object::empty_array());
+ EXPECT_EQ((value1 - value2), result.Value());
+}
+
+
+// Test calls to stub code which calls into a leaf runtime entry.
+static void GenerateCallToCallLeafRuntimeStub(Assembler* assembler,
+ int value1,
+ int value2) {
+ const Smi& smi1 = Smi::ZoneHandle(Smi::New(value1));
+ const Smi& smi2 = Smi::ZoneHandle(Smi::New(value2));
+ __ EnterDartFrame(0);
+ __ ReserveAlignedFrameSpace(0);
+ __ LoadObject(R0, smi1, PP); // Set up argument 1 smi1.
+ __ LoadObject(R1, smi2, PP); // Set up argument 2 smi2.
+ __ CallRuntime(kTestLeafSmiAddRuntimeEntry, 2); // Call SmiAdd runtime func.
+ __ LeaveDartFrame();
+ __ ret(); // Return value is in R0.
+}
+
+
+TEST_CASE(CallLeafRuntimeStubCode) {
+ extern const Function& RegisterFakeFunction(const char* name,
+ const Code& code);
+ const int value1 = 10;
+ const int value2 = 20;
+ const char* kName = "Test_CallLeafRuntimeStubCode";
+ Assembler _assembler_;
+ GenerateCallToCallLeafRuntimeStub(&_assembler_, value1, value2);
+ const Code& code = Code::Handle(Code::FinalizeCode(
+ *CreateFunction("Test_CallLeafRuntimeStubCode"), &_assembler_));
+ const Function& function = RegisterFakeFunction(kName, code);
+ Smi& result = Smi::Handle();
+ result ^= DartEntry::InvokeFunction(function, Object::empty_array());
+ EXPECT_EQ((value1 + value2), result.Value());
+}
+
+} // namespace dart
#endif // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 2c1b247..82acef6 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -27,6 +27,7 @@
DEFINE_FLAG(bool, use_slow_path, false,
"Set to true for debugging & verifying the slow paths.");
DECLARE_FLAG(bool, trace_optimized_ic_calls);
+DEFINE_FLAG(bool, verify_incoming_contexts, false, "");
// Input parameters:
@@ -52,6 +53,19 @@
// to transition to Dart VM C++ code.
__ movl(Address(EAX, Isolate::top_exit_frame_info_offset()), ESP);
+#if defined(DEBUG)
+ if (FLAG_verify_incoming_contexts) {
+ Label ok;
+ // Check that the isolate's saved ctx is null.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ cmpl(Address(EAX, Isolate::top_context_offset()), raw_null);
+ __ j(EQUAL, &ok, Assembler::kNearJump);
+ __ Stop("Found non-null incoming top context: call to runtime stub");
+ __ Bind(&ok);
+ }
+#endif
+
// Save current Context pointer into Isolate structure.
__ movl(Address(EAX, Isolate::top_context_offset()), CTX);
@@ -70,7 +84,7 @@
#endif
// Mark that the isolate is executing VM code.
- __ movl(Address(CTX, Isolate::vm_tag_offset()), Immediate(VMTag::kVMTagId));
+ __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
// Reserve space for arguments and align frame before entering C++ world.
__ AddImmediate(ESP, Immediate(-sizeof(NativeArguments)));
@@ -160,6 +174,20 @@
// to transition to dart VM code.
__ movl(Address(EDI, Isolate::top_exit_frame_info_offset()), ESP);
+#if defined(DEBUG)
+ if (FLAG_verify_incoming_contexts) {
+ Label ok;
+ // Check that the isolate's saved ctx is null.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ cmpl(Address(EDI, Isolate::top_context_offset()), raw_null);
+ __ j(EQUAL, &ok, Assembler::kNearJump);
+ __ Stop("Found non-null incoming top context: "
+ "call to native c function stub");
+ __ Bind(&ok);
+ }
+#endif
+
// Save current Context pointer into Isolate structure.
__ movl(Address(EDI, Isolate::top_context_offset()), CTX);
@@ -178,8 +206,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ movl(Address(CTX, Isolate::vm_tag_offset()),
- Immediate(VMTag::kRuntimeNativeTagId));
+ __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
// Reserve space for the native arguments structure, the outgoing parameters
// (pointer to the native arguments structure, the C function entry point)
@@ -260,6 +287,20 @@
// to transition to dart VM code.
__ movl(Address(EDI, Isolate::top_exit_frame_info_offset()), ESP);
+#if defined(DEBUG)
+ if (FLAG_verify_incoming_contexts) {
+ Label ok;
+ // Check that the isolate's saved ctx is null.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ cmpl(Address(EDI, Isolate::top_context_offset()), raw_null);
+ __ j(EQUAL, &ok, Assembler::kNearJump);
+ __ Stop("Found non-null incoming top context: "
+ "call to bootstrap c function stub");
+ __ Bind(&ok);
+ }
+#endif
+
// Save current Context pointer into Isolate structure.
__ movl(Address(EDI, Isolate::top_context_offset()), CTX);
@@ -278,8 +319,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ movl(Address(CTX, Isolate::vm_tag_offset()),
- Immediate(VMTag::kRuntimeNativeTagId));
+ __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
// Reserve space for the native arguments structure, the outgoing parameter
// (pointer to the native arguments structure) and align frame before
@@ -700,82 +740,6 @@
}
-// Input parameters:
-// EDX: Arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-// called, the stub accesses the closure from this location directly
-// when trying to resolve the call.
-// Uses EDI.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
-
- // Load num_args.
- __ movl(EAX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
- // Load closure object in EDI.
- __ movl(EDI, Address(ESP, EAX, TIMES_2, 0)); // EAX is a Smi.
-
- // Verify that EDI is a closure by checking its class.
- Label not_closure;
- __ cmpl(EDI, raw_null);
- // Not a closure, but null object.
- __ j(EQUAL, ¬_closure, Assembler::kNearJump);
- __ testl(EDI, Immediate(kSmiTagMask));
- __ j(ZERO, ¬_closure, Assembler::kNearJump); // Not a closure, but a smi.
- // Verify that the class of the object is a closure class by checking that
- // class.signature_function() is not null.
- __ LoadClass(EAX, EDI, ECX);
- __ movl(EAX, FieldAddress(EAX, Class::signature_function_offset()));
- __ cmpl(EAX, raw_null);
- // Actual class is not a closure class.
- __ j(EQUAL, ¬_closure, Assembler::kNearJump);
-
- // EAX is just the signature function. Load the actual closure function.
- __ movl(EAX, FieldAddress(EDI, Closure::function_offset()));
-
- // Load closure context in CTX; note that CTX has already been preserved.
- __ movl(CTX, FieldAddress(EDI, Closure::context_offset()));
-
- // EBX: Code (compiled code or lazy compile stub).
- __ movl(EBX, FieldAddress(EAX, Function::code_offset()));
-
- // EAX: Function.
- // EDX: Arguments descriptor array.
- // ECX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
- __ xorl(ECX, ECX);
- __ movl(EBX, FieldAddress(EBX, Code::instructions_offset()));
- __ addl(EBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
- __ jmp(EBX);
-
- __ Bind(¬_closure);
- // Call runtime to attempt to resolve and invoke a call method on a
- // non-closure object, passing the non-closure object and its arguments array,
- // returning here.
- // If no call method exists, throw a NoSuchMethodError.
- // EDI: non-closure object.
- // EDX: arguments descriptor array.
-
- // Create a stub frame as we are pushing some objects on the stack before
- // calling into the runtime.
- __ EnterStubFrame();
-
- __ pushl(raw_null); // Setup space on stack for result from error reporting.
- __ pushl(EDX); // Arguments descriptor.
- // Load smi-tagged arguments array length, including the non-closure.
- __ movl(EDX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
- PushArgumentsArray(assembler);
-
- __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
- // Remove arguments.
- __ Drop(2);
- __ popl(EAX); // Get result into EAX.
-
- // Remove the stub frame as we are about to return.
- __ LeaveFrame();
- __ ret();
-}
-
-
// Called when invoking dart code from C++ (VM code).
// Input parameters:
// ESP : points to return address.
@@ -840,6 +804,18 @@
__ movl(ECX, Address(EDI, Isolate::top_context_offset()));
__ pushl(ECX);
+ // TODO(turnidge): This code should probably be emitted all the time
+ // on all architectures but I am leaving it under DEBUG/flag for
+ // now.
+#if defined(DEBUG)
+ if (FLAG_verify_incoming_contexts) {
+ // Clear Context pointer in Isolate structure.
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ movl(Address(EDI, Isolate::top_context_offset()), raw_null);
+ }
+#endif
+
// Load arguments descriptor array into EDX.
__ movl(EDX, Address(EBP, kArgumentsDescOffset));
__ movl(EDX, Address(EDX, VMHandles::kOffsetOfRawPtrInHandle));
diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc
index aec9159..dfe0601 100644
--- a/runtime/vm/stub_code_mips.cc
+++ b/runtime/vm/stub_code_mips.cc
@@ -73,8 +73,7 @@
#endif
// Mark that the isolate is executing VM code.
- __ LoadImmediate(T0, VMTag::kVMTagId);
- __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+ __ sw(S5, Address(A0, Isolate::vm_tag_offset()));
// Reserve space for arguments and align frame before entering C++ world.
// NativeArguments are passed in registers.
@@ -200,8 +199,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ LoadImmediate(T0, VMTag::kRuntimeNativeTagId);
- __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+ __ sw(T5, Address(A0, Isolate::vm_tag_offset()));
// Initialize NativeArguments structure and call native function.
// Registers A0, A1, A2, and A3 are used.
@@ -334,8 +332,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ LoadImmediate(T0, VMTag::kRuntimeNativeTagId);
- __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+ __ sw(T5, Address(A0, Isolate::vm_tag_offset()));
// Initialize NativeArguments structure and call native function.
// Registers A0, A1, A2, and A3 are used.
@@ -826,100 +823,6 @@
}
-// Input parameters:
-// RA: return address.
-// SP: address of last argument.
-// S4: Arguments descriptor array.
-// Return: V0.
-// Note: The closure object is the first argument to the function being
-// called, the stub accesses the closure from this location directly
-// when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- // Load num_args.
- __ TraceSimMsg("GenerateCallClosureFunctionStub");
- __ lw(T0, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
- __ LoadImmediate(TMP, Smi::RawValue(1));
- __ subu(T0, T0, TMP);
-
- // Load closure object in T1.
- __ sll(T1, T0, 1); // T0 (num_args - 1) is a Smi.
- __ addu(T1, SP, T1);
- __ lw(T1, Address(T1));
-
- // Verify that T1 is a closure by checking its class.
- Label not_closure;
-
- __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
-
- // See if it is not a closure, but null object.
- __ beq(T1, T7, ¬_closure);
-
- __ andi(CMPRES1, T1, Immediate(kSmiTagMask));
- __ beq(CMPRES1, ZR, ¬_closure); // Not a closure, but a smi.
-
- // Verify that the class of the object is a closure class by checking that
- // class.signature_function() is not null.
- __ LoadClass(T0, T1);
- __ lw(T0, FieldAddress(T0, Class::signature_function_offset()));
-
- // See if actual class is not a closure class.
- __ beq(T0, T7, ¬_closure);
-
- // T0 is just the signature function. Load the actual closure function.
- __ lw(T0, FieldAddress(T1, Closure::function_offset()));
-
- // Load closure context in CTX; note that CTX has already been preserved.
- __ lw(CTX, FieldAddress(T1, Closure::context_offset()));
-
- // Load closure function code in T2.
- // S4: arguments descriptor array.
- // S5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
- __ LoadImmediate(S5, 0);
- __ lw(T2, FieldAddress(T0, Function::code_offset()));
- __ lw(T2, FieldAddress(T2, Code::instructions_offset()));
- __ AddImmediate(T2, Instructions::HeaderSize() - kHeapObjectTag);
- __ jr(T2);
-
- __ Bind(¬_closure);
- // Call runtime to attempt to resolve and invoke a call method on a
- // non-closure object, passing the non-closure object and its arguments array,
- // returning here.
- // If no call method exists, throw a NoSuchMethodError.
- // T1: non-closure object.
- // S4: arguments descriptor array.
-
- // Create a stub frame as we are pushing some objects on the stack before
- // calling into the runtime.
- __ EnterStubFrame();
-
- // Setup space on stack for result from error reporting.
- __ addiu(SP, SP, Immediate(-2 * kWordSize));
- // Arguments descriptor and raw null.
- __ sw(T7, Address(SP, 1 * kWordSize));
- __ sw(S4, Address(SP, 0 * kWordSize));
-
- // Load smi-tagged arguments array length, including the non-closure.
- __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
- PushArgumentsArray(assembler);
-
- // Stack:
- // TOS + 0: Argument array.
- // TOS + 1: Arguments descriptor array.
- // TOS + 2: Place for result from the call.
- // TOS + 3: Saved FP of previous frame.
- // TOS + 4: Dart code return address.
- // TOS + 5: PC marker (0 for stub).
- // TOS + 6: Last argument of caller.
- // ....
- __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
- __ lw(V0, Address(SP, 2 * kWordSize)); // Get result into V0.
- __ addiu(SP, SP, Immediate(3 * kWordSize)); // Remove arguments.
-
- // Remove the stub frame as we are about to return.
- __ LeaveStubFrameAndReturn();
-}
-
-
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// RA : points to return address.
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index d914e54..cc7f4cc 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -70,7 +70,7 @@
#endif
// Mark that the isolate is executing VM code.
- __ movq(Address(CTX, Isolate::vm_tag_offset()), Immediate(VMTag::kVMTagId));
+ __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
// Reserve space for arguments and align frame before entering C++ world.
__ subq(RSP, Immediate(sizeof(NativeArguments)));
@@ -175,8 +175,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ movq(Address(CTX, Isolate::vm_tag_offset()),
- Immediate(VMTag::kRuntimeNativeTagId));
+ __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
// Reserve space for the native arguments structure passed on the stack (the
// outgoing pointer parameter to the native arguments structure is passed in
@@ -272,8 +271,7 @@
#endif
// Mark that the isolate is executing Native code.
- __ movq(Address(CTX, Isolate::vm_tag_offset()),
- Immediate(VMTag::kRuntimeNativeTagId));
+ __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
// Reserve space for the native arguments structure passed on the stack (the
// outgoing pointer parameter to the native arguments structure is passed in
@@ -693,81 +691,6 @@
}
-// Input parameters:
-// R10: Arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-// called, the stub accesses the closure from this location directly
-// when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- // Load num_args.
- __ movq(RAX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- // Load closure object in R13.
- __ movq(R13, Address(RSP, RAX, TIMES_4, 0)); // RAX is a Smi.
-
- __ LoadObject(R12, Object::null_object(), PP);
-
- // Verify that R13 is a closure by checking its class.
- Label not_closure;
- __ cmpq(R13, R12);
- // Not a closure, but null object.
- __ j(EQUAL, ¬_closure);
- __ testq(R13, Immediate(kSmiTagMask));
- __ j(ZERO, ¬_closure); // Not a closure, but a smi.
- // Verify that the class of the object is a closure class by checking that
- // class.signature_function() is not null.
- __ LoadClass(RAX, R13);
- __ movq(RAX, FieldAddress(RAX, Class::signature_function_offset()));
- __ cmpq(RAX, R12);
- // Actual class is not a closure class.
- __ j(EQUAL, ¬_closure, Assembler::kNearJump);
-
- // RAX is just the signature function. Load the actual closure function.
- __ movq(RAX, FieldAddress(R13, Closure::function_offset()));
-
- // Load closure context in CTX; note that CTX has already been preserved.
- __ movq(CTX, FieldAddress(R13, Closure::context_offset()));
-
- // Load closure function code in RAX.
- __ movq(RCX, FieldAddress(RAX, Function::code_offset()));
-
- // RAX: Function.
- // R10: Arguments descriptor array.
- // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
- __ xorq(RBX, RBX);
- __ movq(RCX, FieldAddress(RCX, Code::instructions_offset()));
- __ addq(RCX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
- __ jmp(RCX);
-
- __ Bind(¬_closure);
- // Call runtime to attempt to resolve and invoke a call method on a
- // non-closure object, passing the non-closure object and its arguments array,
- // returning here.
- // If no call method exists, throw a NoSuchMethodError.
- // R13: non-closure object.
- // R10: arguments descriptor array.
-
- // Create a stub frame as we are pushing some objects on the stack before
- // calling into the runtime.
- __ EnterStubFrame();
- // Setup space on stack for result from call.
- __ pushq(R12);
- __ pushq(R10); // Arguments descriptor.
- // Load smi-tagged arguments array length, including the non-closure.
- __ movq(R10, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- PushArgumentsArray(assembler);
-
- __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
-
- // Remove arguments.
- __ Drop(2);
- __ popq(RAX); // Get result into RAX.
-
- // Remove the stub frame as we are about to return.
- __ LeaveStubFrame();
- __ ret();
-}
-
-
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// RSP : points to return address.
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 4d11dba..3d6d5b2 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -290,6 +290,7 @@
V(DartMirrors, "dart:mirrors") \
V(DartTypedData, "dart:typed_data") \
V(DartVMService, "dart:vmservice") \
+ V(DartProfiler, "dart:profiler") \
V(_Random, "_Random") \
V(_state, "_state") \
V(_A, "_A") \
@@ -315,6 +316,8 @@
V(ClassId, "get:_classId") \
V(AllocationStubFor, "Allocation stub for ") \
V(TempParam, ":temp_param") \
+ V(UserTag, "UserTag") \
+ V(_UserTag, "_UserTag") \
// Contains a list of frequently used strings in a canonicalized form. This
diff --git a/runtime/vm/tags.cc b/runtime/vm/tags.cc
index f05aa22..21e1d5a 100644
--- a/runtime/vm/tags.cc
+++ b/runtime/vm/tags.cc
@@ -6,10 +6,24 @@
#include "vm/isolate.h"
#include "vm/json_stream.h"
+#include "vm/native_entry.h"
+#include "vm/runtime_entry.h"
+#include "vm/object.h"
namespace dart {
const char* VMTag::TagName(uword tag) {
+ if (IsNativeEntryTag(tag)) {
+ const uint8_t* native_reverse_lookup = NativeEntry::ResolveSymbol(tag);
+ if (native_reverse_lookup != NULL) {
+ return reinterpret_cast<const char*>(native_reverse_lookup);
+ }
+ return "Unknown native entry";
+ } else if (IsRuntimeEntryTag(tag)) {
+ const char* runtime_entry_name = RuntimeEntryTagName(tag);
+ ASSERT(runtime_entry_name != NULL);
+ return runtime_entry_name;
+ }
ASSERT(tag != kInvalidTagId);
ASSERT(tag < kNumVMTags);
const TagEntry& entry = entries_[tag];
@@ -18,6 +32,48 @@
}
+bool VMTag::IsNativeEntryTag(uword tag) {
+ if (tag == 0) {
+ return false;
+ }
+ ASSERT(tag != kInvalidTagId);
+ ASSERT(tag != kNumVMTags);
+ return (tag > kNumVMTags) && !IsRuntimeEntryTag(tag);
+}
+
+static RuntimeEntry* runtime_entry_list = NULL;
+
+bool VMTag::IsRuntimeEntryTag(uword id) {
+ const RuntimeEntry* current = runtime_entry_list;
+ while (current != NULL) {
+ if (reinterpret_cast<uword>(current->function()) == id) {
+ return true;
+ }
+ current = current->next();
+ }
+ return false;
+}
+
+
+const char* VMTag::RuntimeEntryTagName(uword id) {
+ const RuntimeEntry* current = runtime_entry_list;
+ while (current != NULL) {
+ if (reinterpret_cast<uword>(current->function()) == id) {
+ return current->name();
+ }
+ current = current->next();
+ }
+ return NULL;
+}
+
+
+void VMTag::RegisterRuntimeEntry(RuntimeEntry* runtime_entry) {
+ ASSERT(runtime_entry != NULL);
+ runtime_entry->set_next(runtime_entry_list);
+ runtime_entry_list = runtime_entry;
+}
+
+
VMTag::TagEntry VMTag::entries_[] = {
{ "InvalidTag", kInvalidTagId, },
#define DEFINE_VM_TAG_ENTRY(tag) \
@@ -50,6 +106,14 @@
void VMTagCounters::Increment(uword tag) {
+ if (VMTag::IsRuntimeEntryTag(tag)) {
+ counters_[VMTag::kRuntimeTagId]++;
+ return;
+ } else if (tag > VMTag::kNumVMTags) {
+ // Assume native entry.
+ counters_[VMTag::kNativeTagId]++;
+ return;
+ }
ASSERT(tag != VMTag::kInvalidTagId);
ASSERT(tag < VMTag::kNumVMTags);
counters_[tag]++;
@@ -78,4 +142,17 @@
}
}
+
+const char* UserTags::TagName(uword tag_id) {
+ ASSERT(tag_id >= kUserTagIdOffset);
+ ASSERT(tag_id < kUserTagIdOffset + kMaxUserTags);
+ Isolate* isolate = Isolate::Current();
+ const UserTag& tag =
+ UserTag::Handle(isolate, UserTag::FindTagById(tag_id));
+ ASSERT(!tag.IsNull());
+ const String& label = String::Handle(isolate, tag.label());
+ return label.ToCString();
+}
+
+
} // namespace dart
diff --git a/runtime/vm/tags.h b/runtime/vm/tags.h
index de0468f..f24f5a3 100644
--- a/runtime/vm/tags.h
+++ b/runtime/vm/tags.h
@@ -11,6 +11,7 @@
class Isolate;
class JSONObject;
+class RuntimeEntry;
#define VM_TAG_LIST(V) \
V(Idle) \
@@ -19,8 +20,8 @@
V(Script) \
V(GCNewSpace) \
V(GCOldSpace) \
- V(RuntimeNative) \
-
+ V(Runtime) \
+ V(Native) \
class VMTag : public AllStatic {
public:
@@ -33,7 +34,16 @@
kNumVMTags,
};
+ static bool IsVMTag(uword id) {
+ return (id != kInvalidTagId) && (id < kNumVMTags);
+ }
static const char* TagName(uword id);
+ static bool IsNativeEntryTag(uword id);
+
+ static bool IsRuntimeEntryTag(uword id);
+ static const char* RuntimeEntryTagName(uword id);
+
+ static void RegisterRuntimeEntry(RuntimeEntry* runtime_entry);
private:
struct TagEntry {
@@ -70,6 +80,21 @@
int64_t counters_[VMTag::kNumVMTags];
};
+
+class UserTags : public AllStatic {
+ public:
+ static const uword kNoUserTag = 0;
+ // UserTag id space: [kUserTagIdOffset, kUserTagIdOffset + kMaxUserTags).
+ static const intptr_t kMaxUserTags = 64;
+ static const uword kUserTagIdOffset = 0x4096;
+ static const char* TagName(uword tag_id);
+ static bool IsUserTag(uword tag_id) {
+ return (tag_id >= kUserTagIdOffset) &&
+ (tag_id < kUserTagIdOffset + kMaxUserTags);
+ }
+};
+
+
} // namespace dart
#endif // VM_TAGS_H_
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index a800505..5d45453 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -120,7 +120,7 @@
EXPECT_VALID(result);
Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
DART_CHECK_VALID(lib);
- result = Dart_SetNativeResolver(lib, resolver);
+ result = Dart_SetNativeResolver(lib, resolver, NULL);
DART_CHECK_VALID(result);
return lib;
}
diff --git a/runtime/vm/vm.gypi b/runtime/vm/vm.gypi
index ddb0db7..3bc5407 100644
--- a/runtime/vm/vm.gypi
+++ b/runtime/vm/vm.gypi
@@ -29,6 +29,8 @@
'snapshot_test_dart_file': 'snapshot_test.dart',
'typed_data_cc_file': '<(gen_source_dir)/typed_data_gen.cc',
'typed_data_patch_cc_file': '<(gen_source_dir)/typed_data_patch_gen.cc',
+ 'profiler_cc_file': '<(gen_source_dir)/profiler_gen.cc',
+ 'profiler_patch_cc_file': '<(gen_source_dir)/profiler_patch_gen.cc',
},
'targets': [
{
@@ -111,6 +113,8 @@
'generate_mirrors_patch_cc_file#host',
'generate_typed_data_cc_file#host',
'generate_typed_data_patch_cc_file#host',
+ 'generate_profiler_cc_file#host',
+ 'generate_profiler_patch_cc_file#host',
],
'includes': [
'../lib/async_sources.gypi',
@@ -120,6 +124,7 @@
'../lib/math_sources.gypi',
'../lib/mirrors_sources.gypi',
'../lib/typed_data_sources.gypi',
+ '../lib/profiler_sources.gypi',
],
'sources': [
'bootstrap.cc',
@@ -142,6 +147,8 @@
'<(mirrors_patch_cc_file)',
'<(typed_data_cc_file)',
'<(typed_data_patch_cc_file)',
+ '<(profiler_cc_file)',
+ '<(profiler_patch_cc_file)',
],
'include_dirs': [
'..',
@@ -159,6 +166,7 @@
'../lib/math_sources.gypi',
'../lib/mirrors_sources.gypi',
'../lib/typed_data_sources.gypi',
+ '../lib/profiler_sources.gypi',
],
'sources': [
'bootstrap_nocorelib.cc',
@@ -887,6 +895,86 @@
]
},
{
+ 'target_name': 'generate_profiler_cc_file',
+ 'type': 'none',
+ 'toolsets':['host'],
+ 'includes': [
+ # Load the shared library sources.
+ '../../sdk/lib/profiler/profiler_sources.gypi',
+ ],
+ 'sources/': [
+ # Exclude all .[cc|h] files.
+ # This is only here for reference. Excludes happen after
+ # variable expansion, so the script has to do its own
+ # exclude processing of the sources being passed.
+ ['exclude', '\\.cc|h$'],
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_profiler_cc',
+ 'inputs': [
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(profiler_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/gen_library_src_paths.py',
+ '--output', '<(profiler_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::profiler_source_paths_',
+ '--library_name', 'dart:profiler',
+ '<@(_sources)',
+ ],
+ 'message': 'Generating ''<(profiler_cc_file)'' file.'
+ },
+ ]
+ },
+ {
+ 'target_name': 'generate_profiler_patch_cc_file',
+ 'type': 'none',
+ 'toolsets':['host'],
+ 'includes': [
+ # Load the runtime implementation sources.
+ '../lib/profiler_sources.gypi',
+ ],
+ 'sources/': [
+ # Exclude all .[cc|h] files.
+ # This is only here for reference. Excludes happen after
+ # variable expansion, so the script has to do its own
+ # exclude processing of the sources being passed.
+ ['exclude', '\\.cc|h$'],
+ ],
+ 'actions': [
+ {
+ 'action_name': 'generate_profiler_patch_cc',
+ 'inputs': [
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(profiler_patch_cc_file)',
+ ],
+ 'action': [
+ 'python',
+ 'tools/gen_library_src_paths.py',
+ '--output', '<(profiler_patch_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
+ '--include', 'vm/bootstrap.h',
+ '--var_name', 'dart::Bootstrap::profiler_patch_paths_',
+ '--library_name', 'dart:profiler',
+ '<@(_sources)',
+ ],
+ 'message': 'Generating ''<(profiler_patch_cc_file)'' file.'
+ },
+ ]
+ },
+ {
'target_name': 'generate_snapshot_test_dat_file',
'type': 'none',
'toolsets':['host'],
diff --git a/sdk/lib/_internal/compiler/implementation/apiimpl.dart b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
index bdfd4b4..d54dc9a 100644
--- a/sdk/lib/_internal/compiler/implementation/apiimpl.dart
+++ b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
@@ -63,7 +63,8 @@
"build number could not be determined"),
showPackageWarnings:
hasOption(options, '--show-package-warnings'),
- useContentSecurityPolicy: hasOption(options, '--csp')) {
+ useContentSecurityPolicy: hasOption(options, '--csp'),
+ suppressWarnings: hasOption(options, '--suppress-warnings')) {
if (!libraryRoot.path.endsWith("/")) {
throw new ArgumentError("libraryRoot must end with a /");
}
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index 4899ec7..96b2876 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -431,6 +431,8 @@
/// suppressed for each library.
Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{};
+ final bool suppressWarnings;
+
final api.CompilerOutputProvider outputProvider;
bool disableInlining = false;
@@ -660,6 +662,7 @@
this.dumpInfo: false,
this.showPackageWarnings: false,
this.useContentSecurityPolicy: false,
+ this.suppressWarnings: false,
outputProvider,
List<String> strips: const []})
: this.analyzeOnly =
@@ -1116,7 +1119,7 @@
enqueuer.resolution.logSummary(log);
if (compilationFailed) return;
- if (!showPackageWarnings) {
+ if (!showPackageWarnings && !suppressWarnings) {
suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
if (info.warnings == 0) {
@@ -1312,7 +1315,8 @@
assert(invariant(element, !element.isSynthesized || tree == null));
if (tree != null) validator.validate(tree);
elements = resolver.resolve(element);
- if (tree != null && elements != null && !analyzeSignaturesOnly) {
+ if (tree != null && elements != null && !analyzeSignaturesOnly &&
+ !suppressWarnings) {
// Only analyze nodes with a corresponding [TreeElements].
checker.check(elements);
}
diff --git a/sdk/lib/_internal/compiler/implementation/dart2js.dart b/sdk/lib/_internal/compiler/implementation/dart2js.dart
index 5d2349d..f62b083 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2js.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2js.dart
@@ -261,8 +261,10 @@
new OptionHandler('-[chvm?]+', handleShortOptions),
new OptionHandler('--throw-on-error',
(_) => diagnosticHandler.throwOnError = true),
- new OptionHandler('--suppress-warnings',
- (_) => diagnosticHandler.showWarnings = false),
+ new OptionHandler('--suppress-warnings', (_) {
+ diagnosticHandler.showWarnings = false;
+ passThrough('--suppress-warnings');
+ }),
new OptionHandler('--suppress-hints',
(_) => diagnosticHandler.showHints = false),
new OptionHandler('--output-type=dart|--output-type=js', setOutputType),
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
index 2d66728..a4d1f1f 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
@@ -233,16 +233,17 @@
if (!compiler.irBuilder.hasIr(element)) {
node = element.parseNode(compiler);
} else {
- ir.Function function = compiler.irBuilder.getIr(element);
+ ir.FunctionDefinition function = compiler.irBuilder.getIr(element);
tree.Builder builder = new tree.Builder(compiler);
- tree.Expression expr = function.accept(builder);
- compiler.tracer.traceGraph('Tree builder', expr);
+ tree.FunctionDefinition definition = builder.build(function);
+ compiler.tracer.traceCompilation(element.name, null, compiler);
+ compiler.tracer.traceGraph('Tree builder', definition);
treeElements = new TreeElementMapping(element);
tree.Unnamer unnamer = new tree.Unnamer();
- expr = unnamer.unname(expr);
- compiler.tracer.traceGraph('Unnamer', expr);
+ unnamer.unname(definition);
+ compiler.tracer.traceGraph('Unnamer', definition);
tree.Emitter emitter = new tree.Emitter();
- node = emitter.emit(element, treeElements, expr);
+ node = emitter.emit(element, treeElements, definition);
}
return new ElementAst(node, treeElements);
}
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
index 178f3bd..59d7a56 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
@@ -7,7 +7,8 @@
import '../dart2jslib.dart' as dart2js;
import '../dart_types.dart';
import '../util/util.dart';
-import '../elements/elements.dart' show FunctionElement, FunctionSignature;
+import '../elements/elements.dart'
+ show Element, FunctionElement, FunctionSignature, ParameterElement;
import '../ir/ir_nodes.dart' as ir;
import '../tree/tree.dart' as ast;
import '../scanner/scannerlib.dart';
@@ -50,11 +51,15 @@
static int counter = 0;
static String _newName() => 'v${counter++}';
- ast.Identifier identifier = null;
+ final Element element;
+ String name;
+ ast.Identifier identifier;
+
+ Variable(this.element);
ast.Identifier assignIdentifier() {
assert(identifier == null);
- String name = _newName();
+ name = (element == null) ? _newName() : element.name;
identifier = Emitter.makeIdentifier(name);
return identifier;
}
@@ -85,10 +90,11 @@
*/
class LetVal extends Expression {
final Variable variable;
- final Expression definition;
- final Expression body;
+ Expression definition;
+ Expression body;
+ final bool hasExactlyOneUse;
- LetVal(this.variable, this.definition, this.body);
+ LetVal(this.variable, this.definition, this.body, this.hasExactlyOneUse);
bool get isPure => definition.isPure && body.isPure;
@@ -140,9 +146,21 @@
accept(Visitor visitor) => visitor.visitConstant(this);
}
+class FunctionDefinition extends Node {
+ final List<Variable> parameters;
+ Expression body;
+
+ FunctionDefinition(this.parameters, this.body);
+
+ accept(Visitor visitor) => visitor.visitFunctionDefinition(this);
+}
+
abstract class Visitor<T> {
+ T visit(Node node) => node.accept(this);
+
// Abstract classes.
- T visitNode(Node node) => node.accept(this);
+ T visitNode(Node node) => null;
+ T visitFunctionDefinition(FunctionDefinition node) => visitNode(node);
T visitExpression(Expression node) => visitNode(node);
// Concrete classes.
@@ -193,31 +211,47 @@
// mapping from definitions to variables.
final Map<ir.Definition, Variable> variables = {};
+ FunctionDefinition function;
ir.Continuation returnContinuation;
Builder(this.compiler);
+ FunctionDefinition build(ir.FunctionDefinition node) {
+ visit(node);
+ return function;
+ }
+
List<Expression> translateArguments(List<ir.Reference> args) {
return new List.generate(args.length,
(int index) => variables[args[index].definition]);
}
- Expression visitFunction(ir.Function node) {
- // Functions are simplistically translated to their bodies. For now this
- // is good enough.
+ Expression visitFunctionDefinition(ir.FunctionDefinition node) {
returnContinuation = node.returnContinuation;
- return node.body.accept(this);
+ List<Variable> parameters = <Variable>[];
+ for (ir.Parameter p in node.parameters) {
+ Variable parameter = new Variable(p.element);
+ parameters.add(parameter);
+ variables[p] = parameter;
+ }
+ function = new FunctionDefinition(parameters, visit(node.body));
+ return null;
}
Expression visitLetPrim(ir.LetPrim node) {
// LetPrim is translated to LetVal.
- Expression definition = node.primitive.accept(this);
+ Expression definition = visit(node.primitive);
if (node.primitive.hasAtLeastOneUse) {
- Variable variable = new Variable();
+ Variable variable = new Variable(null);
variables[node.primitive] = variable;
- return new LetVal(variable, definition, node.body.accept(this));
+ return new LetVal(variable, definition, node.body.accept(this),
+ node.primitive.hasExactlyOneUse);
+ } else if (node.primitive is ir.Constant) {
+ // TODO(kmillikin): Implement more systematic treatment of pure CPS
+ // values (e.g., as part of a shrinking reductions pass).
+ return visit(node.body);
} else {
- return new Sequence([definition, node.body.accept(this)]);
+ return new Sequence([definition, visit(node.body)]);
}
}
@@ -226,7 +260,7 @@
// arise due to the representation of local control flow or due to
// optimization.
assert(node.continuation.hasAtMostOneUse);
- return node.body.accept(this);
+ return visit(node.body);
}
Expression visitInvokeStatic(ir.InvokeStatic node) {
@@ -239,11 +273,12 @@
} else {
assert(cont.hasExactlyOneUse);
if (cont.parameter.hasAtLeastOneUse) {
- Variable variable = new Variable();
+ Variable variable = new Variable(null);
variables[cont.parameter] = variable;
- return new LetVal(variable, invoke, cont.body.accept(this));
+ return new LetVal(variable, invoke, cont.body.accept(this),
+ cont.parameter.hasExactlyOneUse);
} else {
- return new Sequence([invoke, cont.body.accept(this)]);
+ return new Sequence([invoke, visit(cont.body)]);
}
}
}
@@ -298,15 +333,14 @@
// enclosing binding.
List<LetVal> environment;
- Expression unname(Expression body) {
+ void unname(FunctionDefinition definition) {
environment = <LetVal>[];
- body = body.accept(this);
+ definition.body = visit(definition.body);
// TODO(kmillikin): Allow definitions that are not propagated. Here,
// this means rebuilding the binding with a recursively unnamed definition,
// or else introducing a variable definition and an assignment.
assert(environment.isEmpty);
- return body;
}
Expression visitVariable(Variable node) {
@@ -326,10 +360,11 @@
bool seenImpure = false;
for (int i = environment.length - 1; i >= 0; --i) {
if (environment[i].variable == node) {
- if (!seenImpure || environment[i].definition.isPure) {
+ if ((!seenImpure || environment[i].definition.isPure)
+ && environment[i].hasExactlyOneUse) {
// Use the definition if it is pure or if it is the first impure
// definition (i.e., propagating past only pure expressions).
- return environment.removeAt(i).definition.accept(this);
+ return visit(environment.removeAt(i).definition);
}
break;
} else if (!environment[i].definition.isPure) {
@@ -344,18 +379,22 @@
Expression visitSequence(Sequence node) {
for (int i = 0; i < node.expressions.length; ++i) {
- node.expressions[i] = node.expressions[i].accept(this);
+ node.expressions[i] = visit(node.expressions[i]);
}
return node;
}
Expression visitLetVal(LetVal node) {
environment.add(node);
- Expression body = node.body.accept(this);
+ Expression body = visit(node.body);
- // TODO(kmillikin): Allow definitions that are not propagated. Currently,
- // the only bindings are anonymous intermediate values (which only have one
- // use in the absence of optimizations) and they are not reordered.
+ if (!environment.isEmpty && environment.last == node) {
+ // The definition could not be propagated. Residualize the let binding.
+ node.body = body;
+ environment.removeLast();
+ node.definition = visit(node.definition);
+ return node;
+ }
assert(!environment.contains(node));
return body;
}
@@ -363,13 +402,13 @@
Expression visitInvokeStatic(InvokeStatic node) {
// Process arguments right-to-left, the opposite of evaluation order.
for (int i = node.arguments.length - 1; i >= 0; --i) {
- node.arguments[i] = node.arguments[i].accept(this);
+ node.arguments[i] = visit(node.arguments[i]);
}
return node;
}
Expression visitReturn(Return node) {
- node.value = node.value.accept(this);
+ node.value = visit(node.value);
return node;
}
@@ -444,7 +483,7 @@
*/
ast.FunctionExpression emit(FunctionElement element,
dart2js.TreeElementMapping treeElements,
- Expression expr) {
+ FunctionDefinition definition) {
// Reset the variable index. This function is not reentrant.
Variable.counter = 0;
this.treeElements = treeElements;
@@ -455,27 +494,29 @@
ast.TypeAnnotation returnType;
if (!signature.type.returnType.isDynamic) {
returnType =
- signature.type.returnType.accept(typeEmitter, treeElements);
+ typeEmitter.visitType(signature.type.returnType, treeElements);
}
List<ast.VariableDefinitions> parameterList = <ast.VariableDefinitions>[];
- signature.orderedForEachParameter((parameter) {
+ for (Variable parameter in definition.parameters) {
+ ParameterElement element = parameter.element;
+ parameter.assignIdentifier();
ast.TypeAnnotation type;
- if (!parameter.type.isDynamic) {
- type = parameter.type.accept(typeEmitter, treeElements);
+ if (!element.type.isDynamic) {
+ type = typeEmitter.visitType(element.type, treeElements);
}
parameterList.add(new ast.VariableDefinitions(
type,
ast.Modifiers.EMPTY,
- new ast.NodeList.singleton(makeIdentifier(parameter.name))));
- });
+ new ast.NodeList.singleton(parameter.identifier)));
+ }
ast.NodeList parameters =
new ast.NodeList(openParen,
new Link<ast.Node>.fromList(parameterList),
closeParen,
',');
- ast.Node body = expr.accept(this);
+ ast.Node body = visit(definition.body);
if (!variables.isEmpty) {
// Introduce hoisted definitions for all variables.
@@ -512,7 +553,7 @@
// Remove a final 'return null' that ends the body block.
Link<ast.Node> nodes = (body as ast.Block).statements.nodes;
ast.Node last;
- for (var n in nodes) {
+ for (ast.Node n in nodes) {
last = n;
}
if (last is ast.Return
@@ -532,8 +573,7 @@
* Translate a list of arguments to an AST NodeList.
*/
ast.NodeList translateArguments(List<Expression> args) {
- List<ast.Expression> arguments =
- args.map((e) => e.accept(this)).toList(growable: false);
+ List<ast.Expression> arguments = args.map(visit).toList(growable: false);
return makeArgumentList(arguments);
}
@@ -553,7 +593,7 @@
addStatements(ast.Node node) {
if (node is ast.Block) {
- for (var n in node.statements.nodes) {
+ for (ast.Node n in node.statements.nodes) {
statements.addLast(n);
}
} else if (node is ast.Expression) {
@@ -578,7 +618,7 @@
}
ast.Node visitSequence(Sequence node) {
- return node.expressions.map((e) => e.accept(this)).reduce(concatenate);
+ return node.expressions.map(visit).reduce(concatenate);
}
ast.Node visitLetVal(LetVal node) {
@@ -586,10 +626,10 @@
ast.Identifier identifier = node.variable.assignIdentifier();
variables.add(identifier);
- ast.Expression expression = node.definition.accept(this);
+ ast.Expression expression = visit(node.definition);
ast.Expression assignment = makeAssignment(identifier, expression);
- ast.Node rest = node.body.accept(this);
+ ast.Node rest = visit(node.body);
return concatenate(assignment, rest);
}
@@ -602,7 +642,7 @@
}
ast.Node visitReturn(Return node) {
- ast.Expression expression = node.value.accept(this);
+ ast.Expression expression = visit(node.value);
return new ast.Return(
new KeywordToken(Keyword.keywords['return'], -1),
semicolon,
@@ -632,7 +672,7 @@
ast.TypeAnnotation visitType(DartType type,
dart2js.TreeElementMapping treeElements) {
- return unimplemented();
+ return type.accept(this, treeElements);
}
ast.TypeAnnotation visitVoidType(VoidType type,
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
index e50ca26..1730851 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
@@ -3,26 +3,27 @@
import 'dart:async' show EventSink;
import '../tracer.dart';
import 'dart_tree.dart';
-
+
class TreeTracer extends TracerUtil implements Visitor {
-
final EventSink<String> output;
-
+
TreeTracer(this.output);
-
+
Names names;
- int stmtCounter;
-
- void traceGraph(String name, Expression exp) {
+ int statementCounter;
+
+ void visit(Node node) => node.accept(this);
+
+ void traceGraph(String name, FunctionDefinition function) {
names = new Names();
- stmtCounter = 0;
+ statementCounter = 0;
tag("cfg", () {
printProperty("name", name);
- printBlock(exp);
+ printBlock(function.body);
});
names = null;
}
-
+
void printBlock(Expression e) {
tag("block", () {
printProperty("name", "B0"); // Update when proper blocks exist
@@ -43,102 +44,105 @@
});
});
}
-
- void printStmt(String def, String contents) {
+
+ void printStatement(String name, String contents) {
int bci = 0;
int uses = 0;
- if (def == null) {
- def = 'x${stmtCounter++}';
+ if (name == null) {
+ name = 'x${statementCounter++}';
}
addIndent();
- add("$bci $uses $def $contents <|@\n");
+ add("$bci $uses $name $contents <|@\n");
}
-
+
+ visitFunctionDefinition(FunctionDefinition node) {
+ }
+
visitVariable(Variable node) {
- printStmt(null, "dead-use ${names.varName(node)}");
+ printStatement(null, "dead-use ${names.varName(node)}");
}
-
+
visitSequence(Sequence node) {
for (Expression e in node.expressions) {
e.accept(this);
}
}
-
+
visitLetVal(LetVal node) {
String name = names.varName(node.variable);
String rhs = expr(node.definition);
- printStmt(name, "let $name = $rhs");
+ printStatement(name, "let $name = $rhs");
node.body.accept(this);
}
-
+
visitInvokeStatic(InvokeStatic node) {
- printStmt(null, expr(node));
+ printStatement(null, expr(node));
}
-
+
visitReturn(Return node) {
- printStmt(null, "return ${expr(node.value)}");
+ printStatement(null, "return ${expr(node.value)}");
}
-
+
visitConstant(Constant node) {
- printStmt(null, "dead-use ${node.value}");
+ printStatement(null, "dead-use ${node.value}");
}
visitNode(Node node) {}
visitExpression(Expression node) {}
-
+
String expr(Expression e) {
- return e.accept(new SubExprVisitor(names));
+ return e.accept(new ExpressionVisitor(names));
}
-
}
-class SubExprVisitor implements Visitor<String> {
+class ExpressionVisitor extends Visitor<String> {
Names names;
-
- SubExprVisitor(this.names);
-
+
+ ExpressionVisitor(this.names);
+
String visitVariable(Variable node) {
return names.varName(node);
}
+
String visitSequence(Sequence node) {
String exps = node.expressions.map((e) => e.accept(this)).join('; ');
return "{ $exps }";
}
+
String visitLetVal(LetVal node) {
String name = names.varName(node.variable);
String def = node.definition.accept(this);
String body = node.body.accept(this);
return "(let $name = $def in $body)";
}
+
String visitInvokeStatic(InvokeStatic node) {
String head = node.target.name;
String args = node.arguments.map((e) => e.accept(this)).join(', ');
return "$head($args)";
}
+
String visitReturn(Return node) {
return "return ${node.value.accept(this)}";
}
+
String visitConstant(Constant node) {
return "${node.value}";
}
-
- visitNode(Node node) {}
- visitExpression(Expression node) {}
}
/**
* Invents (and remembers) names for Variables that do not have an associated
* identifier.
- *
+ *
* In case a variable is named v0, v1, etc, it may be assigned a different
* name to avoid clashing with a previously synthesized variable name.
*/
class Names {
-
final Map<Variable, String> _names = {};
final Set<String> _usedNames = new Set();
int _counter = 0;
-
+
String varName(Variable v) {
String name = _names[v];
if (name == null) {
@@ -153,5 +157,4 @@
}
return name;
}
-
}
\ No newline at end of file
diff --git a/sdk/lib/_internal/compiler/implementation/dart_types.dart b/sdk/lib/_internal/compiler/implementation/dart_types.dart
index 4177c5b..17794f9 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_types.dart
@@ -1186,20 +1186,23 @@
VoidType voidType = new VoidType(new VoidElementX(library));
DynamicType dynamicType = new DynamicType(dynamicElement);
dynamicElement.rawTypeCache = dynamicElement.thisTypeCache = dynamicType;
- MoreSpecificVisitor moreSpecificVisitor =
- new MoreSpecificVisitor(compiler, dynamicType, voidType);
- SubtypeVisitor subtypeVisitor =
- new SubtypeVisitor(compiler, dynamicType, voidType);
- PotentialSubtypeVisitor potentialSubtypeVisitor =
- new PotentialSubtypeVisitor(compiler, dynamicType, voidType);
-
- return new Types.internal(compiler, voidType, dynamicType,
- moreSpecificVisitor, subtypeVisitor, potentialSubtypeVisitor);
+ return new Types.internal(compiler, voidType, dynamicType);
}
- Types.internal(this.compiler, this.voidType, this.dynamicType,
- this.moreSpecificVisitor, this.subtypeVisitor,
- this.potentialSubtypeVisitor);
+ Types.internal(Compiler compiler, VoidType voidType, DynamicType dynamicType)
+ : this.compiler = compiler,
+ this.voidType = voidType,
+ this.dynamicType = dynamicType,
+ this.moreSpecificVisitor =
+ new MoreSpecificVisitor(compiler, dynamicType, voidType),
+ this.subtypeVisitor =
+ new SubtypeVisitor(compiler, dynamicType, voidType),
+ this.potentialSubtypeVisitor =
+ new PotentialSubtypeVisitor(compiler, dynamicType, voidType);
+
+ Types copy(Compiler compiler) {
+ return new Types.internal(compiler, voidType, dynamicType);
+ }
/** Returns true if [t] is more specific than [s]. */
bool isMoreSpecific(DartType t, DartType s) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
index 1bcc733..74f2f1f 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
@@ -23,7 +23,7 @@
* have an IR or not, depending on the language features that are used. For
* elements that do have an IR, the tree [ast.Node]s and the [Token]s are not
* used in the rest of the compilation. This is ensured by setting the element's
- * cached tree to [:null:] and also breaking the token stream to crash future
+ * cached tree to `null` and also breaking the token stream to crash future
* attempts to parse.
*
* The type inferrer works on either IR nodes or tree nodes. The IR nodes are
@@ -32,7 +32,8 @@
* re-implemented to work directly on the IR.
*/
class IrBuilderTask extends CompilerTask {
- final Map<Element, ir.Function> nodes = <Element, ir.Function>{};
+ final Map<Element, ir.FunctionDefinition> nodes =
+ <Element, ir.FunctionDefinition>{};
IrBuilderTask(Compiler compiler) : super(compiler);
@@ -40,7 +41,7 @@
bool hasIr(Element element) => nodes.containsKey(element.implementation);
- ir.Function getIr(Element element) => nodes[element.implementation];
+ ir.FunctionDefinition getIr(Element element) => nodes[element.implementation];
void buildNodes() {
if (!irEnabled()) return;
@@ -54,7 +55,7 @@
SourceFile sourceFile = elementSourceFile(element);
IrBuilder builder =
new IrBuilder(elementsMapping, compiler, sourceFile);
- ir.Function function;
+ ir.FunctionDefinition function;
ElementKind kind = element.kind;
if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
// TODO(lry): build ir for constructors.
@@ -152,6 +153,7 @@
class IrBuilder extends ResolvedVisitor<ir.Definition> {
final SourceFile sourceFile;
ir.Continuation returnContinuation = null;
+ List<ir.Parameter> parameters = <ir.Parameter>[];
// The IR builder maintains a context, which is an expression with a hole in
// it. The hole represents the focus where new expressions can be added.
@@ -179,33 +181,42 @@
ir.Expression root = null;
ir.Expression current = null;
+ Map<Element, int> variableIndex = <Element, int>{};
+ List<ir.Definition> assignedVars = <ir.Definition>[];
+
IrBuilder(TreeElements elements, Compiler compiler, this.sourceFile)
: super(elements, compiler);
/**
- * Builds the [ir.Function] for a function element. In case the function
- * uses features that cannot be expressed in the IR, this function returns
- * [:null:].
+ * Builds the [ir.FunctionDefinition] for a function element. In case the
+ * function uses features that cannot be expressed in the IR, this function
+ * returns `null`.
*/
- ir.Function buildFunction(FunctionElement functionElement) {
+ ir.FunctionDefinition buildFunction(FunctionElement functionElement) {
return nullIfGiveup(() => buildFunctionInternal(functionElement));
}
- ir.Function buildFunctionInternal(FunctionElement functionElement) {
- assert(invariant(functionElement, functionElement.isImplementation));
- ast.FunctionExpression function = functionElement.parseNode(compiler);
+ ir.FunctionDefinition buildFunctionInternal(FunctionElement element) {
+ assert(invariant(element, element.isImplementation));
+ ast.FunctionExpression function = element.parseNode(compiler);
assert(function != null);
assert(!function.modifiers.isExternal());
assert(elements[function] != null);
returnContinuation = new ir.Continuation.retrn();
root = current = null;
+
+ FunctionSignature signature = element.functionSignature;
+ signature.orderedForEachParameter((parameterElement) {
+ ir.Parameter parameter = new ir.Parameter(parameterElement);
+ parameters.add(parameter);
+ variableIndex[parameterElement] = assignedVars.length;
+ assignedVars.add(parameter);
+ });
+
function.body.accept(this);
ensureReturn(function);
- int endPosition = function.getEndToken().charOffset;
- int namePosition = elements[function].position().charOffset;
- return
- new ir.Function(endPosition, namePosition, returnContinuation, root);
+ return new ir.FunctionDefinition(returnContinuation, parameters, root);
}
ConstantSystem get constantSystem => compiler.backend.constantSystem;
@@ -226,9 +237,9 @@
}
/**
- * Add an explicit [:return null:] for functions that don't have a return
+ * Add an explicit `return null` for functions that don't have a return
* statement on each branch. This includes functions with an empty body,
- * such as [:foo(){ }:].
+ * such as `foo(){ }`.
*/
void ensureReturn(ast.FunctionExpression node) {
if (!isOpen) return;
@@ -339,7 +350,9 @@
}
ir.Definition visitGetterSend(ast.Send node) {
- return giveup();
+ Element element = elements[node];
+ if (!Elements.isLocal(element)) return giveup();
+ return assignedVars[variableIndex[element]];
}
ir.Definition visitOperatorSend(ast.Send node) {
@@ -385,7 +398,7 @@
return giveup();
}
if (!isOpen) return null;
- ir.Parameter v = new ir.Parameter();
+ ir.Parameter v = new ir.Parameter(null);
ir.Continuation k = new ir.Continuation(v);
ir.Expression invoke =
new ir.InvokeStatic(element, selector, k, arguments);
@@ -401,11 +414,50 @@
return giveup();
}
+ ir.Definition visitSendSet(ast.SendSet node) {
+ Element element = elements[node];
+ if (!Elements.isLocal(element)) return giveup();
+ if (node.assignmentOperator.source != '=') return giveup();
+ // Exactly one argument expected for a simple assignment.
+ assert(!node.arguments.isEmpty);
+ assert(node.arguments.tail.isEmpty);
+ ir.Definition result = node.arguments.head.accept(this);
+ assignedVars[variableIndex[element]] = result;
+ return result;
+ }
+
+ ir.Definition visitVariableDefinitions(ast.VariableDefinitions node) {
+ assert(isOpen);
+ for (ast.Node definition in node.definitions.nodes) {
+ Element element = elements[definition];
+ // Definitions are either SendSets if there is an initializer, or
+ // Identifiers if there is no initializer.
+ if (definition is ast.SendSet) {
+ assert(!definition.arguments.isEmpty);
+ assert(definition.arguments.tail.isEmpty);
+ ir.Definition initialValue = definition.arguments.head.accept(this);
+ // Do not continue adding instructions if the initializer throws.
+ if (!isOpen) return null;
+ variableIndex[element] = assignedVars.length;
+ assignedVars.add(initialValue);
+ } else {
+ assert(definition is ast.Identifier);
+ // The initial value is null.
+ // TODO(kmillikin): Consider pooling constants.
+ ir.Constant constant = new ir.Constant(constantSystem.createNull());
+ add(new ir.LetPrim(constant));
+ variableIndex[element] = assignedVars.length;
+ assignedVars.add(constant);
+ }
+ }
+ return null;
+ }
+
static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
ir.Definition giveup() => throw ABORT_IRNODE_BUILDER;
- ir.Function nullIfGiveup(ir.Function action()) {
+ ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
try {
return action();
} catch(e) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
index 7235ecd..78c2bb8 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
@@ -7,10 +7,10 @@
library dart2js.ir_nodes;
import '../dart2jslib.dart' as dart2js show Constant;
-import '../elements/elements.dart' show FunctionElement, LibraryElement;
+import '../elements/elements.dart'
+ show FunctionElement, LibraryElement, ParameterElement;
import 'ir_pickler.dart' show Pickler, IrConstantPool;
import '../universe/universe.dart' show Selector, SelectorKind;
-import '../util/util.dart' show Spannable;
abstract class Node {
static int hashCount = 0;
@@ -133,7 +133,9 @@
}
class Parameter extends Primitive {
- Parameter();
+ final ParameterElement element;
+
+ Parameter(this.element);
accept(Visitor visitor) => visitor.visitParameter(this);
}
@@ -154,32 +156,30 @@
/// A function definition, consisting of parameters and a body. The parameters
/// include a distinguished continuation parameter.
-class Function extends Node {
- final int endOffset;
- final int namePosition;
-
+class FunctionDefinition extends Node {
final Continuation returnContinuation;
+ final List<Parameter> parameters;
final Expression body;
- Function(this.endOffset, this.namePosition, this.returnContinuation,
- this.body);
+ FunctionDefinition(this.returnContinuation, this.parameters, this.body);
List<int> pickle(IrConstantPool constantPool) {
return new Pickler(constantPool).pickle(this);
}
- accept(Visitor visitor) => visitor.visitFunction(this);
+ accept(Visitor visitor) => visitor.visitFunctionDefinition(this);
}
abstract class Visitor<T> {
+ T visit(Node node) => node.accept(this);
// Abstract classes.
- T visitNode(Node node) => node.accept(this);
+ T visitNode(Node node) => null;
T visitExpression(Expression node) => visitNode(node);
T visitDefinition(Definition node) => visitNode(node);
T visitPrimitive(Primitive node) => visitDefinition(node);
// Concrete classes.
- T visitFunction(Function node) => visitNode(node);
+ T visitFunctionDefinition(FunctionDefinition node) => visitNode(node);
T visitLetPrim(LetPrim node) => visitExpression(node);
T visitLetCont(LetCont node) => visitExpression(node);
@@ -204,9 +204,16 @@
String newValueName() => 'v${_valueCounter++}';
String newContinuationName() => 'k${_continuationCounter++}';
- String visitFunction(Function node) {
+ String visitFunctionDefinition(FunctionDefinition node) {
names[node.returnContinuation] = 'return';
- return '(Function ${node.body.accept(this)})';
+ String parameters = node.parameters
+ .map((p) {
+ String name = p.element.name;
+ names[p] = name;
+ return name;
+ })
+ .join(' ');
+ return '(FunctionDefinition ($parameters) ${node.body.accept(this)})';
}
String visitLetPrim(LetPrim expr) {
@@ -230,9 +237,8 @@
String visitInvokeStatic(InvokeStatic expr) {
String name = expr.target.name;
String cont = names[expr.continuation.definition];
- List<String> args =
- expr.arguments.map((v) => names[v.definition]).toList(growable: false);
- return '(InvokeStatic $name $cont ${args.join(' ')})';
+ String args = expr.arguments.map((v) => names[v.definition]).join(' ');
+ return '(InvokeStatic $name $cont $args)';
}
String visitInvokeContinuation(InvokeContinuation expr) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
index 2a6c5ed..8a341a6 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
@@ -28,7 +28,7 @@
*
* pickle ::= int(entries) function
*
- * function ::= int(endSourceOffset) int(namePosition) node(body)
+ * function ::= int(parameter count) {element(parameter)} node(body)
*
* int ::= see [writeInt] for number encoding
*
@@ -168,7 +168,7 @@
*/
ByteData doubleData = new ByteData(8);
- List<int> pickle(ir.Function function) {
+ List<int> pickle(ir.FunctionDefinition function) {
data = new Uint8List(INITIAL_SIZE);
offset = 0;
emitted = <Object, int>{};
@@ -356,11 +356,14 @@
}
}
- void visitFunction(ir.Function node) {
- writeInt(node.endOffset);
- writeInt(node.namePosition);
+ void visitFunctionDefinition(ir.FunctionDefinition node) {
// The continuation parameter is bound in the body.
recordForBackReference(node.returnContinuation);
+ writeInt(node.parameters.length);
+ for (var parameter in node.parameters) {
+ recordForBackReference(parameter);
+ writeElement(parameter.element);
+ }
node.body.accept(this);
}
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
index 643d46a2..9b80f32 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
@@ -2,8 +2,7 @@
import 'dart:async' show EventSink;
-import 'ir_nodes.dart' as ir;
-import 'ir_nodes.dart' hide Function;
+import 'ir_nodes.dart' as ir hide Function;
import '../tracer.dart';
/**
@@ -11,23 +10,25 @@
*/
const bool IR_TRACE_LET_CONT = false;
-class IRTracer extends TracerUtil implements Visitor {
+class IRTracer extends TracerUtil implements ir.Visitor {
int indent = 0;
EventSink<String> output;
IRTracer(this.output);
- void traceGraph(String name, ir.Function graph) {
+ visit(ir.Node node) => node.accept(this);
+
+ void traceGraph(String name, ir.FunctionDefinition graph) {
tag("cfg", () {
printProperty("name", name);
- visitFunction(graph);
+ visitFunctionDefinition(graph);
});
}
// Temporary field used during tree walk
Names names;
- visitFunction(ir.Function f) {
+ visitFunctionDefinition(ir.FunctionDefinition f) {
names = new Names();
BlockCollector builder = new BlockCollector(names);
f.accept(builder);
@@ -69,13 +70,13 @@
add("$bci $uses $resultVar $contents <|@\n");
}
- visitLetPrim(LetPrim node) {
+ visitLetPrim(ir.LetPrim node) {
String id = names.name(node.primitive);
printStmt(id, "LetPrim $id = ${formatPrimitive(node.primitive)}");
node.body.accept(this);
}
- visitLetCont(LetCont node) {
+ visitLetCont(ir.LetCont node) {
if (IR_TRACE_LET_CONT) {
String dummy = names.name(node);
String id = names.name(node.continuation);
@@ -84,7 +85,7 @@
node.body.accept(this);
}
- visitInvokeStatic(InvokeStatic node) {
+ visitInvokeStatic(ir.InvokeStatic node) {
String dummy = names.name(node);
String callName = node.selector.name;
String args = node.arguments.map(formatReference).join(', ');
@@ -92,23 +93,23 @@
printStmt(dummy, "InvokeStatic $callName ($args) $kont");
}
- visitInvokeContinuation(InvokeContinuation node) {
+ visitInvokeContinuation(ir.InvokeContinuation node) {
String dummy = names.name(node);
String kont = formatReference(node.continuation);
String arg = formatReference(node.argument);
printStmt(dummy, "InvokeContinuation $kont ($arg)");
}
- String formatReference(Reference ref) {
- Definition target = ref.definition;
- if (target is Continuation && target.body == null) {
+ String formatReference(ir.Reference ref) {
+ ir.Definition target = ref.definition;
+ if (target is ir.Continuation && target.body == null) {
return "return"; // Do not generate a name for the return continuation
} else {
return names.name(ref.definition);
}
}
- String formatPrimitive(Primitive p) {
+ String formatPrimitive(ir.Primitive p) {
return p.accept(this);
}
@@ -116,23 +117,23 @@
return "Constant ${node.value}";
}
- visitParameter(Parameter node) {
+ visitParameter(ir.Parameter node) {
return "Parameter ${names.name(node)}";
}
- visitContinuation(Continuation node) {
+ visitContinuation(ir.Continuation node) {
return "Continuation ${names.name(node)}";
}
- visitExpression(Expression e) {}
- visitPrimitive(Primitive p) {}
- visitDefinition(Definition d) {}
- visitNode(Node n) {}
+ visitExpression(ir.Expression e) {}
+ visitPrimitive(ir.Primitive p) {}
+ visitDefinition(ir.Definition d) {}
+ visitNode(ir.Node n) {}
}
-/**
+/**
* Invents (and remembers) names for Continuations, Parameters, etc.
- * The names must match the conventions used by IR Hydra, e.g.
+ * The names must match the conventions used by IR Hydra, e.g.
* Continuations and Functions must have names of form B### since they
* are visualized as basic blocks.
*/
@@ -146,9 +147,9 @@
};
String prefix(x) {
- if (x is Parameter) return 'r';
- if (x is Continuation || x is ir.Function) return 'B';
- if (x is Primitive) return 'v';
+ if (x is ir.Parameter) return 'r';
+ if (x is ir.Continuation || x is ir.FunctionDefinition) return 'B';
+ if (x is ir.Primitive) return 'v';
return 'x';
}
@@ -164,12 +165,12 @@
}
/**
- * A vertex in the graph visualization, used in place of basic blocks.
+ * A vertex in the graph visualization, used in place of basic blocks.
*/
class Block {
String name;
- final List<Parameter> parameters;
- final Expression body;
+ final List<ir.Parameter> parameters;
+ final ir.Expression body;
final List<Block> succ = <Block>[];
final List<Block> pred = <Block>[];
@@ -181,15 +182,15 @@
}
}
-class BlockCollector implements Visitor {
+class BlockCollector extends ir.Visitor {
Block entry;
- final Map<Continuation, Block> cont2block = <Continuation, Block> {};
+ final Map<ir.Continuation, Block> cont2block = <ir.Continuation, Block> {};
Block current_block;
Names names;
BlockCollector(this.names);
- Block getBlock(Continuation c) {
+ Block getBlock(ir.Continuation c) {
Block block = cont2block[c];
if (block == null) {
block = new Block(names.name(c), [c.parameter], c.body);
@@ -198,41 +199,42 @@
return block;
}
- visitFunction(ir.Function f) {
+ visitFunctionDefinition(ir.FunctionDefinition f) {
entry = current_block = new Block(names.name(f), [], f.body);
f.body.accept(this);
}
- visitLetPrim(LetPrim exp) {
+
+ visitLetPrim(ir.LetPrim exp) {
exp.body.accept(this);
}
- visitLetCont(LetCont exp) {
+
+ visitLetCont(ir.LetCont exp) {
exp.continuation.accept(this);
exp.body.accept(this);
}
- visitInvokeStatic(InvokeStatic exp) {
- Definition target = exp.continuation.definition;
- if (target is Continuation && target.body != null) {
+
+ visitInvokeStatic(ir.InvokeStatic exp) {
+ ir.Definition target = exp.continuation.definition;
+ if (target is ir.Continuation && target.body != null) {
current_block.addEdgeTo(getBlock(target));
}
}
- visitInvokeContinuation(InvokeContinuation exp) {
- Definition target = exp.continuation.definition;
- if (target is Continuation && target.body != null) {
+ visitInvokeContinuation(ir.InvokeContinuation exp) {
+ ir.Definition target = exp.continuation.definition;
+ if (target is ir.Continuation && target.body != null) {
current_block.addEdgeTo(getBlock(target));
}
}
+
visitConstant(ir.Constant constant) {}
- visitParameter(Parameter p) {}
- visitContinuation(Continuation c) {
+
+ visitParameter(ir.Parameter p) {}
+
+ visitContinuation(ir.Continuation c) {
var old_node = current_block;
current_block = getBlock(c);
c.body.accept(this);
current_block = old_node;
}
-
- visitPrimitive(Primitive p) {}
- visitDefinition(Definition d) {}
- visitExpression(Expression e) {}
- visitNode(Node n) {}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
index e560997..5cee8e8 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
@@ -22,8 +22,8 @@
int index;
/**
- * This buffer is used in [readConstant] to reconstruct a double value from
- * a sequence of bytes.
+ * This buffer is used in [readConstantValue] to reconstruct a double value
+ * from a sequence of bytes.
*/
ByteData doubleData = new ByteData(8);
@@ -37,14 +37,14 @@
ir.Expression root;
ir.Expression current;
- ir.Function unpickle(List<int> data) {
+ ir.FunctionDefinition unpickle(List<int> data) {
this.data = data;
offset = 0;
int numEntries = readInt();
unpickled = new List<Object>(numEntries);
index = 0;
root = current = null;
- return readFunctionNode();
+ return readFunctionDefinition();
}
int readByte() {
@@ -122,12 +122,12 @@
int tag = readByte();
switch (tag) {
case Pickles.NODE_CONSTANT:
- ir.Definition constant = readConstantNode();
+ ir.Definition constant = readConstant();
unpickled[index++] = constant;
addExpression(new ir.LetPrim(constant));
break;
case Pickles.NODE_LET_CONT:
- ir.Parameter parameter = new ir.Parameter();
+ ir.Parameter parameter = new ir.Parameter(null);
ir.Continuation continuation = new ir.Continuation(parameter);
unpickled[index++] = continuation;
ir.Expression body = readDelimitedExpressionNode();
@@ -135,11 +135,11 @@
addExpression(new ir.LetCont(continuation, body));
break;
case Pickles.NODE_INVOKE_STATIC:
- addExpression(readInvokeStaticNode());
+ addExpression(readInvokeStatic());
current = null;
break;
case Pickles.NODE_INVOKE_CONTINUATION:
- addExpression(readInvokeContinuationNode());
+ addExpression(readInvokeContinuation());
current = null;
break;
default:
@@ -174,30 +174,34 @@
List<ir.Definition> readBackReferenceList() {
int length = readInt();
List<ir.Definition> result = new List<ir.Definition>(length);
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < length; ++i) {
result[i] = readBackReference();
}
return result;
}
- ir.Function readFunctionNode() {
- int endOffset = readInt();
- int namePosition = readInt();
+ ir.FunctionDefinition readFunctionDefinition() {
// There is implicitly a return continuation which can be the target of
// back references.
ir.Continuation continuation = new ir.Continuation.retrn();
unpickled[index++] = continuation;
+ int parameterCount = readInt();
+ List<ir.Parameter> parameters = new List<ir.Parameter>(parameterCount);
+ for (int i = 0; i < parameterCount; ++i) {
+ unpickled[index++] = parameters[i] = new ir.Parameter(readElement());
+ }
+
ir.Expression body = readDelimitedExpressionNode();
- return new ir.Function(endOffset, namePosition, continuation, body);
+ return new ir.FunctionDefinition(continuation, parameters, body);
}
- ir.Constant readConstantNode() {
- Constant constant = readConstant();
+ ir.Constant readConstant() {
+ Constant constant = readConstantValue();
return new ir.Constant(constant);
}
- ir.InvokeStatic readInvokeStaticNode() {
+ ir.InvokeStatic readInvokeStatic() {
FunctionElement functionElement = readElement();
Selector selector = readSelector();
ir.Continuation continuation = readBackReference();
@@ -206,13 +210,13 @@
arguments);
}
- ir.InvokeContinuation readInvokeContinuationNode() {
+ ir.InvokeContinuation readInvokeContinuation() {
ir.Continuation continuation = readBackReference();
ir.Definition argument = readBackReference();
return new ir.InvokeContinuation(continuation, argument);
}
- Constant readConstant() {
+ Constant readConstantValue() {
int tag = readByte();
switch(tag) {
case Pickles.CONST_BOOL:
diff --git a/sdk/lib/_internal/compiler/implementation/js/builder.dart b/sdk/lib/_internal/compiler/implementation/js/builder.dart
index 5ae235e..31e88f1 100644
--- a/sdk/lib/_internal/compiler/implementation/js/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/builder.dart
@@ -10,30 +10,41 @@
class JsBuilder {
const JsBuilder();
- // Parse a bit of JavaScript, and return an expression.
- // See the MiniJsParser class.
- // You can provide an expression or a list of expressions, which will be
- // interpolated into the source at the '#' signs.
+ /**
+ * Parses a bit of JavaScript, and returns an expression.
+ *
+ * See the MiniJsParser class.
+ *
+ * [expression] can be an [Expression] or a list of [Expression]s, which will
+ * be interpolated into the source at the '#' signs.
+ */
Expression call(String source, [var expression]) {
var result = new MiniJsParser(source).expression();
if (expression == null) return result;
- List<Expression> expressions;
+ List<Node> nodes;
if (expression is List) {
- expressions = expression;
+ nodes = expression;
} else {
- expressions = <Expression>[expression];
+ nodes = <Node>[expression];
}
- if (expressions.length != result.interpolatedExpressions.length) {
- throw "Unmatched number of interpolated expressions";
+ if (nodes.length != result.interpolatedNodes.length) {
+ throw 'Unmatched number of interpolated expressions given ${nodes.length}'
+ ' expected ${result.interpolatedNodes.length}';
}
- for (int i = 0; i < expressions.length; i++) {
- result.interpolatedExpressions[i].value = expressions[i];
+ for (int i = 0; i < nodes.length; i++) {
+ result.interpolatedNodes[i].assign(nodes[i]);
}
return result.value;
}
+ Statement statement(String source) {
+ var result = new MiniJsParser(source).statement();
+ // TODO(sra): Interpolation.
+ return result;
+ }
+
// Parse JavaScript written in the JS foreign instruction.
Expression parseForeignJS(String source, [var expression]) {
// We can parse simple JS with the mini parser. At the moment we can't
@@ -198,13 +209,27 @@
class MiniJsParserError {
MiniJsParserError(this.parser, this.message) { }
- MiniJsParser parser;
- String message;
+ final MiniJsParser parser;
+ final String message;
String toString() {
- var codes = new List.filled(parser.lastPosition, charCodes.$SPACE);
- var spaces = new String.fromCharCodes(codes);
- return "Error in MiniJsParser:\n${parser.src}\n$spaces^\n$spaces$message\n";
+ int pos = parser.lastPosition;
+
+ // Discard lines following the line containing lastPosition.
+ String src = parser.src;
+ int newlinePos = src.indexOf('\n', pos);
+ if (newlinePos >= pos) src = src.substring(0, newlinePos);
+
+ // Extract the prefix of the error line before lastPosition.
+ String line = src;
+ int lastLineStart = line.lastIndexOf('\n');
+ if (lastLineStart >= 0) line = line.substring(lastLineStart + 1);
+ String prefix = line.substring(0, pos - (src.length - line.length));
+
+ // Replace non-tabs with spaces, giving a print indent that matches the text
+ // for tabbing.
+ String spaces = prefix.replaceAll(new RegExp(r'[^\t]'), ' ');
+ return 'Error in MiniJsParser:\n${src}\n$spaces^\n$spaces$message\n';
}
}
@@ -241,13 +266,13 @@
getToken();
}
- int lastCategory;
- String lastToken;
- int lastPosition;
- int position;
- String src;
- final List<InterpolatedExpression> interpolatedValues =
- <InterpolatedExpression>[];
+ int lastCategory = NONE;
+ String lastToken = null;
+ int lastPosition = 0;
+ int position = 0;
+ bool skippedNewline = false; // skipped newline in last getToken?
+ final String src;
+ final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[];
static const NONE = -1;
static const ALPHA = 0;
@@ -265,9 +290,10 @@
static const COMMA = 12;
static const QUERY = 13;
static const COLON = 14;
- static const HASH = 15;
- static const WHITESPACE = 16;
- static const OTHER = 17;
+ static const SEMICOLON = 15;
+ static const HASH = 16;
+ static const WHITESPACE = 17;
+ static const OTHER = 18;
// Make sure that ]] is two symbols.
bool singleCharCategory(int category) => category >= DOT;
@@ -290,6 +316,7 @@
case COMMA: return "COMMA";
case QUERY: return "QUERY";
case COLON: return "COLON";
+ case SEMICOLON: return "SEMICOLON";
case HASH: return "HASH";
case WHITESPACE: return "WHITESPACE";
case OTHER: return "OTHER";
@@ -307,7 +334,7 @@
LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./
NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234
NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789
- COLON, OTHER, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@
+ COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX
@@ -355,6 +382,7 @@
position++;
if (position >= src.length) error("Unterminated literal");
currentCode = src.codeUnitAt(position);
+ if (currentCode == charCodes.$LF) error("Unterminated literal");
if (currentCode == charCodes.$BACKSLASH) {
if (++position >= src.length) error("Unterminated literal");
int escaped = src.codeUnitAt(position);
@@ -370,10 +398,24 @@
}
void getToken() {
- while (position < src.length &&
- category(src.codeUnitAt(position)) == WHITESPACE) {
- position++;
+ skippedNewline = false;
+ for (;;) {
+ if (position >= src.length) break;
+ int code = src.codeUnitAt(position);
+ // Skip '//' style comment.
+ if (code == charCodes.$SLASH &&
+ position + 1 < src.length &&
+ src.codeUnitAt(position + 1) == charCodes.$SLASH) {
+ int nextPosition = src.indexOf('\n', position);
+ if (nextPosition == -1) nextPosition = src.length;
+ position = nextPosition;
+ } else {
+ if (category(code) != WHITESPACE) break;
+ if (code == charCodes.$LF) skippedNewline = true;
+ ++position;
+ }
}
+
if (position == src.length) {
lastCategory = NONE;
lastToken = null;
@@ -458,6 +500,21 @@
return false;
}
+ void expectSemicolon() {
+ if (acceptSemicolon()) return;
+ error('Expected SEMICOLON');
+ }
+
+ bool acceptSemicolon() {
+ // Accept semicolon or automatically inserted semicolon before close brace.
+ // Miniparser forbids other kinds of semicolon insertion.
+ if (RBRACE == lastCategory) return true;
+ if (skippedNewline) {
+ error('No automatic semicolon insertion at preceding newline');
+ }
+ return acceptCategory(SEMICOLON);
+ }
+
bool acceptString(String string) {
if (lastToken == string) {
getToken();
@@ -479,6 +536,8 @@
return new LiteralBool(false);
} else if (last == "null") {
return new LiteralNull();
+ } else if (last == "function") {
+ return parseFunctionExpression();
} else {
return new VariableUse(last);
}
@@ -491,13 +550,12 @@
} else if (acceptCategory(NUMERIC)) {
return new LiteralNumber(last);
} else if (acceptCategory(LBRACE)) {
- expectCategory(RBRACE);
- return new ObjectInitializer([]);
+ return parseObjectInitializer();
} else if (acceptCategory(LSQUARE)) {
var values = <ArrayElement>[];
if (!acceptCategory(RSQUARE)) {
do {
- values.add(new ArrayElement(values.length, parseExpression()));
+ values.add(new ArrayElement(values.length, parseAssignment()));
} while (acceptCategory(COMMA));
expectCategory(RSQUARE);
}
@@ -519,6 +577,57 @@
}
}
+ Expression parseFunctionExpression() {
+ String last = lastToken;
+ if (acceptCategory(ALPHA)) {
+ String functionName = last;
+ return new NamedFunction(new VariableDeclaration(functionName),
+ parseFun());
+ }
+ return parseFun();
+ }
+
+ Expression parseFun() {
+ List<Parameter> params = <Parameter>[];
+ expectCategory(LPAREN);
+ String argumentName = lastToken;
+ if (acceptCategory(ALPHA)) {
+ params.add(new Parameter(argumentName));
+ while (acceptCategory(COMMA)) {
+ argumentName = lastToken;
+ expectCategory(ALPHA);
+ params.add(new Parameter(argumentName));
+ }
+ }
+ expectCategory(RPAREN);
+ expectCategory(LBRACE);
+ Block block = parseBlock();
+ return new Fun(params, block);
+ }
+
+ Expression parseObjectInitializer() {
+ List<Property> properties = <Property>[];
+ for (;;) {
+ if (acceptCategory(RBRACE)) break;
+ // Limited subset: keys are identifiers, no 'get' or 'set' properties.
+ Literal propertyName;
+ String identifier = lastToken;
+ if (acceptCategory(ALPHA)) {
+ propertyName = new LiteralString('"$identifier"');
+ } else if (acceptCategory(STRING)) {
+ propertyName = new LiteralString(identifier);
+ } else {
+ error('Expected property name');
+ }
+ expectCategory(COLON);
+ Expression value = parseAssignment();
+ properties.add(new Property(propertyName, value));
+ if (acceptCategory(RBRACE)) break;
+ expectCategory(COMMA);
+ }
+ return new ObjectInitializer(properties);
+ }
+
Expression parseMember() {
Expression receiver = parsePrimary();
while (true) {
@@ -543,7 +652,7 @@
final arguments = <Expression>[];
if (!acceptCategory(RPAREN)) {
while (true) {
- Expression argument = parseExpression();
+ Expression argument = parseAssignment();
arguments.add(argument);
if (acceptCategory(RPAREN)) break;
expectCategory(COMMA);
@@ -585,9 +694,16 @@
Expression parsePostfix() {
Expression expression = parseCall();
String operator = lastToken;
- if (lastCategory == SYMBOL && (acceptString("++") || acceptString("--"))) {
+ // JavaScript grammar is:
+ // LeftHandSideExpression [no LineTerminator here] ++
+ if (lastCategory == SYMBOL &&
+ !skippedNewline &&
+ (acceptString("++") || acceptString("--"))) {
return new Postfix(operator, expression);
}
+ // If we don't accept '++' or '--' due to skippedNewline a newline, no other
+ // part of the parser will accept the token and we will get an error at the
+ // whole expression level.
return expression;
}
@@ -664,23 +780,45 @@
return lhs;
}
- Expression parseExpression() => parseAssignment();
+ Expression parseExpression() {
+ Expression expression = parseAssignment();
+ while (acceptCategory(COMMA)) {
+ Expression right = parseAssignment();
+ expression = new Binary(',', expression, right);
+ }
+ return expression;
+ }
+
+ VariableDeclarationList parseVariableDeclarationList() {
+ String firstVariable = lastToken;
+ expectCategory(ALPHA);
+ return finishVariableDeclarationList(firstVariable);
+ }
+
+ VariableDeclarationList finishVariableDeclarationList(String firstVariable) {
+ var initialization = [];
+
+ void declare(String variable) {
+ Expression initializer = null;
+ if (acceptString("=")) {
+ initializer = parseAssignment();
+ }
+ var declaration = new VariableDeclaration(variable);
+ initialization.add(new VariableInitialization(declaration, initializer));
+ }
+
+ declare(firstVariable);
+ while (acceptCategory(COMMA)) {
+ String variable = lastToken;
+ expectCategory(ALPHA);
+ declare(variable);
+ }
+ return new VariableDeclarationList(initialization);
+ }
Expression parseVarDeclarationOrExpression() {
if (acceptString("var")) {
- var initialization = [];
- do {
- String variable = lastToken;
- expectCategory(ALPHA);
- Expression initializer = null;
- if (acceptString("=")) {
- initializer = parseExpression();
- }
- var declaration = new VariableDeclaration(variable);
- initialization.add(
- new VariableInitialization(declaration, initializer));
- } while (acceptCategory(COMMA));
- return new VariableDeclarationList(initialization);
+ return parseVariableDeclarationList();
} else {
return parseExpression();
}
@@ -696,6 +834,167 @@
}
return expression;
}
+
+ Statement statement() {
+ Statement statement = parseStatement();
+ if (lastCategory != NONE || position != src.length) {
+ error("Unparsed junk: ${categoryToString(lastCategory)}");
+ }
+ // TODO(sra): interpolated capture here?
+ return statement;
+ }
+
+ Block parseBlock() {
+ List<Statement> statements = <Statement>[];
+
+ while (!acceptCategory(RBRACE)) {
+ Statement statement = parseStatement();
+ statements.add(statement);
+ }
+ return new Block(statements);
+ }
+
+ Statement parseStatement() {
+ if (acceptCategory(LBRACE)) return parseBlock();
+
+ if (lastCategory == ALPHA) {
+ if (acceptString('return')) return parseReturn();
+
+ if (acceptString('throw')) return parseThrow();
+
+ if (acceptString('break')) {
+ return parseBreakOrContinue((label) => new Break(label));
+ }
+
+ if (acceptString('continue')) {
+ return parseBreakOrContinue((label) => new Continue(label));
+ }
+
+ if (acceptString('if')) return parseIfThenElse();
+
+ if (acceptString('for')) return parseFor();
+
+ if (acceptString('function')) return parseFunctionDeclaration();
+
+ if (acceptString('var')) {
+ Expression declarations = parseVariableDeclarationList();
+ expectSemicolon();
+ return new ExpressionStatement(declarations);
+ }
+
+ if (lastToken == 'case' ||
+ lastToken == 'do' ||
+ lastToken == 'while' ||
+ lastToken == 'switch' ||
+ lastToken == 'try' ||
+ lastToken == 'with') {
+ error('Not implemented in mini parser');
+ }
+ }
+
+ if (acceptCategory(HASH)) {
+ InterpolatedStatement statement = new InterpolatedStatement(null);
+ interpolatedValues.add(statement);
+ return statement;
+ }
+
+ // TODO: label: statement
+
+ Expression expression = parseExpression();
+ expectSemicolon();
+ return new ExpressionStatement(expression);
+ }
+
+ Statement parseReturn() {
+ if (acceptSemicolon()) return new Return();
+ Expression expression = parseExpression();
+ expectSemicolon();
+ return new Return(expression);
+ }
+
+ Statement parseThrow() {
+ if (skippedNewline) error('throw expression must be on same line');
+ Expression expression = parseExpression();
+ expectSemicolon();
+ return new Throw(expression);
+ }
+
+ Statement parseBreakOrContinue(constructor) {
+ var identifier = lastToken;
+ if (!skippedNewline && acceptCategory(ALPHA)) {
+ expectSemicolon();
+ return constructor(identifier);
+ }
+ expectSemicolon();
+ return constructor(null);
+ }
+
+ Statement parseIfThenElse() {
+ expectCategory(LPAREN);
+ Expression condition = parseExpression();
+ expectCategory(RPAREN);
+ Statement thenStatement = parseStatement();
+ if (acceptString('else')) {
+ // Resolves dangling else by binding 'else' to closest 'if'.
+ Statement elseStatement = parseStatement();
+ return new If(condition, thenStatement, elseStatement);
+ } else {
+ return new If.noElse(condition, thenStatement);
+ }
+ }
+
+ Statement parseFor() {
+ // For-init-condition-increment style loops are fully supported.
+ //
+ // Only one for-in variant is currently implemented:
+ //
+ // for (var variable in Expression) Statement
+ //
+ Statement finishFor(Expression init) {
+ Expression condition = null;
+ if (!acceptCategory(SEMICOLON)) {
+ condition = parseExpression();
+ expectCategory(SEMICOLON);
+ }
+ Expression update = null;
+ if (!acceptCategory(RPAREN)) {
+ update = parseExpression();
+ expectCategory(RPAREN);
+ }
+ Statement body = parseStatement();
+ return new For(init, condition, update, body);
+ }
+
+ expectCategory(LPAREN);
+ if (acceptCategory(SEMICOLON)) {
+ return finishFor(null);
+ }
+
+ if (acceptString('var')) {
+ String identifier = lastToken;
+ expectCategory(ALPHA);
+ if (acceptString('in')) {
+ Expression objectExpression = parseExpression();
+ expectCategory(RPAREN);
+ Statement body = parseStatement();
+ return new ForIn(js.defineVar(identifier), objectExpression, body);
+ }
+ Expression declarations = finishVariableDeclarationList(identifier);
+ expectCategory(SEMICOLON);
+ return finishFor(declarations);
+ }
+
+ Expression init = parseExpression();
+ expectCategory(SEMICOLON);
+ return finishFor(init);
+ }
+
+ Statement parseFunctionDeclaration() {
+ String name = lastToken;
+ expectCategory(ALPHA);
+ Expression fun = parseFun();
+ return new FunctionDeclaration(new VariableDeclaration(name), fun);
+ }
}
/**
@@ -754,6 +1053,10 @@
return arguments[argumentIndex++];
}
+ Node visitInterpolatedStatement(InterpolatedStatement statement) {
+ return arguments[argumentIndex++];
+ }
+
Node visitJSExpression(JSExpression expression) {
assert(argumentIndex == 0);
Node result = visit(expression.value);
diff --git a/sdk/lib/_internal/compiler/implementation/js/nodes.dart b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
index 95191f0..b48c4ae 100644
--- a/sdk/lib/_internal/compiler/implementation/js/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
@@ -64,6 +64,7 @@
T visitComment(Comment node);
T visitInterpolatedExpression(InterpolatedExpression node);
+ T visitInterpolatedStatement(InterpolatedStatement node);
T visitJSExpression(JSExpression node);
}
@@ -151,6 +152,8 @@
T visitInterpolatedExpression(InterpolatedExpression node)
=> visitExpression(node);
+ T visitInterpolatedStatement(InterpolatedStatement node)
+ => visitStatement(node);
T visitJSExpression(JSExpression node) => visitExpression(node);
// Ignore comments by default.
@@ -702,6 +705,8 @@
return LOGICAL_AND;
case "||":
return LOGICAL_OR;
+ case ',':
+ return EXPRESSION;
default:
throw new leg.CompilerCancelledException(
"Internal Error: Unhandled binary operator: $op");
@@ -947,11 +952,18 @@
}
}
-class InterpolatedExpression extends Expression {
+/// Tag class for all interpolated positions.
+abstract class InterpolatedNode implements Node {}
+
+class InterpolatedExpression extends Expression implements InterpolatedNode {
Expression value;
InterpolatedExpression(this.value);
+ void assign(Expression newValue) {
+ value = newValue;
+ }
+
accept(NodeVisitor visitor) => visitor.visitInterpolatedExpression(this);
void visitChildren(NodeVisitor visitor) {
@@ -961,24 +973,45 @@
int get precedenceLevel => value.precedenceLevel;
}
+class InterpolatedStatement extends Statement implements InterpolatedNode {
+ Statement value;
+
+ InterpolatedStatement(this.value);
+
+ void assign(Node newValue) {
+ if (newValue is Expression)
+ value = new ExpressionStatement(newValue);
+ else
+ value = newValue;
+ }
+
+ accept(NodeVisitor visitor) => visitor.visitInterpolatedStatement(this);
+
+ void visitChildren(NodeVisitor visitor) {
+ if (value != null) value.accept(visitor);
+ }
+}
+
class JSExpression extends Expression {
Expression value;
- List<InterpolatedExpression> interpolatedExpressions;
+ List<InterpolatedNode> interpolatedNodes;
- JSExpression(this.value, this.interpolatedExpressions);
+ JSExpression(this.value, this.interpolatedNodes);
accept(NodeVisitor visitor) => visitor.visitJSExpression(this);
void visitChildren(NodeVisitor visitor) {
value.accept(visitor);
- for (InterpolatedExpression expression in interpolatedExpressions) {
- expression.accept(visitor);
+ for (InterpolatedNode node in interpolatedNodes) {
+ node.accept(visitor);
}
}
int get precedenceLevel => value.precedenceLevel;
}
+// TODO(sra): JSStatement like JSExpression.
+
/**
* [RegExpLiteral]s, despite being called "Literal", do not inherit from
* [Literal]. Indeed, regular expressions in JavaScript have a side-effect and
diff --git a/sdk/lib/_internal/compiler/implementation/js/printer.dart b/sdk/lib/_internal/compiler/implementation/js/printer.dart
index 855eb1d..975cbf6 100644
--- a/sdk/lib/_internal/compiler/implementation/js/printer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/printer.dart
@@ -553,7 +553,13 @@
String op = binary.op;
int leftPrecedenceRequirement;
int rightPrecedenceRequirement;
+ bool leftSpace = true; // left<HERE>op right
switch (op) {
+ case ',':
+ leftPrecedenceRequirement = EXPRESSION;
+ rightPrecedenceRequirement = LOGICAL_OR;
+ leftSpace = false;
+ break;
case "||":
leftPrecedenceRequirement = LOGICAL_OR;
// x || (y || z) <=> (x || y) || z.
@@ -633,7 +639,7 @@
out(op);
out(" ");
} else {
- spaceOut();
+ if (leftSpace) spaceOut();
out(op);
spaceOut();
}
@@ -881,6 +887,10 @@
visit(node.value);
}
+ visitInterpolatedStatement(InterpolatedStatement node) {
+ visit(node.value);
+ }
+
void visitComment(Comment node) {
if (shouldCompressOutput) return;
String comment = node.comment.trim();
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
index ad0e1ff..6866b25 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -74,25 +74,6 @@
return backend.namer.isolateAccess(element);
}
- // The tags string contains comma-separated 'words' which are either dispatch
- // tags (having JavaScript identifier syntax) and directives that begin with
- // `!`.
- List<String> nativeTagsOfClassRaw(ClassElement cls) {
- String quotedName = cls.nativeTagInfo;
- return quotedName.substring(1, quotedName.length - 1).split(',');
- }
-
- List<String> nativeTagsOfClass(ClassElement cls) {
- return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList();
- }
-
- bool nativeHasTagsMarker(ClassElement cls, String marker) {
- return nativeTagsOfClassRaw(cls).contains(marker);
- }
-
- bool nativeForcedNonLeaf(ClassElement cls) =>
- nativeHasTagsMarker(cls, '!nonleaf');
-
/**
* Writes the class definitions for the interceptors to [mainBuffer].
* Writes code to associate dispatch tags with interceptors to [nativeBuffer].
@@ -185,7 +166,8 @@
} else if (extensionPoints.containsKey(classElement)) {
needed = true;
}
- if (classElement.isNative() && nativeForcedNonLeaf(classElement)) {
+ if (classElement.isNative() &&
+ native.nativeTagsForcedNonLeaf(classElement)) {
needed = true;
nonleafClasses.add(classElement);
}
@@ -206,7 +188,7 @@
for (ClassElement classElement in classes) {
if (!classElement.isNative()) continue;
- List<String> nativeTags = nativeTagsOfClass(classElement);
+ List<String> nativeTags = native.nativeTagsOfClass(classElement);
if (nonleafClasses.contains(classElement) ||
extensionPoints.containsKey(classElement)) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
index 9721322..c5dd94a 100644
--- a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
@@ -233,32 +233,32 @@
// },
// });
- var defineClass = js.fun(['name', 'cls', 'fields'], [
- js('var accessors = []'),
+ var defineClass = js('''function(name, cls, fields) {
+ var accessors = [];
- js('var str = "function " + cls + "("'),
- js('var body = ""'),
+ var str = "function " + cls + "(";
+ var body = "";
- js.for_('var i = 0', 'i < fields.length', 'i++', [
- js.if_('i != 0', js('str += ", "')),
+ for (var i = 0; i < fields.length; i++) {
+ if(i != 0) str += ", ";
- js('var field = generateAccessor(fields[i], accessors, cls)'),
- js('var parameter = "parameter_" + field'),
- js('str += parameter'),
- js('body += ("this." + field + " = " + parameter + ";\\n")')
- ]),
- js('str += ") {\\n" + body + "}\\n"'),
- js('str += cls + ".builtin\$cls=\\"" + name + "\\";\\n"'),
- js('str += "\$desc=\$collectedClasses." + cls + ";\\n"'),
- js('str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n"'),
- js('str += cls + ".prototype = \$desc;\\n"'),
- js.if_(
- 'typeof defineClass.name != "string"',
- [js('str += cls + ".name=\\"" + cls + "\\";\\n"')]),
- js('str += accessors.join("")'),
+ var field = generateAccessor(fields[i], accessors, cls);
+ var parameter = "parameter_" + field;
+ str += parameter;
+ body += ("this." + field + " = " + parameter + ";\\n");
+ }
+ str += ") {\\n" + body + "}\\n";
+ str += cls + ".builtin\$cls=\\"" + name + "\\";\\n";
+ str += "\$desc=\$collectedClasses." + cls + ";\\n";
+ str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n";
+ str += cls + ".prototype = \$desc;\\n";
+ if (typeof defineClass.name != "string") {
+ str += cls + ".name=\\"" + cls + "\\";\\n";
+ }
+ str += accessors.join("");
- js.return_('str')
- ]);
+ return str;
+ }''');
// Declare a function called "generateAccessor". This is used in
// defineClassFunction (it's a local declaration in init()).
return [
@@ -270,23 +270,23 @@
/** Needs defineClass to be defined. */
List buildInheritFrom() {
- return [
- js('var inheritFrom = #',
- js.fun([], [
- new jsAst.FunctionDeclaration(
- new jsAst.VariableDeclaration('tmp'), js.fun([], [])),
- js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
- js.return_(js.fun(['constructor', 'superConstructor'], [
- js('tmp.prototype = superConstructor.prototype'),
- js('var object = new tmp()'),
- js('var properties = constructor.prototype'),
- js.forIn('member', 'properties',
- js.if_('hasOwnProperty.call(properties, member)',
- js('object[member] = properties[member]'))),
- js('object.constructor = constructor'),
- js('constructor.prototype = object'),
- js.return_('object')
- ]))])())];
+ return [js(r'''
+ var inheritFrom = function() {
+ function tmp() {}
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ return function (constructor, superConstructor) {
+ tmp.prototype = superConstructor.prototype;
+ var object = new tmp();
+ var properties = constructor.prototype;
+ for (var member in properties)
+ if (hasOwnProperty.call(properties, member))
+ object[member] = properties[member];
+ object.constructor = constructor;
+ constructor.prototype = object;
+ return object;
+ };
+ }()
+ ''')];
}
jsAst.Fun get finishClassesFunction {
@@ -385,13 +385,15 @@
js.if_('supr', js('pendingClasses[cls] = supr'))
])
]),
- js.if_('typeof dart_precompiled != "function"',
- [js('combinedConstructorFunction +='
- ' "return [\\n " + constructorsList.join(",\\n ") + "\\n]"'),
- js('var constructors ='
- ' new Function("\$collectedClasses", combinedConstructorFunction)'
- '(collectedClasses)'),
- js('combinedConstructorFunction = null')]),
+ js.statement('''
+ if (typeof dart_precompiled != "function") {
+ combinedConstructorFunction +=
+ "return [\\n " + constructorsList.join(",\\n ") + "\\n]";
+ var constructors =
+ new Function("\$collectedClasses", combinedConstructorFunction)
+ (collectedClasses);
+ combinedConstructorFunction = null;
+ }'''),
js.for_('var i = 0', 'i < constructors.length', 'i++', [
js('var constructor = constructors[i]'),
js('var cls = constructor.name'),
@@ -418,8 +420,9 @@
nsmEmitter.addTrivialNsmHandlers(statements);
statements.add(
- js.forIn('cls', 'pendingClasses', js('finishClass(cls)'))
+ js.statement('for (var cls in pendingClasses) finishClass(cls);')
);
+
// function(collectedClasses,
// isolateProperties,
// existingIsolateProperties)
@@ -519,32 +522,34 @@
//
// We also copy over old values like the prototype, and the
// isolateProperties themselves.
- return js.fun('oldIsolate', [
- js('var isolateProperties = oldIsolate.${namer.isolatePropertiesName}'),
- new jsAst.FunctionDeclaration(
- new jsAst.VariableDeclaration('Isolate'),
- js.fun([], [
- js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
- js.forIn('staticName', 'isolateProperties',
- js.if_('hasOwnProperty.call(isolateProperties, staticName)',
- js('this[staticName] = isolateProperties[staticName]'))),
- // Use the newly created object as prototype. In Chrome,
- // this creates a hidden class for the object and makes
- // sure it is fast to access.
- new jsAst.FunctionDeclaration(
- new jsAst.VariableDeclaration('ForceEfficientMap'),
- js.fun([], [])),
- js('ForceEfficientMap.prototype = this'),
- js('new ForceEfficientMap()')])),
- js('Isolate.prototype = oldIsolate.prototype'),
- js('Isolate.prototype.constructor = Isolate'),
- js('Isolate.${namer.isolatePropertiesName} = isolateProperties'),
- optional(needsDefineClass,
- js('Isolate.$finishClassesProperty ='
- ' oldIsolate.$finishClassesProperty')),
- optional(hasMakeConstantList,
- js('Isolate.makeConstantList = oldIsolate.makeConstantList')),
- js.return_('Isolate')]);
+ return js('''
+ function (oldIsolate) {
+ var isolateProperties = oldIsolate.${namer.isolatePropertiesName};
+ function Isolate() {
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ for (var staticName in isolateProperties)
+ if (hasOwnProperty.call(isolateProperties, staticName))
+ this[staticName] = isolateProperties[staticName];
+ // Use the newly created object as prototype. In Chrome,
+ // this creates a hidden class for the object and makes
+ // sure it is fast to access.
+ function ForceEfficientMap() {}
+ ForceEfficientMap.prototype = this;
+ new ForceEfficientMap();
+ }
+ Isolate.prototype = oldIsolate.prototype;
+ Isolate.prototype.constructor = Isolate;
+ Isolate.${namer.isolatePropertiesName} = isolateProperties;
+ #
+ #
+ return Isolate;
+ }''',
+ [ optional(needsDefineClass,
+ js('Isolate.$finishClassesProperty ='
+ ' oldIsolate.$finishClassesProperty')),
+ optional(hasMakeConstantList,
+ js('Isolate.makeConstantList = oldIsolate.makeConstantList'))
+ ]);
}
jsAst.Fun get lazyInitializerFunction {
@@ -949,8 +954,8 @@
int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
- // Emit constant interceptors first. Constant intercpetors for primitives
- // might be used by code that builds other constants. See Issue 19183.
+ // Emit constant interceptors first. Constant interceptors for primitives
+ // might be used by code that builds other constants. See Issue 18173.
if (a.isInterceptor != b.isInterceptor) {
return a.isInterceptor ? -1 : 1;
}
@@ -992,37 +997,38 @@
* Emits code that sets `init.isolateTag` to a unique string.
*/
jsAst.Expression generateIsolateAffinityTagInitialization() {
- return js('!#', js.fun([], [
-
+ return js('''
+ !function() {
// On V8, the 'intern' function converts a string to a symbol, which
// makes property access much faster.
- new jsAst.FunctionDeclaration(new jsAst.VariableDeclaration('intern'),
- js.fun(['s'], [
- js('var o = {}'),
- js('o[s] = 1'),
- js.return_(js('Object.keys(convertToFastObject(o))[0]'))])),
+ function intern(s) {
+ var o = {};
+ o[s] = 1;
+ return Object.keys(convertToFastObject(o))[0];
+ }
-
- js('init.getIsolateTag = #',
- js.fun(['name'],
- js.return_('intern("___dart_" + name + init.isolateTag)'))),
+ init.getIsolateTag = function(name) {
+ return intern("___dart_" + name + init.isolateTag);
+ };
// To ensure that different programs loaded into the same context (page)
// use distinct dispatch properies, we place an object on `Object` to
// contain the names already in use.
- js('var tableProperty = "___dart_isolate_tags_"'),
- js('var usedProperties = Object[tableProperty] ||'
- '(Object[tableProperty] = Object.create(null))'),
+ var tableProperty = "___dart_isolate_tags_";
+ var usedProperties = Object[tableProperty] ||
+ (Object[tableProperty] = Object.create(null));
- js('var rootProperty = "_${generateIsolateTagRoot()}"'),
- js.for_('var i = 0', null, 'i++', [
- js('var property = intern(rootProperty + "_" + i + "_")'),
- js.if_('!(property in usedProperties)', [
- js('usedProperties[property] = 1'),
- js('init.isolateTag = property'),
- new jsAst.Break(null)])])])
- ());
-
+ var rootProperty = "_${generateIsolateTagRoot()}";
+ for (var i = 0; ; i++) {
+ var property = intern(rootProperty + "_" + i + "_");
+ if (!(property in usedProperties)) {
+ usedProperties[property] = 1;
+ init.isolateTag = property;
+ break;
+ }
+ }
+ }()
+ ''');
}
jsAst.Expression generateDispatchPropertyNameInitialization() {
@@ -1406,7 +1412,12 @@
}
}
mainBuffer
- ..write(getReflectionDataParser(classesCollector, backend))
+ ..write('(')
+ ..write(
+ jsAst.prettyPrint(
+ getReflectionDataParser(classesCollector, backend),
+ compiler))
+ ..write(')')
..write('([$n');
List<Element> sortedElements =
@@ -1664,7 +1675,12 @@
'$_${namer.isolateName}.prototype$N$n'
// The classesCollector object ($$).
'$classesCollector$_=$_{};$n')
- ..write(getReflectionDataParser(classesCollector, backend))
+ ..write('(')
+ ..write(
+ jsAst.prettyPrint(
+ getReflectionDataParser(classesCollector, backend),
+ compiler))
+ ..write(')')
..write('([$n')
..addBuffer(outputBuffer)
..write('])$N');
diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
index ffe3b52..59eb43e 100644
--- a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
@@ -15,8 +15,15 @@
const bool VALIDATE_DATA = false;
// TODO(ahe): This code should be integrated in CodeEmitterTask.finishClasses.
-String getReflectionDataParser(String classesCollector,
- JavaScriptBackend backend) {
+jsAst.Expression getReflectionDataParser(String classesCollector,
+ JavaScriptBackend backend) {
+ var text = getReflectionDataParserAsString(classesCollector, backend);
+ return js(text);
+}
+
+
+String getReflectionDataParserAsString(String classesCollector,
+ JavaScriptBackend backend) {
Namer namer = backend.namer;
Compiler compiler = backend.compiler;
Element closureFromTearOff = compiler.findHelper('closureFromTearOff');
@@ -30,7 +37,7 @@
} else {
// Default values for mocked-up test libraries.
tearOffAccess =
- 'function() { throw "Helper \'closureFromTearOff\' missing." }';
+ r'''function() { throw 'Helper \'closureFromTearOff\' missing.' }''';
tearOffGlobalObjectName = 'MissingHelperFunction';
tearOffGlobalObject = '($tearOffAccess())';
}
@@ -47,31 +54,107 @@
String methodsWithOptionalArgumentsField =
namer.methodsWithOptionalArgumentsField;
- String header = '''
-(function (reflectionData) {
- "use strict";
-'''
-// [map] returns an object literal that V8 shouldn't try to optimize with a
-// hidden class. This prevents a potential performance problem where V8 tries
-// to build a hidden class for an object used as a hashMap.
-'''
- function map(x){x={x:x};delete x.x;return x}
-''';
-
String unmangledNameIndex = backend.mustRetainMetadata
? ' 3 * optionalParameterCount + 2 * requiredParameterCount + 3'
: ' 2 * optionalParameterCount + requiredParameterCount + 3';
+
+ String header = '''
+(function (reflectionData) {
+ "use strict";
+
+// [map] returns an object literal that V8 shouldn't try to optimize with a
+// hidden class. This prevents a potential performance problem where V8 tries
+// to build a hidden class for an object used as a hashMap.
+
+ function map(x){x={x:x};delete x.x;return x}
+''';
+
+ String processStatics = '''
+ function processStatics(descriptor) {
+ for (var property in descriptor) {
+ if (!hasOwnProperty.call(descriptor, property)) continue;
+ if (property === "${namer.classDescriptorProperty}") continue;
+ var element = descriptor[property];
+ var firstChar = property.substring(0, 1);
+ var previousProperty;
+ if (firstChar === "+") {
+ mangledGlobalNames[previousProperty] = property.substring(1);
+ var flag = descriptor[property];
+ if (flag > 0)
+ descriptor[previousProperty].$reflectableField = flag;
+ if (element && element.length)
+ init.typeInformation[previousProperty] = element;
+ } else if (firstChar === "@") {
+ property = property.substring(1);
+ ${namer.currentIsolate}[property][$metadataField] = element;
+ } else if (firstChar === "*") {
+ globalObject[previousProperty].$defaultValuesField = element;
+ var optionalMethods = descriptor.$methodsWithOptionalArgumentsField;
+ if (!optionalMethods) {
+ descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {}
+ }
+ optionalMethods[property] = previousProperty;
+ } else if (typeof element === "function") {
+ globalObject[previousProperty = property] = element;
+ functions.push(property);
+ init.globalFunctions[property] = element;
+ } else if (element.constructor === Array) {
+ addStubs(globalObject, element, property,
+ true, descriptor, functions);
+ } else {
+ previousProperty = property;
+ var newDesc = {};
+ var previousProp;
+ for (var prop in element) {
+ if (!hasOwnProperty.call(element, prop)) continue;
+ firstChar = prop.substring(0, 1);
+ if (prop === "static") {
+ processStatics(init.statics[property] = element[prop]);
+ } else if (firstChar === "+") {
+ mangledNames[previousProp] = prop.substring(1);
+ var flag = element[prop];
+ if (flag > 0)
+ element[previousProp].$reflectableField = flag;
+ } else if (firstChar === "@" && prop !== "@") {
+ newDesc[prop.substring(1)][$metadataField] = element[prop];
+ } else if (firstChar === "*") {
+ newDesc[previousProp].$defaultValuesField = element[prop];
+ var optionalMethods = newDesc.$methodsWithOptionalArgumentsField;
+ if (!optionalMethods) {
+ newDesc.$methodsWithOptionalArgumentsField = optionalMethods={}
+ }
+ optionalMethods[prop] = previousProp;
+ } else {
+ var elem = element[prop];
+ if (prop !== "${namer.classDescriptorProperty}" &&
+ elem != null &&
+ elem.constructor === Array &&
+ prop !== "<>") {
+ addStubs(newDesc, elem, prop, false, element, []);
+ } else {
+ newDesc[previousProp = prop] = elem;
+ }
+ }
+ }
+ $classesCollector[property] = [globalObject, newDesc];
+ classes.push(property);
+ }
+ }
+ }
+''';
+
+
/**
* See [dart2js.js_emitter.ContainerBuilder.addMemberMethod] for format of
* [array].
*/
String addStubs = '''
- function addStubs(descriptor, array, name, isStatic,''' // Break long line.
- ''' originalDescriptor, functions) {
- var f, funcs =''' // Break long line.
- ''' [originalDescriptor[name] =''' // Break long line.
- ''' descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}];
+ function addStubs(descriptor, array, name, isStatic,
+ originalDescriptor, functions) {
+ var f, funcs =
+ [originalDescriptor[name] =
+ descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}];
f.\$stubName = name;
functions.push(name);
for (var index = $FUNCTION_INDEX; index < array.length; index += 2) {
@@ -97,8 +180,8 @@
var optionalParameterInfo = ${readInt("array", "1")};
var optionalParameterCount = optionalParameterInfo >> 1;
var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1;
- var isIntercepted =''' // Break long line.
- ''' requiredParameterCount + optionalParameterCount != funcs[0].length;
+ var isIntercepted =
+ requiredParameterCount + optionalParameterCount != funcs[0].length;
var functionTypeIndex = ${readFunctionType("array", "2")};
var unmangledNameIndex = $unmangledNameIndex;
var isReflectable = array.length > unmangledNameIndex;
@@ -107,9 +190,7 @@
f = tearOff(funcs, array, isStatic, name, isIntercepted);
descriptor[name].\$getter = f;
f.\$getterStub = true;
-'''
- /* Used to create an isolate using spawnFunction.*/
-'''
+ // Used to create an isolate using spawnFunction.
if (isStatic) init.globalFunctions[name] = f;
originalDescriptor[getterStubName] = descriptor[getterStubName] = f;
funcs.push(f);
@@ -125,18 +206,16 @@
}
var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames;
var unmangledName = ${readString("array", "unmangledNameIndex")};
-'''
// The function is either a getter, a setter, or a method.
// If it is a method, it might also have a tear-off closure.
// The unmangledName is the same as the getter-name.
-'''
var reflectionName = unmangledName;
if (getterStubName) mangledNames[getterStubName] = reflectionName;
if (isSetter) {
reflectionName += "=";
} else if (!isGetter) {
- reflectionName += ":" + requiredParameterCount +''' // Break long line.
- ''' ":" + optionalParameterCount;
+ reflectionName += ":" + requiredParameterCount +
+ ":" + optionalParameterCount;
}
mangledNames[name] = reflectionName;
funcs[0].$reflectionNameField = reflectionName;
@@ -149,41 +228,41 @@
String tearOff = '''
function tearOffGetterNoCsp(funcs, reflectionInfo, name, isIntercepted) {
return isIntercepted
- ? new Function("funcs", "reflectionInfo", "name",''' // Break long line.
- ''' "$tearOffGlobalObjectName", "c",
+ ? new Function("funcs", "reflectionInfo", "name",
+ "$tearOffGlobalObjectName", "c",
"return function tearOff_" + name + (functionCounter++)+ "(x) {" +
"if (c === null) c = $tearOffAccess(" +
"this, funcs, reflectionInfo, false, [x], name);" +
"return new c(this, funcs[0], x, name);" +
"}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null)
- : new Function("funcs", "reflectionInfo", "name",''' // Break long line.
- ''' "$tearOffGlobalObjectName", "c",
+ : new Function("funcs", "reflectionInfo", "name",
+ "$tearOffGlobalObjectName", "c",
"return function tearOff_" + name + (functionCounter++)+ "() {" +
"if (c === null) c = $tearOffAccess(" +
"this, funcs, reflectionInfo, false, [], name);" +
"return new c(this, funcs[0], null, name);" +
- "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null)
+ "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null);
}
function tearOffGetterCsp(funcs, reflectionInfo, name, isIntercepted) {
var cache = null;
return isIntercepted
? function(x) {
- if (cache === null) cache = $tearOffAccess(''' // Break long line.
- '''this, funcs, reflectionInfo, false, [x], name);
- return new cache(this, funcs[0], x, name)
+ if (cache === null) cache = $tearOffAccess(
+ this, funcs, reflectionInfo, false, [x], name);
+ return new cache(this, funcs[0], x, name);
}
: function() {
- if (cache === null) cache = $tearOffAccess(''' // Break long line.
- '''this, funcs, reflectionInfo, false, [], name);
- return new cache(this, funcs[0], null, name)
- }
+ if (cache === null) cache = $tearOffAccess(
+ this, funcs, reflectionInfo, false, [], name);
+ return new cache(this, funcs[0], null, name);
+ };
}
function tearOff(funcs, reflectionInfo, isStatic, name, isIntercepted) {
var cache;
return isStatic
? function() {
- if (cache === void 0) cache = $tearOffAccess(''' // Break long line.
- '''this, funcs, reflectionInfo, true, [], name).prototype;
+ if (cache === void 0) cache = $tearOffAccess(
+ this, funcs, reflectionInfo, true, [], name).prototype;
return cache;
}
: tearOffGetter(funcs, reflectionInfo, name, isIntercepted);
@@ -211,7 +290,7 @@
var length = reflectionData.length;
for (var i = 0; i < length; i++) {
var data = reflectionData[i];
-'''
+
// [data] contains these elements:
// 0. The library name (not unique).
// 1. The library URI (unique).
@@ -222,7 +301,7 @@
// library is the root library (see dart:mirrors IsolateMirror.rootLibrary).
//
// The entries of [data] are built in [assembleProgram] above.
-'''
+
var name = data[0];
var uri = data[1];
var metadata = data[2];
@@ -234,80 +313,6 @@
var functions = [];
''';
- String processStatics = '''
- function processStatics(descriptor) {
- for (var property in descriptor) {
- if (!hasOwnProperty.call(descriptor, property)) continue;
- if (property === "${namer.classDescriptorProperty}") continue;
- var element = descriptor[property];
- var firstChar = property.substring(0, 1);
- var previousProperty;
- if (firstChar === "+") {
- mangledGlobalNames[previousProperty] = property.substring(1);
- var flag = descriptor[property];
- if (flag > 0) ''' // Break long line.
- '''descriptor[previousProperty].$reflectableField = flag;
- if (element && element.length) ''' // Break long line.
- '''init.typeInformation[previousProperty] = element;
- } else if (firstChar === "@") {
- property = property.substring(1);
- ${namer.currentIsolate}[property][$metadataField] = element;
- } else if (firstChar === "*") {
- globalObject[previousProperty].$defaultValuesField = element;
- var optionalMethods = descriptor.$methodsWithOptionalArgumentsField;
- if (!optionalMethods) {
- descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {}
- }
- optionalMethods[property] = previousProperty;
- } else if (typeof element === "function") {
- globalObject[previousProperty = property] = element;
- functions.push(property);
- init.globalFunctions[property] = element;
- } else if (element.constructor === Array) {
- addStubs(globalObject, element, property, ''' // Break long line.
- '''true, descriptor, functions);
- } else {
- previousProperty = property;
- var newDesc = {};
- var previousProp;
- for (var prop in element) {
- if (!hasOwnProperty.call(element, prop)) continue;
- firstChar = prop.substring(0, 1);
- if (prop === "static") {
- processStatics(init.statics[property] = element[prop]);
- } else if (firstChar === "+") {
- mangledNames[previousProp] = prop.substring(1);
- var flag = element[prop];
- if (flag > 0) ''' // Break long line.
- '''element[previousProp].$reflectableField = flag;
- } else if (firstChar === "@" && prop !== "@") {
- newDesc[prop.substring(1)][$metadataField] = element[prop];
- } else if (firstChar === "*") {
- newDesc[previousProp].$defaultValuesField = element[prop];
- var optionalMethods = newDesc.$methodsWithOptionalArgumentsField;
- if (!optionalMethods) {
- newDesc.$methodsWithOptionalArgumentsField = optionalMethods={}
- }
- optionalMethods[prop] = previousProp;
- } else {
- var elem = element[prop];
- if (prop !== "${namer.classDescriptorProperty}" &&'''
- ''' elem != null &&''' // Break long line.
- ''' elem.constructor === Array &&''' // Break long line.
- ''' prop !== "<>") {
- addStubs(newDesc, elem, prop, false, element, []);
- } else {
- newDesc[previousProp = prop] = elem;
- }
- }
- }
- $classesCollector[property] = [globalObject, newDesc];
- classes.push(property);
- }
- }
- }
-''';
-
String footer = '''
processStatics(descriptor);
libraries.push([name, uri, classes, functions, metadata, fields, isRoot,
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 38577c9..f5caa61 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -537,9 +537,28 @@
class NativeResolutionEnqueuer extends NativeEnqueuerBase {
+ Map<String, ClassElement> tagOwner = new Map<String, ClassElement>();
+
NativeResolutionEnqueuer(Enqueuer world, Compiler compiler)
: super(world, compiler, compiler.enableNativeLiveTypeAnalysis);
+ void processNativeClass(ClassElement classElement) {
+ super.processNativeClass(classElement);
+
+ // Since we map from dispatch tags to classes, a dispatch tag must be used
+ // on only one native class.
+ for (String tag in nativeTagsOfClass(classElement)) {
+ ClassElement owner = tagOwner[tag];
+ if (owner != null) {
+ compiler.reportError(classElement,
+ MessageKind.GENERIC,
+ {'text': "Tag '$tag' already in use by '${owner.name}'"});
+ } else {
+ tagOwner[tag] = classElement;
+ }
+ }
+ }
+
void logSummary(log(message)) {
log('Resolved ${registeredClasses.length} native elements used, '
'${unusedClasses.length} native elements dead.');
@@ -1048,6 +1067,22 @@
return nativeTagInfo;
}
+// The tags string contains comma-separated 'words' which are either dispatch
+// tags (having JavaScript identifier syntax) and directives that begin with
+// `!`.
+List<String> nativeTagsOfClassRaw(ClassElement cls) {
+ String quotedName = cls.nativeTagInfo;
+ return quotedName.substring(1, quotedName.length - 1).split(',');
+}
+
+List<String> nativeTagsOfClass(ClassElement cls) {
+ return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList();
+}
+
+bool nativeTagsForcedNonLeaf(ClassElement cls) =>
+ nativeTagsOfClassRaw(cls).contains('!nonleaf');
+
+
final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
void handleSsaNative(SsaBuilder builder, Expression nativeBody) {
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart b/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
index 8e218ae..ba3fd64 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
@@ -35,7 +35,7 @@
Map<dynamic/* Member | Element */, Set<MessageKind>> reportedMessages =
new Map<dynamic, Set<MessageKind>>();
- MembersCreator(Compiler this.compiler, ClassElement this.cls) {
+ MembersCreator(this.compiler, this.cls) {
assert(invariant(cls, cls.isDeclaration,
message: "Members may only be computed on declarations."));
}
@@ -171,7 +171,7 @@
declaredMembers[name] = new DeclaredMember(
name, element, thisType, type, type);
}
- };
+ }
cls.forEachLocalMember(createMember);
if (cls.isPatched) {
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index d81eb38..b687c2a 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -472,7 +472,7 @@
}
}
element.parseNode(compiler);
- element.computeSignature(compiler);
+ element.computeType(compiler);
if (element.isPatched) {
FunctionElementX patch = element.patch;
compiler.withCurrentElement(patch, () {
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
index e28b110..2b82c5e 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
@@ -1941,6 +1941,8 @@
}
Token parseLiteralString(Token token) {
+ bool old = mayParseFunctionExpressions;
+ mayParseFunctionExpressions = true;
token = parseSingleLiteralString(token);
int count = 1;
while (identical(token.kind, STRING_TOKEN)) {
@@ -1950,6 +1952,7 @@
if (count > 1) {
listener.handleStringJuxtaposition(count);
}
+ mayParseFunctionExpressions = old;
return token;
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart b/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
index ecfd632..f3c3098 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
@@ -64,29 +64,24 @@
return false;
}
- bool canUseSelfForInterceptor(HInstruction instruction,
+ bool canUseSelfForInterceptor(HInstruction receiver,
Set<ClassElement> interceptedClasses) {
JavaScriptBackend backend = compiler.backend;
- if (instruction.canBePrimitive(compiler)) {
+ if (receiver.canBePrimitive(compiler)) {
// Primitives always need interceptors.
return false;
}
- if (instruction.canBeNull()
- && interceptedClasses.contains(backend.jsNullClass)) {
+ if (receiver.canBeNull() &&
+ interceptedClasses.contains(backend.jsNullClass)) {
// Need the JSNull interceptor.
return false;
}
- // [interceptedClasses] is sparse - it is just the classes that define some
- // intercepted method. Their subclasses (that inherit the method) are
- // implicit, so we have to extend them.
- TypeMask receiverType = instruction.instructionType;
- return interceptedClasses
- .where((cls) => cls != compiler.objectClass)
- .map((cls) => backend.classesMixedIntoInterceptedClasses.contains(cls)
- ? new TypeMask.subtype(cls)
- : new TypeMask.subclass(cls))
- .every((mask) => receiverType.intersection(mask, compiler).isEmpty);
+ // All intercepted classes extend `Interceptor`, so if the receiver can't be
+ // a class extending `Interceptor` then it can be called directly.
+ return new TypeMask.nonNullSubclass(backend.jsInterceptorClass)
+ .intersection(receiver.instructionType, compiler)
+ .isEmpty;
}
HInstruction tryComputeConstantInterceptor(
@@ -115,9 +110,9 @@
constantInterceptor = backend.jsStringClass;
} else if (input.isArray(compiler)) {
constantInterceptor = backend.jsArrayClass;
- } else if (input.isNumber(compiler)
- && !interceptedClasses.contains(backend.jsIntClass)
- && !interceptedClasses.contains(backend.jsDoubleClass)) {
+ } else if (input.isNumber(compiler) &&
+ !interceptedClasses.contains(backend.jsIntClass) &&
+ !interceptedClasses.contains(backend.jsDoubleClass)) {
// If the method being intercepted is not defined in [int] or [double] we
// can safely use the number interceptor. This is because none of the
// [int] or [double] methods are called from a method defined on [num].
@@ -144,8 +139,8 @@
// If we just happen to be in an instance method of the constant
// interceptor, `this` is a shorter alias.
- if (constantInterceptor == work.element.getEnclosingClass()
- && graph.thisInstruction != null) {
+ if (constantInterceptor == work.element.getEnclosingClass() &&
+ graph.thisInstruction != null) {
return graph.thisInstruction;
}
@@ -183,9 +178,9 @@
// If we found that we need number, we must still go through all
// uses to check if they require int, or double.
- if (interceptedClasses.contains(backend.jsNumberClass)
- && !(interceptedClasses.contains(backend.jsDoubleClass)
- || interceptedClasses.contains(backend.jsIntClass))) {
+ if (interceptedClasses.contains(backend.jsNumberClass) &&
+ !(interceptedClasses.contains(backend.jsDoubleClass) ||
+ interceptedClasses.contains(backend.jsIntClass))) {
for (HInstruction user in node.usedBy) {
if (user is! HInvoke) continue;
Set<ClassElement> intercepted =
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 3cdfa57..633a2db 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -839,6 +839,14 @@
if (input.isConstant()) {
HConstant constant = input;
if (!constant.constant.isPrimitive) return node;
+ if (constant.constant.isInt) {
+ // Only constant-fold int.toString() when Dart and JS results the same.
+ // TODO(18103): We should be able to remove this work-around when issue
+ // 18103 is resolved by providing the correct string.
+ IntConstant intConstant = constant.constant;
+ // Very conservative range.
+ if (!intConstant.isUInt32()) return node;
+ }
PrimitiveConstant primitive = constant.constant;
return graph.addConstant(constantSystem.createString(
primitive.toDartString()), compiler);
diff --git a/sdk/lib/_internal/compiler/implementation/tracer.dart b/sdk/lib/_internal/compiler/implementation/tracer.dart
index 0ffc3d6..65603c9 100644
--- a/sdk/lib/_internal/compiler/implementation/tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/tracer.dart
@@ -22,7 +22,7 @@
/**
* Dumps the intermediate representation after each phase in a format
- * readable by Hydra IR.
+ * readable by IR Hydra.
*/
class Tracer extends TracerUtil {
Compiler compiler;
@@ -30,10 +30,10 @@
bool traceActive = false;
final EventSink<String> output;
final bool enabled = GENERATE_TRACE;
-
+
Tracer(api.CompilerOutputProvider outputProvider) :
output = GENERATE_TRACE ? outputProvider('dart', 'cfg') : null;
-
+
void traceCompilation(String methodName,
ItemCompilationContext compilationContext,
Compiler compiler) {
@@ -55,14 +55,14 @@
if (irObject is ssa.HGraph) {
new HTracer(output, compiler, context).traceGraph(name, irObject);
}
- else if (irObject is ir.Function) {
+ else if (irObject is ir.FunctionDefinition) {
new IRTracer(output).traceGraph(name, irObject);
}
- else if (irObject is tree.Expression) {
+ else if (irObject is tree.FunctionDefinition) {
new TreeTracer(output).traceGraph(name, irObject);
}
}
-
+
void close() {
if (output != null) {
output.close();
@@ -74,7 +74,7 @@
abstract class TracerUtil {
int indent = 0;
EventSink<String> get output;
-
+
void tag(String tagName, Function f) {
println("begin_$tagName");
@@ -89,11 +89,11 @@
add(string);
add("\n");
}
-
+
void printEmptyProperty(String propertyName) {
println(propertyName);
}
-
+
String formatPrty(x) {
if (x is num) {
return '${x}';
@@ -105,7 +105,7 @@
throw "invalid property type: ${x}";
}
}
-
+
void printProperty(String propertyName, value) {
println("$propertyName ${formatPrty(value)}");
}
diff --git a/sdk/lib/_internal/compiler/implementation/use_unused_api.dart b/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
index aea9845..ea458d7 100644
--- a/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
+++ b/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
@@ -196,6 +196,7 @@
compiler.importHelperLibrary(null);
typeGraphInferrer.getCallersOf(null);
dart_types.Types.sorted(null);
+ new dart_types.Types(compiler, null).copy(compiler);
new universe.TypedSelector.subclass(null, null);
new universe.TypedSelector.subtype(null, null);
new universe.TypedSelector.exact(null, null);
@@ -218,7 +219,7 @@
..visitContinuation(null)
..visitDefinition(null)
..visitExpression(null)
- ..visitFunction(null)
+ ..visitFunctionDefinition(null)
..visitInvokeStatic(null)
..visitLetCont(null)
..visitNode(null)
diff --git a/sdk/lib/_internal/lib/async_patch.dart b/sdk/lib/_internal/lib/async_patch.dart
index aa1c342..c67b3ff 100644
--- a/sdk/lib/_internal/lib/async_patch.dart
+++ b/sdk/lib/_internal/lib/async_patch.dart
@@ -8,7 +8,13 @@
Primitives,
convertDartClosureToJS,
loadDeferredLibrary;
-import 'dart:_isolate_helper' show TimerImpl;
+import 'dart:_isolate_helper' show
+ IsolateNatives,
+ TimerImpl,
+ leaveJsAsync,
+ enterJsAsync,
+ isWorker,
+ globalThis;
import 'dart:_foreign_helper' show JS;
@@ -27,7 +33,34 @@
patch class _AsyncRun {
patch static void _scheduleImmediate(void callback()) {
+ scheduleImmediateClosure(callback);
+ }
+
+ // Lazily initialized.
+ static final Function scheduleImmediateClosure =
+ _initializeScheduleImmediate();
+
+ static Function _initializeScheduleImmediate() {
+ if (JS('', '#.scheduleImmediate', globalThis) != null) {
+ return _scheduleImmediateJsOverride;
+ }
// TODO(9002): don't use the Timer to enqueue the immediate callback.
+ // Also check for other JS options like mutation observer or runImmediate.
+ return _scheduleImmediateWithTimer;
+ }
+
+ static void _scheduleImmediateJsOverride(void callback()) {
+ internalCallback() {
+ leaveJsAsync();
+ callback();
+ };
+ enterJsAsync();
+ JS('void', '#.scheduleImmediate(#)',
+ globalThis,
+ convertDartClosureToJS(internalCallback, 0));
+ }
+
+ static void _scheduleImmediateWithTimer(void callback()) {
_createTimer(Duration.ZERO, callback);
}
}
diff --git a/sdk/lib/_internal/lib/isolate_helper.dart b/sdk/lib/_internal/lib/isolate_helper.dart
index 8a19dc68..9245d07 100644
--- a/sdk/lib/_internal/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/lib/isolate_helper.dart
@@ -280,6 +280,16 @@
// Container with the "on exit" handler send-ports.
var doneHandlers;
+ /**
+ * Queue of functions to call when the current event is complete.
+ *
+ * These events are not just put at the front of the event queue, because
+ * they represent control messages, and should be handled even if the
+ * event queue is paused.
+ */
+ var _scheduledControlEvents;
+ bool _isExecutingEvent = false;
+
/** Whether errors are considered fatal. */
// This doesn't do anything yet. We need to be able to catch uncaught errors
// (oxymoronically) in order to take lethal action. This is waiting for the
@@ -332,15 +342,41 @@
}
void handlePing(SendPort responsePort, int pingType) {
- if (pingType == Isolate.PING_EVENT) {
- _globalState.topEventLoop.enqueue(this, () {
- responsePort.send(null);
- }, "ping");
- } else {
- // There is no difference between PING_ALIVE and PING_CONTROL
- // since we don't handle it before the control event queue.
+ if (pingType == Isolate.IMMEDIATE ||
+ (pingType == Isolate.BEFORE_NEXT_EVENT &&
+ !_isExecutingEvent)) {
responsePort.send(null);
+ return;
}
+ void respond() { responsePort.send(null); }
+ if (pingType == Isolate.AS_EVENT) {
+ _globalState.topEventLoop.enqueue(this, respond, "ping");
+ return;
+ }
+ assert(pingType == Isolate.BEFORE_NEXT_EVENT);
+ if (_scheduledControlEvents == null) {
+ _scheduledControlEvents = new Queue();
+ }
+ _scheduledControlEvents.addLast(respond);
+ }
+
+ void handleKill(Capability authentification, int priority) {
+ if (this.terminateCapability != authentification) return;
+ if (priority == Isolate.IMMEDIATE ||
+ (priority == Isolate.BEFORE_NEXT_EVENT &&
+ !_isExecutingEvent)) {
+ kill();
+ return;
+ }
+ if (priority == Isolate.AS_EVENT) {
+ _globalState.topEventLoop.enqueue(this, kill, "kill");
+ return;
+ }
+ assert(priority == Isolate.BEFORE_NEXT_EVENT);
+ if (_scheduledControlEvents == null) {
+ _scheduledControlEvents = new Queue();
+ }
+ _scheduledControlEvents.addLast(kill);
}
/**
@@ -351,11 +387,18 @@
_globalState.currentContext = this;
this._setGlobals();
var result = null;
+ _isExecutingEvent = true;
try {
result = code();
} finally {
+ _isExecutingEvent = false;
_globalState.currentContext = old;
if (old != null) old._setGlobals();
+ if (_scheduledControlEvents != null) {
+ while (_scheduledControlEvents.isNotEmpty) {
+ (_scheduledControlEvents.removeFirst())();
+ }
+ }
}
return result;
}
@@ -364,6 +407,13 @@
JS_SET_CURRENT_ISOLATE(isolateStatics);
}
+ /**
+ * Handle messages comming in on the control port.
+ *
+ * These events do not go through the event queue.
+ * The `_globalState.currentContext` context is not set to this context
+ * during the handling.
+ */
void handleControlMessage(message) {
switch (message[0]) {
case "pause":
@@ -384,8 +434,10 @@
case "ping":
handlePing(message[1], message[2]);
break;
+ case "kill":
+ handleKill(message[1], message[2]);
+ break;
default:
- print("UNKNOWN MESSAGE: $message");
}
}
@@ -419,18 +471,30 @@
if (ports.length - weakPorts.length > 0 || isPaused) {
_globalState.isolates[id] = this; // indicate this isolate is active
} else {
- _shutdown();
+ kill();
}
}
- void _shutdown() {
+ void kill() {
+ if (_scheduledControlEvents != null) {
+ // Kill all pending events.
+ _scheduledControlEvents.clear();
+ }
+ // Stop listening on all ports.
+ // This should happen before sending events to done handlers, in case
+ // we are listening on ourselves.
+ // Closes all ports, including control port.
+ for (var port in ports.values) {
+ port._close();
+ }
+ ports.clear();
+ weakPorts.clear();
_globalState.isolates.remove(id); // indicate this isolate is not active
- // Send "done" event to all listeners. This must be done after deactivating
- // the current isolate, or it may get events if listening to itself.
if (doneHandlers != null) {
for (SendPort port in doneHandlers) {
port.send(null);
}
+ doneHandlers = null;
}
}
@@ -1046,6 +1110,13 @@
_handler = newHandler;
}
+ // Close the port without unregistering it.
+ // Used by an isolate context to close all ports when shutting down.
+ void _close() {
+ _isClosed = true;
+ _handler = null;
+ }
+
void close() {
if (_isClosed) return;
_isClosed = true;
diff --git a/sdk/lib/_internal/lib/native_helper.dart b/sdk/lib/_internal/lib/native_helper.dart
index 6608481..3d7e17b 100644
--- a/sdk/lib/_internal/lib/native_helper.dart
+++ b/sdk/lib/_internal/lib/native_helper.dart
@@ -608,6 +608,7 @@
"BeforeUnloadEvent": "Event",
"DataTransfer": "Clipboard",
"GeoGeolocation": "Geolocation",
+ "Location": "!Location", // Fixes issue 18151
"WorkerMessageEvent": "MessageEvent",
"XMLDocument": "!Document"};
diff --git a/sdk/lib/_internal/lib/preambles/d8.js b/sdk/lib/_internal/lib/preambles/d8.js
index 2b20e49..e965083 100644
--- a/sdk/lib/_internal/lib/preambles/d8.js
+++ b/sdk/lib/_internal/lib/preambles/d8.js
@@ -3,3 +3,258 @@
// BSD-style license that can be found in the LICENSE file.
// Javascript preamble, that lets the output of dart2js run on V8's d8 shell.
+
+var setTimeout;
+var clearTimeout;
+var setInterval;
+var clearInterval;
+var dartMainRunner;
+var scheduleImmediate;
+
+(function() {
+ // Event loop.
+
+ // Task queue as cyclic list queue.
+ var taskQueue = new Array(8); // Length is power of 2.
+ var head = 0;
+ var tail = 0;
+ var mask = taskQueue.length - 1;
+ function addTask(elem) {
+ taskQueue[head] = elem;
+ head = (head + 1) & mask;
+ if (head == tail) _growTaskQueue();
+ }
+ function removeTask() {
+ if (head == tail) return;
+ var result = taskQueue[tail];
+ taskQueue[tail] = undefined;
+ tail = (tail + 1) & mask;
+ return result;
+ }
+ function _growTaskQueue() {
+ // head == tail.
+ var length = taskQueue.length;
+ var split = head;
+ taskQueue.length = length * 2;
+ if (split * 2 < length) { // split < length / 2
+ for (var i = 0; i < split; i++) {
+ taskQueue[length + i] = taskQueue[i];
+ taskQueue[i] = undefined;
+ }
+ head += length;
+ } else {
+ for (var i = split; i < length; i++) {
+ taskQueue[length + i] = taskQueue[i];
+ taskQueue[i] = undefined;
+ }
+ tail += length;
+ }
+ mask = taskQueue.length - 1;
+ }
+
+ // Mapping from timer id to timer function.
+ // The timer id is written on the function as .$timerId.
+ // That field is cleared when the timer is cancelled, but it is not returned
+ // from the queue until its time comes.
+ var timerIds = {};
+ var timerIdCounter = 1; // Counter used to assing ids.
+
+ // Zero-timer queue as simple array queue using push/shift.
+ var zeroTimerQueue = [];
+
+ function addTimer(f, ms) {
+ var id = timerIdCounter++;
+ f.$timerId = id;
+ timerIds[id] = f;
+ if (ms == 0) {
+ zeroTimerQueue.push(f);
+ } else {
+ addDelayedTimer(f, ms);
+ }
+ return id;
+ }
+
+ function nextZeroTimer() {
+ while (zeroTimerQueue.length > 0) {
+ var action = zeroTimerQueue.shift();
+ if (action.$timerId !== undefined) return action;
+ }
+ }
+
+ function nextEvent() {
+ var action = removeTask();
+ if (action) {
+ return action;
+ }
+ do {
+ action = nextZeroTimer();
+ if (action) break;
+ var nextList = nextDelayedTimerQueue();
+ if (!nextList) {
+ return;
+ }
+ var newTime = nextList.shift();
+ advanceTimeTo(newTime);
+ zeroTimerQueue = nextList;
+ } while (true)
+ var id = action.$timerId;
+ clearTimerId(action, id);
+ return action;
+ }
+
+ // Mocking time.
+ var timeOffset = 0;
+ var now = function() {
+ // Install the mock Date object only once.
+ // Following calls to "now" will just use the new (mocked) Date.now
+ // method directly.
+ installMockDate();
+ now = Date.now;
+ return Date.now();
+ };
+ var originalDate = Date;
+ var originalNow = originalDate.now;
+ function advanceTimeTo(time) {
+ timeOffset = time - originalNow();
+ }
+ function installMockDate() {
+ var NewDate = function Date(Y, M, D, h, m, s, ms) {
+ if (this instanceof Date) {
+ // Assume a construct call.
+ switch (arguments.length) {
+ case 0: return new originalDate(originalNow() + timeOffset);
+ case 1: return new originalDate(Y);
+ case 2: return new originalDate(Y, M);
+ case 3: return new originalDate(Y, M, D);
+ case 4: return new originalDate(Y, M, D, h);
+ case 5: return new originalDate(Y, M, D, h, m);
+ case 6: return new originalDate(Y, M, D, h, m, s);
+ default: return new originalDate(Y, M, D, h, m, s, ms);
+ }
+ }
+ return new originalDate(originalNow() + timeOffset).toString();
+ };
+ NewDate.UTC = originalDate.UTC;
+ NewDate.parse = originalDate.parse;
+ NewDate.now = function now() { return originalNow() + timeOffset; };
+ NewDate.prototype = originalDate.prototype;
+ originalDate.prototype.constructor = NewDate;
+ Date = NewDate;
+ }
+
+ // Heap priority queue with key index.
+ // Each entry is list of [timeout, callback1 ... callbackn].
+ var timerHeap = [];
+ var timerIndex = {};
+ function addDelayedTimer(f, ms) {
+ var timeout = now() + ms;
+ var timerList = timerIndex[timeout];
+ if (timerList == null) {
+ timerList = [timeout, f];
+ timerIndex[timeout] = timerList;
+ var index = timerHeap.length;
+ timerHeap.length += 1;
+ bubbleUp(index, timeout, timerList);
+ } else {
+ timerList.push(f);
+ }
+ }
+
+ function nextDelayedTimerQueue() {
+ if (timerHeap.length == 0) return null;
+ var result = timerHeap[0];
+ var last = timerHeap.pop();
+ if (timerHeap.length > 0) {
+ bubbleDown(0, last[0], last);
+ }
+ return result;
+ }
+
+ function bubbleUp(index, key, value) {
+ while (index != 0) {
+ var parentIndex = (index - 1) >> 1;
+ var parent = timerHeap[parentIndex];
+ var parentKey = parent[0];
+ if (key > parentKey) break;
+ timerHeap[index] = parent;
+ index = parentIndex;
+ }
+ timerHeap[index] = value;
+ }
+
+ function bubbleDown(index, key, value) {
+ while (true) {
+ var leftChildIndex = index * 2 + 1;
+ if (leftChildIndex >= timerHeap.length) break;
+ var minChildIndex = leftChildIndex;
+ var minChild = timerHeap[leftChildIndex];
+ var minChildKey = minChild[0];
+ var rightChildIndex = leftChildIndex + 1;
+ if (rightChildIndex < timerHeap.length) {
+ var rightChild = timerHeap[rightChildIndex];
+ var rightKey = rightChild[0];
+ if (rightKey < minChildKey) {
+ minChildIndex = rightChildIndex;
+ minChild = rightChild;
+ minChildKey = rightKey;
+ }
+ }
+ if (minChildKey > key) break;
+ timerHeap[index] = minChild;
+ index = minChildIndex;
+ }
+ timerHeap[index] = value;
+ }
+
+ function addInterval(f, ms) {
+ var id = timerIdCounter++;
+ function repeat() {
+ // Reactivate with the same id.
+ repeat.$timerId = id;
+ timerIds[id] = repeat;
+ addDelayedTimer(repeat, ms);
+ f();
+ }
+ repeat.$timerId = id;
+ timerIds[id] = repeat;
+ addDelayedTimer(repeat, ms);
+ return id;
+ }
+
+ function cancelTimer(id) {
+ var f = timerIds[id];
+ if (f == null) return;
+ clearTimerId(f, id);
+ }
+
+ function clearTimerId(f, id) {
+ f.$timerId = undefined;
+ delete timerIds[id];
+ }
+
+ function eventLoop(action) {
+ while (action) {
+ try {
+ action();
+ } catch (e) {
+ if (typeof onerror == "function") {
+ onerror(e, null, -1);
+ } else {
+ throw e;
+ }
+ }
+ action = nextEvent();
+ }
+ }
+
+ dartMainRunner = function(main, args) {
+ // Initialize.
+ var action = function() { main(args); }
+ eventLoop(action);
+ };
+ setTimeout = addTimer;
+ clearTimeout = cancelTimer;
+ setInterval = addInterval;
+ clearInterval = cancelTimer;
+ scheduleImmediate = addTask;
+})();
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index cb6e7b2..8df3421 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -94,6 +94,10 @@
maturity: Maturity.UNSTABLE,
dart2jsPatchPath: "_internal/lib/mirrors_patch.dart"),
+ "profiler": const LibraryInfo(
+ "profiler/profiler.dart",
+ maturity: Maturity.UNSTABLE),
+
"nativewrappers": const LibraryInfo(
"html/dartium/nativewrappers.dart",
category: "Client",
diff --git a/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart b/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
index cc5e052..38e2ff1 100644
--- a/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
+++ b/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
@@ -15,20 +15,19 @@
import '../serialize.dart';
import '../utils.dart';
-/// Converts [transform] into a serializable map.
-Map serializeTransform(Transform transform) {
+/// Serialize the methods shared between [Transform] and [DeclaringTransform].
+///
+/// [additionalFields] contains additional serialized fields to add to the
+/// serialized transform. [methodHandlers] is a set of additional methods. Each
+/// value should take a JSON message and return the response (which may be a
+/// Future).
+Map _serializeBaseTransform(transform, Map additionalFields,
+ Map<String, Function> methodHandlers) {
var receivePort = new ReceivePort();
receivePort.listen((wrappedMessage) {
respond(wrappedMessage, (message) {
- if (message['type'] == 'getInput') {
- return transform.getInput(deserializeId(message['id']))
- .then((asset) => serializeAsset(asset));
- }
-
- if (message['type'] == 'addOutput') {
- transform.addOutput(deserializeAsset(message['output']));
- return null;
- }
+ var handler = methodHandlers[message['type']];
+ if (handler != null) return handler(message);
if (message['type'] == 'consumePrimary') {
transform.consumePrimary();
@@ -36,17 +35,13 @@
}
assert(message['type'] == 'log');
- var method;
- if (message['level'] == 'Info') {
- method = transform.logger.info;
- } else if (message['level'] == 'Fine') {
- method = transform.logger.fine;
- } else if (message['level'] == 'Warning') {
- method = transform.logger.warning;
- } else {
- assert(message['level'] == 'Error');
- method = transform.logger.error;
- }
+ var method = {
+ 'Info': transform.logger.info,
+ 'Fine': transform.logger.fine,
+ 'Warning': transform.logger.warning,
+ 'Error': transform.logger.error
+ }[message['level']];
+ assert(method != null);
var assetId = message['assetId'] == null ? null :
deserializeId(message['assetId']);
@@ -56,30 +51,43 @@
});
});
- return {
- 'port': receivePort.sendPort,
- 'primaryInput': serializeAsset(transform.primaryInput)
- };
+ return {'port': receivePort.sendPort}..addAll(additionalFields);
}
-/// A wrapper for a [Transform] that's in the host isolate.
-///
-/// This retrieves inputs from and sends outputs and logs to the host isolate.
-class ForeignTransform implements Transform {
+/// Converts [transform] into a serializable map.
+Map serializeTransform(Transform transform) {
+ return _serializeBaseTransform(transform, {
+ 'primaryInput': serializeAsset(transform.primaryInput)
+ }, {
+ 'getInput': (message) => transform.getInput(deserializeId(message['id']))
+ .then((asset) => serializeAsset(asset)),
+ 'addOutput': (message) =>
+ transform.addOutput(deserializeAsset(message['output']))
+ });
+}
+
+/// Converts [transform] into a serializable map.
+Map serializeDeclaringTransform(DeclaringTransform transform) {
+ return _serializeBaseTransform(transform, {
+ 'primaryId': serializeId(transform.primaryId)
+ }, {
+ 'declareOutput': (message) =>
+ transform.declareOutput(deserializeId(message['output']))
+ });
+}
+
+/// The base class for wrappers for [Transform]s that are in the host isolate.
+class _ForeignBaseTransform {
/// The port with which we communicate with the host isolate.
///
/// This port and all messages sent across it are specific to this transform.
final SendPort _port;
- final Asset primaryInput;
-
TransformLogger get logger => _logger;
TransformLogger _logger;
- /// Creates a transform from a serializable map sent from the host isolate.
- ForeignTransform(Map transform)
- : _port = transform['port'],
- primaryInput = deserializeAsset(transform['primaryInput']) {
+ _ForeignBaseTransform(Map transform)
+ : _port = transform['port'] {
_logger = new TransformLogger((assetId, level, message, span) {
call(_port, {
'type': 'log',
@@ -91,6 +99,22 @@
});
}
+ void consumePrimary() {
+ call(_port, {'type': 'consumePrimary'});
+ }
+}
+
+/// A wrapper for a [Transform] that's in the host isolate.
+///
+/// This retrieves inputs from and sends outputs and logs to the host isolate.
+class ForeignTransform extends _ForeignBaseTransform implements Transform {
+ final Asset primaryInput;
+
+ /// Creates a transform from a serialized map sent from the host isolate.
+ ForeignTransform(Map transform)
+ : primaryInput = deserializeAsset(transform['primaryInput']),
+ super(transform);
+
Future<Asset> getInput(AssetId id) {
return call(_port, {
'type': 'getInput',
@@ -119,8 +143,22 @@
'output': serializeAsset(output)
});
}
+}
- void consumePrimary() {
- call(_port, {'type': 'consumePrimary'});
+/// A wrapper for a [DeclaringTransform] that's in the host isolate.
+class ForeignDeclaringTransform extends _ForeignBaseTransform
+ implements DeclaringTransform {
+ final AssetId primaryId;
+
+ /// Creates a transform from a serializable map sent from the host isolate.
+ ForeignDeclaringTransform(Map transform)
+ : primaryId = deserializeId(transform['primaryId']),
+ super(transform);
+
+ void declareOutput(AssetId id) {
+ call(_port, {
+ 'type': 'declareOutput',
+ 'output': serializeId(id)
+ });
}
}
diff --git a/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart b/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
index e6e030d..a7d3937 100644
--- a/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
+++ b/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
@@ -7,19 +7,21 @@
import 'dart:isolate';
import 'package:barback/barback.dart';
-// TODO(nweiz): don't import from "src" once issue 14966 is fixed.
-import 'package:barback/src/internal_asset.dart';
import '../serialize.dart';
import 'transform.dart';
/// Converts [transformer] into a serializable map.
-Map serializeTransformer(Transformer transformer) {
+Map _serializeTransformer(Transformer transformer) {
var port = new ReceivePort();
port.listen((wrappedMessage) {
respond(wrappedMessage, (message) {
if (message['type'] == 'isPrimary') {
return transformer.isPrimary(deserializeId(message['id']));
+ } else if (message['type'] == 'declareOutputs') {
+ return (transformer as DeclaringTransformer).declareOutputs(
+ new ForeignDeclaringTransform(message['transform']))
+ .then((_) => null);
} else {
assert(message['type'] == 'apply');
@@ -31,15 +33,24 @@
});
});
+ var type;
+ if (transformer is LazyTransformer) {
+ type = 'LazyTransformer';
+ } else if (transformer is DeclaringTransformer) {
+ type = 'DeclaringTransformer';
+ } else {
+ type = 'Transformer';
+ }
+
return {
- 'type': 'Transformer',
+ 'type': type,
'toString': transformer.toString(),
'port': port.sendPort
};
}
// Converts [group] into a serializable map.
-Map serializeTransformerGroup(TransformerGroup group) {
+Map _serializeTransformerGroup(TransformerGroup group) {
return {
'type': 'TransformerGroup',
'toString': group.toString(),
@@ -52,9 +63,9 @@
/// Converts [transformerOrGroup] into a serializable map.
Map serializeTransformerOrGroup(transformerOrGroup) {
if (transformerOrGroup is Transformer) {
- return serializeTransformer(transformerOrGroup);
+ return _serializeTransformer(transformerOrGroup);
} else {
assert(transformerOrGroup is TransformerGroup);
- return serializeTransformerGroup(transformerOrGroup);
+ return _serializeTransformerGroup(transformerOrGroup);
}
}
diff --git a/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart b/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
index 5b6dfa7..3b66841 100644
--- a/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
+++ b/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
@@ -29,36 +29,53 @@
/// Loads all the transformers and groups defined in [uri].
///
-/// Loads the library, finds any Transformer or TransformerGroup subclasses in
-/// it, instantiates them with [configuration] and [mode], and returns them.
-Iterable _initialize(Uri uri, Map configuration, BarbackMode mode) {
+/// Loads the library, finds any [Transformer] or [TransformerGroup] subclasses
+/// in it, instantiates them with [configuration] and [mode], and returns them.
+List _initialize(Uri uri, Map configuration, BarbackMode mode) {
var mirrors = currentMirrorSystem();
var transformerClass = reflectClass(Transformer);
var groupClass = reflectClass(TransformerGroup);
- // TODO(nweiz): if no valid transformers are found, throw an error message
- // describing candidates and why they were rejected.
- return mirrors.libraries[uri].declarations.values.map((declaration) {
- if (declaration is! ClassMirror) return null;
- var classMirror = declaration;
- if (classMirror.isPrivate) return null;
- if (classMirror.isAbstract) return null;
- if (!classMirror.isSubtypeOf(transformerClass) &&
- !classMirror.isSubtypeOf(groupClass)) {
- return null;
+ var seen = new Set();
+ var transformers = [];
+
+ loadFromLibrary(library) {
+ if (seen.contains(library)) return;
+ seen.add(library);
+
+ // Load transformers from libraries exported by [library].
+ for (var dependency in library.libraryDependencies) {
+ if (!dependency.isExport) continue;
+ loadFromLibrary(dependency.targetLibrary);
}
- var constructor = _getConstructor(classMirror, 'asPlugin');
- if (constructor == null) return null;
- if (constructor.parameters.isEmpty) {
- if (configuration.isNotEmpty) return null;
- return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
- }
- if (constructor.parameters.length != 1) return null;
+ // TODO(nweiz): if no valid transformers are found, throw an error message
+ // describing candidates and why they were rejected.
+ transformers.addAll(library.declarations.values.map((declaration) {
+ if (declaration is! ClassMirror) return null;
+ var classMirror = declaration;
+ if (classMirror.isPrivate) return null;
+ if (classMirror.isAbstract) return null;
+ if (!classMirror.isSubtypeOf(transformerClass) &&
+ !classMirror.isSubtypeOf(groupClass)) {
+ return null;
+ }
- return classMirror.newInstance(const Symbol('asPlugin'),
- [new BarbackSettings(configuration, mode)]).reflectee;
- }).where((classMirror) => classMirror != null);
+ var constructor = _getConstructor(classMirror, 'asPlugin');
+ if (constructor == null) return null;
+ if (constructor.parameters.isEmpty) {
+ if (configuration.isNotEmpty) return null;
+ return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
+ }
+ if (constructor.parameters.length != 1) return null;
+
+ return classMirror.newInstance(const Symbol('asPlugin'),
+ [new BarbackSettings(configuration, mode)]).reflectee;
+ }).where((classMirror) => classMirror != null));
+ }
+
+ loadFromLibrary(mirrors.libraries[uri]);
+ return transformers;
}
// TODO(nweiz): clean this up when issue 13248 is fixed.
diff --git a/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart b/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
index f07b7fa..f31d4d2 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
@@ -10,8 +10,8 @@
import 'package:stack_trace/stack_trace.dart';
import '../log.dart' as log;
+import 'asset_environment.dart';
import 'base_server.dart';
-import 'build_environment.dart';
import 'web_socket_api.dart';
/// The web admin interface to pub serve.
@@ -22,7 +22,7 @@
final _webSockets = new Set<WebSocket>();
/// Creates a new server and binds it to [port] of [host].
- static Future<AdminServer> bind(BuildEnvironment environment,
+ static Future<AdminServer> bind(AssetEnvironment environment,
String host, int port) {
return Chain.track(HttpServer.bind(host, port)).then((server) {
log.fine('Bound admin server to $host:$port.');
@@ -30,7 +30,7 @@
});
}
- AdminServer._(BuildEnvironment environment, HttpServer server)
+ AdminServer._(AssetEnvironment environment, HttpServer server)
: super(environment, server);
/// Closes the server and all Web Socket connections.
diff --git a/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart b/sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
similarity index 92%
rename from sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
rename to sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
index a332dd5..8bed911 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
@@ -2,7 +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.
-library pub.barback.build_environment;
+library pub.barback.asset_environment;
import 'dart:async';
import 'dart:io';
@@ -19,20 +19,21 @@
import '../package_graph.dart';
import '../sdk.dart' as sdk;
import 'admin_server.dart';
-import 'build_directory.dart';
+import 'barback_server.dart';
import 'dart_forwarding_transformer.dart';
import 'dart2js_transformer.dart';
import 'load_all_transformers.dart';
import 'pub_package_provider.dart';
-import 'barback_server.dart';
+import 'source_directory.dart';
/// The entire "visible" state of the assets of a package and all of its
/// dependencies, taking into account the user's configuration when running pub.
///
/// Where [PackageGraph] just describes the entrypoint's dependencies as
/// specified by pubspecs, this includes "transient" information like the mode
-/// that the user is running pub in, or which directories they want to build.
-class BuildEnvironment {
+/// that the user is running pub in, or which directories they want to
+/// transform.
+class AssetEnvironment {
/// Creates a new build environment for working with the assets used by
/// [entrypoint] and its dependencies.
///
@@ -53,7 +54,7 @@
///
/// Returns a [Future] that completes to the environment once the inputs,
/// transformers, and server are loaded and ready.
- static Future<BuildEnvironment> create(Entrypoint entrypoint,
+ static Future<AssetEnvironment> create(Entrypoint entrypoint,
String hostname, int basePort, BarbackMode mode, WatcherType watcherType,
{bool useDart2JS: true}) {
return entrypoint.loadPackageGraph().then((graph) {
@@ -61,7 +62,7 @@
var barback = new Barback(new PubPackageProvider(graph));
barback.log.listen(_log);
- var environment = new BuildEnvironment._(graph, barback, mode,
+ var environment = new AssetEnvironment._(graph, barback, mode,
watcherType, hostname, basePort);
return environment._load(useDart2JS: useDart2JS)
@@ -72,9 +73,9 @@
/// The server for the Web Socket API and admin interface.
AdminServer _adminServer;
- /// The public directories in the root package that are available for
- /// building, keyed by their root directory.
- final _directories = new Map<String, BuildDirectory>();
+ /// The public directories in the root package that are included in the asset
+ /// environment, keyed by their root directory.
+ final _directories = new Map<String, SourceDirectory>();
/// The [Barback] instance used to process assets in this environment.
final Barback barback;
@@ -120,7 +121,7 @@
/// go to barback immediately.
Set<AssetId> _modifiedSources;
- BuildEnvironment._(this.graph, this.barback, this.mode, this._watcherType,
+ AssetEnvironment._(this.graph, this.barback, this.mode, this._watcherType,
this._hostname, this._basePort);
/// Gets the built-in [Transformer]s that should be added to [package].
@@ -190,14 +191,14 @@
}
}
- var buildDirectory = new BuildDirectory(
+ var sourceDirectory = new SourceDirectory(
this, rootDirectory, _hostname, port);
- _directories[rootDirectory] = buildDirectory;
+ _directories[rootDirectory] = sourceDirectory;
return _provideDirectorySources(rootPackage, rootDirectory)
.then((subscription) {
- buildDirectory.watchSubscription = subscription;
- return buildDirectory.serve();
+ sourceDirectory.watchSubscription = subscription;
+ return sourceDirectory.serve();
});
}
@@ -213,20 +214,18 @@
return directory.server.then((server) {
var url = server.url;
- return directory.close().then((_) {
- // Remove the sources from barback, unless some other build directory
- // includes them.
- return _removeDirectorySources(rootDirectory);
- }).then((_) => url);
+ return directory.close()
+ .then((_) => _removeDirectorySources(rootDirectory))
+ .then((_) => url);
});
}
- /// Gets the build directory that contains [assetPath] within the entrypoint
+ /// Gets the source directory that contains [assetPath] within the entrypoint
/// package.
///
- /// If [assetPath] is not contained within a build directory, this will
- /// throw an exception.
- String getBuildDirectoryContaining(String assetPath) =>
+ /// If [assetPath] is not contained within a source directory, this throws
+ /// an exception.
+ String getSourceDirectoryContaining(String assetPath) =>
_directories.values
.firstWhere((dir) => path.isWithin(dir.directory, assetPath))
.directory;
@@ -496,16 +495,9 @@
});
}
- /// Removes all of the files in [dir] in the root package from barback unless
- /// some other build directory still contains them.
+ /// Removes all of the files in [dir] in the root package from barback.
Future _removeDirectorySources(String dir) {
- return _listDirectorySources(rootPackage, dir, where: (relative) {
- // TODO(rnystrom): This is O(n*m) where n is the number of files and
- // m is the number of served directories. Consider something more
- // optimal if this becomes a bottleneck.
- // Don't remove a source if some other directory still includes it.
- return !_directories.keys.any((dir) => path.isWithin(dir, relative));
- }).then((ids) {
+ return _listDirectorySources(rootPackage, dir).then((ids) {
if (_modifiedSources == null) {
barback.removeSources(ids);
} else {
@@ -519,12 +511,7 @@
/// For large packages, listing the contents is a performance bottleneck, so
/// this is optimized for our needs in here instead of using the more general
/// but slower [listDir].
- ///
- /// If [where] is given, then it is used to filter the resulting list of
- /// packages. Only assets whose relative path within [package] matches that
- /// will be included in the results.
- Future<List<AssetId>> _listDirectorySources(Package package, String dir,
- {bool where(String relativePath)}) {
+ Future<List<AssetId>> _listDirectorySources(Package package, String dir) {
var subdirectory = path.join(package.dir, dir);
if (!dirExists(subdirectory)) return new Future.value([]);
@@ -556,8 +543,6 @@
if (relative.endsWith(".dart.js.map")) return [];
if (relative.endsWith(".dart.precompiled.js")) return [];
- if (where != null && !where(relative)) return [];
-
return [new AssetId(package.name, relative)];
}).toList();
}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart b/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
index 611d62f..413f929 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
@@ -16,7 +16,7 @@
import '../log.dart' as log;
import '../utils.dart';
import 'base_server.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
/// Callback for determining if an asset with [id] should be served or not.
typedef bool AllowAsset(AssetId id);
@@ -40,16 +40,11 @@
/// If this is `null`, all assets may be served.
AllowAsset allowAsset;
- // TODO(rnystrom): Remove this when the Editor is using the admin server.
- // port. See #17640.
- /// All currently open [WebSocket] connections.
- final _webSockets = new Set<WebSocket>();
-
/// Creates a new server and binds it to [port] of [host].
///
/// This server will serve assets from [barback], and use [rootDirectory] as
/// the root directory.
- static Future<BarbackServer> bind(BuildEnvironment environment,
+ static Future<BarbackServer> bind(AssetEnvironment environment,
String host, int port, String rootDirectory) {
return Chain.track(HttpServer.bind(host, port)).then((server) {
log.fine('Bound "$rootDirectory" to $host:$port.');
@@ -57,19 +52,10 @@
});
}
- BarbackServer._(BuildEnvironment environment, HttpServer server,
+ BarbackServer._(AssetEnvironment environment, HttpServer server,
this.rootDirectory)
: super(environment, server);
- // TODO(rnystrom): Remove this when the Editor is using the admin server.
- // port. See #17640.
- /// Closes the server.
- Future close() {
- var futures = [super.close()];
- futures.addAll(_webSockets.map((socket) => socket.close()));
- return Future.wait(futures);
- }
-
/// Converts a [url] served by this server into an [AssetId] that can be
/// requested from barback.
AssetId urlToId(Uri url) {
@@ -117,6 +103,25 @@
return;
}
+ // TODO(rnystrom): Remove this when #16647 is fixed.
+ // The "assets" path is deprecated so warn if the user is relying on it.
+ // We do this here in pub serve so we only warn if they in fact actually
+ // use an asset from a package's "asset" directory.
+ if (id.path.startsWith("asset/")) {
+ var message = 'Warning: Support for the "asset" directory is deprecated '
+ 'and will be removed soon.\n';
+
+ var fixed = id.path.replaceAll(new RegExp(r"^asset/"), "lib/");
+ if (id.package == environment.rootPackage.name) {
+ message += 'Please move "${id.path}" to "$fixed".';
+ } else {
+ message += 'Please ask the maintainer of "${id.package}" to move '
+ '"${id.path}" to "$fixed".';
+ }
+
+ log.warning(log.yellow(message));
+ }
+
logRequest(request, "Loading $id");
environment.barback.getAssetById(id).then((result) {
logRequest(request, "getAssetById($id) returned");
diff --git a/sdk/lib/_internal/pub/lib/src/barback/base_server.dart b/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
index dcf739d..0dcc936 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
@@ -13,12 +13,12 @@
import '../log.dart' as log;
import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
/// Base class for a pub-controlled server.
abstract class BaseServer<T> {
/// The [BuildEnvironment] being served.
- final BuildEnvironment environment;
+ final AssetEnvironment environment;
/// The underlying HTTP server.
final HttpServer _server;
diff --git a/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart b/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
index ca71b15..9a71186 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
@@ -6,7 +6,6 @@
import 'dart:async';
import 'dart:convert';
-import 'dart:io';
import 'package:analyzer/analyzer.dart';
import 'package:barback/barback.dart';
@@ -21,7 +20,7 @@
import '../dart.dart' as dart;
import '../pool.dart';
import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
/// The set of all valid configuration options for this transformer.
final _validOptions = new Set<String>.from([
@@ -40,7 +39,7 @@
/// is here: https://code.google.com/p/dart/issues/detail?id=14730.
static final _pool = new Pool(1);
- final BuildEnvironment _environment;
+ final AssetEnvironment _environment;
final BarbackSettings _settings;
/// Whether source maps should be generated for the compiled JS.
@@ -56,7 +55,7 @@
"${toSentence(invalidOptions.map((option) => '"$option"'))}.");
}
- Dart2JSTransformer(BuildEnvironment environment, BarbackMode mode)
+ Dart2JSTransformer(AssetEnvironment environment, BarbackMode mode)
: this.withSettings(environment, new BarbackSettings({}, mode));
/// Only ".dart" entrypoint files within a buildable directory are processed.
@@ -202,7 +201,7 @@
class _BarbackCompilerProvider implements dart.CompilerProvider {
Uri get libraryRoot => Uri.parse("${path.toUri(_libraryRootPath)}/");
- final BuildEnvironment _environment;
+ final AssetEnvironment _environment;
final Transform _transform;
String _libraryRootPath;
@@ -246,10 +245,10 @@
// do that by placing them in a special "$sdk" pseudo-package. In order for
// dart2js to generate the right URLs to point to that package, we give it
// a library root that corresponds to where that package can be found
- // relative to the public build directory containing that entrypoint.
+ // relative to the public source directory containing that entrypoint.
//
// For example, say the package being compiled is "/dev/myapp", the
- // entrypoint is "web/sub/foo/bar.dart", and the build directory is
+ // entrypoint is "web/sub/foo/bar.dart", and the source directory is
// "web/sub". This means the SDK sources will be (conceptually) at:
//
// /dev/myapp/web/sub/packages/$sdk/lib/
@@ -259,7 +258,7 @@
// $sdk|lib/lib/...
//
// TODO(rnystrom): Fix this if #17751 is fixed.
- var buildDir = _environment.getBuildDirectoryContaining(
+ var buildDir = _environment.getSourceDirectoryContaining(
_transform.primaryInput.id.path);
_libraryRootPath = path.join(_environment.rootPackage.dir,
buildDir, "packages", r"$sdk");
diff --git a/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart b/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart
new file mode 100644
index 0000000..2fc1a0f
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart
@@ -0,0 +1,102 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub.foreign_transformer;
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:barback/barback.dart';
+
+import '../../../asset/dart/serialize.dart';
+import '../barback.dart';
+import 'excluding_transformer.dart';
+
+/// A wrapper for a transformer that's in a different isolate.
+class _ForeignTransformer extends Transformer {
+ /// The port with which we communicate with the child isolate.
+ ///
+ /// This port and all messages sent across it are specific to this
+ /// transformer.
+ final SendPort _port;
+
+ /// The result of calling [toString] on the transformer in the isolate.
+ final String _toString;
+
+ _ForeignTransformer(Map map)
+ : _port = map['port'],
+ _toString = map['toString'];
+
+ Future<bool> isPrimary(AssetId id) {
+ return call(_port, {
+ 'type': 'isPrimary',
+ 'id': serializeId(id)
+ });
+ }
+
+ Future apply(Transform transform) {
+ return call(_port, {
+ 'type': 'apply',
+ 'transform': serializeTransform(transform)
+ });
+ }
+
+ String toString() => _toString;
+}
+
+class _ForeignDeclaringTransformer extends _ForeignTransformer
+ implements DeclaringTransformer {
+ _ForeignDeclaringTransformer(Map map)
+ : super(map);
+
+ Future declareOutputs(DeclaringTransform transform) {
+ return call(_port, {
+ 'type': 'declareOutputs',
+ 'transform': serializeDeclaringTransform(transform)
+ });
+ }
+}
+
+class _ForeignLazyTransformer extends _ForeignDeclaringTransformer
+ implements LazyTransformer {
+ _ForeignLazyTransformer(Map map)
+ : super(map);
+}
+
+/// A wrapper for a transformer group that's in a different isolate.
+class _ForeignGroup implements TransformerGroup {
+ final Iterable<Iterable> phases;
+
+ /// The result of calling [toString] on the transformer group in the isolate.
+ final String _toString;
+
+ _ForeignGroup(TransformerId id, Map map)
+ : phases = map['phases'].map((phase) {
+ return phase.map((transformer) => deserializeTransformerOrGroup(
+ transformer, id)).toList();
+ }).toList(),
+ _toString = map['toString'];
+
+ String toString() => _toString;
+}
+
+/// Converts a serializable map into a [Transformer] or a [TransformerGroup].
+deserializeTransformerOrGroup(Map map, TransformerId id) {
+ var transformer;
+ switch(map['type']) {
+ case 'TransformerGroup': return new _ForeignGroup(id, map);
+ case 'Transformer':
+ transformer = new _ForeignTransformer(map);
+ break;
+ case 'DeclaringTransformer':
+ transformer = new _ForeignDeclaringTransformer(map);
+ break;
+ case 'LazyTransformer':
+ transformer = new _ForeignLazyTransformer(map);
+ break;
+ default: assert(false);
+ }
+
+ return ExcludingTransformer.wrap(transformer, id.includes, id.excludes);
+}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
index 03e24d6..0f9016d 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
@@ -12,7 +12,7 @@
import '../log.dart' as log;
import '../package_graph.dart';
import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
import 'dart2js_transformer.dart';
import 'excluding_transformer.dart';
import 'load_transformers.dart';
@@ -27,7 +27,7 @@
///
/// Any built-in transformers that are provided by the environment will
/// automatically be added to the end of the root package's cascade.
-Future loadAllTransformers(BuildEnvironment environment,
+Future loadAllTransformers(AssetEnvironment environment,
BarbackServer transformerServer) {
// In order to determine in what order we should load transformers, we need to
// know which transformers depend on which others. This is different than
@@ -230,7 +230,7 @@
/// A class that loads transformers defined in specific files.
class _TransformerLoader {
- final BuildEnvironment _environment;
+ final AssetEnvironment _environment;
final BarbackServer _transformerServer;
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
index c5cb4b9..2d5d8cb 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
@@ -8,22 +8,18 @@
import 'dart:convert';
import 'dart:isolate';
-import 'package:barback/barback.dart';
-// TODO(nweiz): don't import from "src" once issue 14966 is fixed.
-import 'package:barback/src/internal_asset.dart';
-
import '../../../asset/dart/serialize.dart';
import '../barback.dart';
import '../dart.dart' as dart;
import '../log.dart' as log;
import '../utils.dart';
-import 'build_environment.dart';
-import 'excluding_transformer.dart';
+import 'asset_environment.dart';
+import 'foreign_transformer.dart';
import 'barback_server.dart';
/// Load and return all transformers and groups from the library identified by
/// [id].
-Future<Set> loadTransformers(BuildEnvironment environment,
+Future<Set> loadTransformers(AssetEnvironment environment,
BarbackServer transformerServer, TransformerId id) {
return id.getAssetId(environment.barback).then((assetId) {
var path = assetId.path.replaceFirst('lib/', '');
@@ -53,7 +49,7 @@
'configuration': JSON.encode(id.configuration)
}).then((transformers) {
transformers = transformers.map(
- (transformer) => _deserializeTransformerOrGroup(transformer, id))
+ (transformer) => deserializeTransformerOrGroup(transformer, id))
.toSet();
log.fine("Transformers from $assetId: $transformers");
return transformers;
@@ -75,63 +71,3 @@
});
});
}
-
-/// A wrapper for a transformer that's in a different isolate.
-class _ForeignTransformer extends Transformer {
- /// The port with which we communicate with the child isolate.
- ///
- /// This port and all messages sent across it are specific to this
- /// transformer.
- final SendPort _port;
-
- /// The result of calling [toString] on the transformer in the isolate.
- final String _toString;
-
- _ForeignTransformer(Map map)
- : _port = map['port'],
- _toString = map['toString'];
-
- Future<bool> isPrimary(AssetId id) {
- return call(_port, {
- 'type': 'isPrimary',
- 'id': serializeId(id)
- });
- }
-
- Future apply(Transform transform) {
- return call(_port, {
- 'type': 'apply',
- 'transform': serializeTransform(transform)
- });
- }
-
- String toString() => _toString;
-}
-
-/// A wrapper for a transformer group that's in a different isolate.
-class _ForeignGroup implements TransformerGroup {
- final Iterable<Iterable> phases;
-
- /// The result of calling [toString] on the transformer group in the isolate.
- final String _toString;
-
- _ForeignGroup(TransformerId id, Map map)
- : phases = map['phases'].map((phase) {
- return phase.map((transformer) => _deserializeTransformerOrGroup(
- transformer, id)).toList();
- }).toList(),
- _toString = map['toString'];
-
- String toString() => _toString;
-}
-
-/// Converts a serializable map into a [Transformer] or a [TransformerGroup].
-_deserializeTransformerOrGroup(Map map, TransformerId id) {
- if (map['type'] == 'Transformer') {
- var transformer = new _ForeignTransformer(map);
- return ExcludingTransformer.wrap(transformer, id.includes, id.excludes);
- }
-
- assert(map['type'] == 'TransformerGroup');
- return new _ForeignGroup(id, map);
-}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/build_directory.dart b/sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
similarity index 81%
rename from sdk/lib/_internal/pub/lib/src/barback/build_directory.dart
rename to sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
index c01da04..725c4fc 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/build_directory.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
@@ -2,21 +2,19 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-library pub.barback.build_environment;
+library pub.barback.source_directory;
import 'dart:async';
import 'package:watcher/watcher.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
import 'barback_server.dart';
-// TODO(rnystrom): Rename to "SourceDirectory" and clean up various doc
-// comments that refer to "build directories" to use "source directory".
/// A directory in the entrypoint package whose contents have been made
/// available to barback and that are bound to a server.
-class BuildDirectory {
- final BuildEnvironment _environment;
+class SourceDirectory {
+ final AssetEnvironment _environment;
/// The relative directory path within the package.
final String directory;
@@ -40,7 +38,7 @@
/// If the directory is not being watched, this will be `null`.
StreamSubscription<WatchEvent> watchSubscription;
- BuildDirectory(this._environment, this.directory, this.hostname, this.port);
+ SourceDirectory(this._environment, this.directory, this.hostname, this.port);
/// Binds a server running on [hostname]:[port] to this directory.
Future<BarbackServer> serve() {
@@ -51,7 +49,7 @@
});
}
- /// Removes the build directory from the build environment.
+ /// Removes the source directory from the build environment.
///
/// Closes the server, removes the assets from barback, and stops watching it.
Future close() {
diff --git a/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart b/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
index beb8c11..6953d7c 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
@@ -11,7 +11,7 @@
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
/// Implements the [WebSocket] API for communicating with a running pub serve
/// process, mainly for use by the Editor.
@@ -20,7 +20,7 @@
/// methods are described in the method-level documentation below.
class WebSocketApi {
final WebSocket _socket;
- final BuildEnvironment _environment;
+ final AssetEnvironment _environment;
final _server = new json_rpc.Server();
WebSocketApi(this._socket, this._environment) {
diff --git a/sdk/lib/_internal/pub/lib/src/command/build.dart b/sdk/lib/_internal/pub/lib/src/command/build.dart
index 61e46ef..560bbe2 100644
--- a/sdk/lib/_internal/pub/lib/src/command/build.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/build.dart
@@ -9,7 +9,7 @@
import 'package:barback/barback.dart';
import 'package:path/path.dart' as path;
-import '../barback/build_environment.dart';
+import '../barback/asset_environment.dart';
import '../exit_codes.dart' as exit_codes;
import '../io.dart';
import '../log.dart' as log;
@@ -53,7 +53,7 @@
// Since this server will only be hit by the transformer loader and isn't
// user-facing, just use an IPv4 address to avoid a weird bug on the
// OS X buildbots.
- return BuildEnvironment.create(entrypoint, "127.0.0.1", 0, mode,
+ return AssetEnvironment.create(entrypoint, "127.0.0.1", 0, mode,
WatcherType.NONE, useDart2JS: true)
.then((environment) {
// Show in-progress errors, but not results. Those get handled
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache.dart b/sdk/lib/_internal/pub/lib/src/command/cache.dart
index 93897fa..63d027b 100644
--- a/sdk/lib/_internal/pub/lib/src/command/cache.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/cache.dart
@@ -7,6 +7,7 @@
import '../command.dart';
import 'cache_add.dart';
import 'cache_list.dart';
+import 'cache_repair.dart';
/// Handles the `cache` pub command.
class CacheCommand extends PubCommand {
@@ -15,6 +16,7 @@
final subcommands = {
"add": new CacheAddCommand(),
- "list": new CacheListCommand()
+ "list": new CacheListCommand(),
+ "repair": new CacheRepairCommand()
};
}
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache_add.dart b/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
index fac394a..4b775f2 100644
--- a/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
@@ -55,7 +55,7 @@
}
// TODO(rnystrom): Support installing from git too.
- var source = this.cache.sources["hosted"];
+ var source = cache.sources["hosted"];
// TODO(rnystrom): Allow specifying the server.
return source.getVersions(package, package).then((versions) {
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart b/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart
new file mode 100644
index 0000000..450951b
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart
@@ -0,0 +1,50 @@
+// 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.
+
+library pub.command.cache_repair;
+
+import 'dart:async';
+
+import '../command.dart';
+import '../exit_codes.dart' as exit_codes;
+import '../io.dart';
+import '../log.dart' as log;
+import '../utils.dart';
+
+/// Handles the `cache repair` pub command.
+class CacheRepairCommand extends PubCommand {
+ String get description => "Reinstall cached packages.";
+ String get usage => "pub cache repair";
+ bool get requiresEntrypoint => false;
+
+ Future onRun() {
+ var successes = 0;
+ var failures = 0;
+
+ // Repair every cached source.
+ return Future.forEach(cache.sources.where(
+ (source) => source.shouldCache), (source) {
+ return source.repairCachedPackages().then((results) {
+ successes += results.first;
+ failures += results.last;
+ });
+ }).then((_) {
+ if (successes > 0) {
+ var packages = pluralize("package", successes);
+ log.message("Reinstalled ${log.green(successes)} $packages.");
+ }
+
+ if (failures > 0) {
+ var packages = pluralize("package", failures);
+ log.message("Failed to reinstall ${log.red(failures)} $packages.");
+ }
+
+ if (successes == 0 && failures == 0) {
+ log.message("No packages in cache, so nothing to repair.");
+ }
+
+ if (failures > 0) return flushThenExit(exit_codes.UNAVAILABLE);
+ });
+ }
+}
diff --git a/sdk/lib/_internal/pub/lib/src/command/lish.dart b/sdk/lib/_internal/pub/lib/src/command/lish.dart
index 91bfacf..b85d0f4 100644
--- a/sdk/lib/_internal/pub/lib/src/command/lish.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/lish.dart
@@ -56,6 +56,7 @@
if (url is! String) invalidServerResponse(response);
cloudStorageUrl = Uri.parse(url);
var request = new http.MultipartRequest('POST', cloudStorageUrl);
+ request.headers['Pub-Http-Timeout'] = 'None';
var fields = _expectField(parameters, 'fields', response);
if (fields is! Map) invalidServerResponse(response);
diff --git a/sdk/lib/_internal/pub/lib/src/command/serve.dart b/sdk/lib/_internal/pub/lib/src/command/serve.dart
index a2cc64e..55895d6 100644
--- a/sdk/lib/_internal/pub/lib/src/command/serve.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/serve.dart
@@ -9,7 +9,7 @@
import 'package:barback/barback.dart';
-import '../barback/build_environment.dart';
+import '../barback/asset_environment.dart';
import '../barback/pub_package_provider.dart';
import '../log.dart' as log;
import '../utils.dart';
@@ -86,7 +86,7 @@
var watcherType = commandOptions['force-poll'] ?
WatcherType.POLLING : WatcherType.AUTO;
- return BuildEnvironment.create(entrypoint, hostname, port, mode,
+ return AssetEnvironment.create(entrypoint, hostname, port, mode,
watcherType, useDart2JS: useDart2JS).then((environment) {
var directoryLength = sourceDirectories.map((dir) => dir.length)
@@ -132,7 +132,7 @@
});
}
- Future _startServer(BuildEnvironment environment, String rootDirectory,
+ Future _startServer(AssetEnvironment environment, String rootDirectory,
int directoryLength) {
return environment.serveDirectory(rootDirectory).then((server) {
// In release mode, strip out .dart files since all relevant ones have
diff --git a/sdk/lib/_internal/pub/lib/src/entrypoint.dart b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
index c098eec..2c360e14 100644
--- a/sdk/lib/_internal/pub/lib/src/entrypoint.dart
+++ b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
@@ -248,57 +248,21 @@
});
}
- /// Warns users if they have directory or file named `assets` directly
- /// inside a top-level directory.
- void _warnOnAssetsPaths() {
- var buffer = new StringBuffer();
-
- warn(message) {
- if (buffer.isEmpty) {
- buffer.writeln(
- 'Warning: Pub reserves paths containing "assets" for using assets '
- 'from packages.');
- }
-
- buffer.writeln(message);
- }
-
- // Look inside all of the top-level directories.
- for (var dir in ordered(listDir(root.dir))) {
- var assetsPath = path.join(dir, "assets");
- var relative = path.relative(assetsPath, from: root.dir);
- if (dirExists(assetsPath)) {
- warn('Please rename the directory "$relative".');
- } else if (entryExists(assetsPath)) {
- warn('Please rename the file "$relative".');
- }
- }
-
- if (buffer.isNotEmpty) log.warning(buffer);
- }
-
/// Loads the package graph for the application and all of its transitive
/// dependencies. Before loading makes sure the lockfile and dependencies are
/// installed and up to date.
- Future<PackageGraph> loadPackageGraph() =>
- _ensureLockFileIsUpToDate()
- .then((_) {
- _warnOnAssetsPaths();
- return _loadPackageGraph();
+ Future<PackageGraph> loadPackageGraph() {
+ return _ensureLockFileIsUpToDate().then((_) {
+ var lockFile = loadLockFile();
+ return Future.wait(lockFile.packages.values.map((id) {
+ var source = cache.sources[id.source];
+ return source.getDirectory(id)
+ .then((dir) => new Package.load(id.name, dir, cache.sources));
+ })).then((packages) {
+ var packageMap = new Map.fromIterable(packages, key: (p) => p.name);
+ packageMap[root.name] = root;
+ return new PackageGraph(this, lockFile, packageMap);
});
-
- /// Loads the package graph for the application and all of its transitive
- /// dependencies.
- Future<PackageGraph> _loadPackageGraph() {
- var lockFile = loadLockFile();
- return Future.wait(lockFile.packages.values.map((id) {
- var source = cache.sources[id.source];
- return source.getDirectory(id)
- .then((dir) => new Package.load(id.name, dir, cache.sources));
- })).then((packages) {
- var packageMap = new Map.fromIterable(packages, key: (p) => p.name);
- packageMap[root.name] = root;
- return new PackageGraph(this, lockFile, packageMap);
});
}
diff --git a/sdk/lib/_internal/pub/lib/src/http.dart b/sdk/lib/_internal/pub/lib/src/http.dart
index afaefd8..08c30fd 100644
--- a/sdk/lib/_internal/pub/lib/src/http.dart
+++ b/sdk/lib/_internal/pub/lib/src/http.dart
@@ -34,6 +34,10 @@
/// An HTTP client that transforms 40* errors and socket exceptions into more
/// user-friendly error messages.
+///
+/// This also adds a 30-second timeout to every request. This can be configured
+/// on a per-request basis by setting the 'Pub-Request-Timeout' header to the
+/// desired number of milliseconds, or to "None" to disable the timeout.
class PubHttpClient extends http.BaseClient {
final _requestStopwatches = new Map<http.BaseRequest, Stopwatch>();
@@ -55,7 +59,15 @@
stackTrace = localStackTrace;
}
- return timeout(inner.send(request).then((streamedResponse) {
+ var timeoutLength = HTTP_TIMEOUT;
+ var timeoutString = request.headers.remove('Pub-Request-Timeout');
+ if (timeoutString == 'None') {
+ timeoutLength = null;
+ } else if (timeoutString != null) {
+ timeoutLength = int.parse(timeoutString);
+ }
+
+ var future = inner.send(request).then((streamedResponse) {
_logResponse(streamedResponse);
var status = streamedResponse.statusCode;
@@ -95,7 +107,10 @@
}
}
throw error;
- }), HTTP_TIMEOUT, 'fetching URL "${request.url}"');
+ });
+
+ if (timeoutLength == null) return future;
+ return timeout(future, timeoutLength, 'fetching URL "${request.url}"');
}
/// Logs the fact that [request] was sent, and information about it.
diff --git a/sdk/lib/_internal/pub/lib/src/package.dart b/sdk/lib/_internal/pub/lib/src/package.dart
index 7d25a2e..8f6f617 100644
--- a/sdk/lib/_internal/pub/lib/src/package.dart
+++ b/sdk/lib/_internal/pub/lib/src/package.dart
@@ -15,6 +15,18 @@
/// A named, versioned, unit of code and resource reuse.
class Package {
+ /// Compares [a] and [b] orders them by name then version number.
+ ///
+ /// This is normally used as a [Comparator] to pass to sort. This does not
+ /// take a package's description or root directory into account, so multiple
+ /// distinct packages may order the same.
+ static int orderByNameAndVersion(Package a, Package b) {
+ var name = a.name.compareTo(b.name);
+ if (name != 0) return name;
+
+ return a.version.compareTo(b.version);
+ }
+
/// The path to the directory containing the package.
final String dir;
diff --git a/sdk/lib/_internal/pub/lib/src/pubspec.dart b/sdk/lib/_internal/pub/lib/src/pubspec.dart
index e6344a4..759f01e 100644
--- a/sdk/lib/_internal/pub/lib/src/pubspec.dart
+++ b/sdk/lib/_internal/pub/lib/src/pubspec.dart
@@ -149,7 +149,7 @@
} else {
if (transformer.length != 1) {
_error('"$field" must have a single key: the transformer '
- 'identifier.');
+ 'identifier. Was "$transformer".');
} else if (transformer.keys.single is! String) {
_error('"$field" transformer identifier must be a string, but was '
'"$library".');
diff --git a/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart b/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
index 186ac8e..d4994a2 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
@@ -144,14 +144,7 @@
// Gather some solving metrics.
var buffer = new StringBuffer();
buffer.writeln('${runtimeType} took ${stopwatch.elapsed} seconds.');
- buffer.writeln(
- '- Requested ${cache.versionCacheMisses} version lists');
- buffer.writeln(
- '- Looked up ${cache.versionCacheHits} cached version lists');
- buffer.writeln(
- '- Requested ${cache.pubspecCacheMisses} pubspecs');
- buffer.writeln(
- '- Looked up ${cache.pubspecCacheHits} cached pubspecs');
+ buffer.writeln(cache.describeResults());
log.solver(buffer);
});
}
@@ -491,11 +484,11 @@
for (var dep in deps) {
if (!dep.isRoot && !_solver.sources.contains(dep.source)) {
throw new UnknownSourceException(id.name,
- [new Dependency(id.name, dep)]);
+ [new Dependency(id.name, id.version, dep)]);
}
}
- return _traverseDeps(id.name, new DependencyQueue(_solver, deps));
+ return _traverseDeps(id, new DependencyQueue(_solver, deps));
});
}
@@ -504,7 +497,8 @@
/// Desctructively modifies [deps]. Completes to a list of packages if the
/// traversal is complete. Completes it to an error if a failure occurred.
/// Otherwise, recurses.
- Future<List<PackageId>> _traverseDeps(String depender, DependencyQueue deps) {
+ Future<List<PackageId>> _traverseDeps(PackageId depender,
+ DependencyQueue deps) {
// Move onto the next package if we've traversed all of these references.
if (deps.isEmpty) return _traversePackage();
@@ -514,7 +508,7 @@
// Add the dependency.
var dependencies = _getDependencies(dep.name);
- dependencies.add(new Dependency(depender, dep));
+ dependencies.add(new Dependency(depender.name, depender.version, dep));
// If the package is barback, pub has an implicit version constraint on
// it since pub itself uses barback too. Note that we don't check for
@@ -534,7 +528,7 @@
// find barback.
var barbackDep = new PackageDep(dep.name, dep.source,
barback.supportedVersions, dep.description);
- dependencies.add(new Dependency("pub itself", barbackDep));
+ dependencies.add(new Dependency("pub itself", null, barbackDep));
}
var constraint = _getConstraint(dep.name);
@@ -578,7 +572,7 @@
if (allowed.isEmpty) {
_solver.logSolve('no versions for ${dep.name} match $constraint');
- throw new NoVersionException(dep.name, constraint,
+ throw new NoVersionException(dep.name, null, constraint,
_getDependencies(dep.name));
}
@@ -608,7 +602,7 @@
/// other dependencies on the same package. Throws a [SolveFailure]
/// exception if not. Only validates sources and descriptions, not the
/// version.
- void _validateDependency(PackageDep dep, String depender) {
+ void _validateDependency(PackageDep dep, PackageId depender) {
// Make sure the dependencies agree on source and description.
var required = _getRequired(dep.name);
if (required == null) return;
@@ -618,7 +612,7 @@
_solver.logSolve('source mismatch on ${dep.name}: ${required.dep.source} '
'!= ${dep.source}');
throw new SourceMismatchException(dep.name,
- [required, new Dependency(depender, dep)]);
+ [required, new Dependency(depender.name, depender.version, dep)]);
}
// Make sure all of the existing descriptions match the new reference.
@@ -627,7 +621,7 @@
_solver.logSolve('description mismatch on ${dep.name}: '
'${required.dep.description} != ${dep.description}');
throw new DescriptionMismatchException(dep.name,
- [required, new Dependency(depender, dep)]);
+ [required, new Dependency(depender.name, depender.version, dep)]);
}
}
@@ -643,7 +637,7 @@
// Make sure it meets the constraint.
if (!dep.constraint.allows(selected.version)) {
_solver.logSolve('selection $selected does not match $constraint');
- throw new NoVersionException(dep.name, constraint,
+ throw new NoVersionException(dep.name, selected.version, constraint,
_getDependencies(dep.name));
}
diff --git a/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart b/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
index c462120..b215247 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
@@ -130,6 +130,18 @@
}
return _solver.cache.getVersions(dep.toRef()).then((versions) {
+ // If the root package depends on this one, ignore versions that don't
+ // match that constraint. Since the root package's dependency constraints
+ // won't change during solving, we can safely filter out packages that
+ // don't meet it.
+ for (var rootDep in _solver.root.immediateDependencies) {
+ if (rootDep.name == dep.name) {
+ versions = versions.where(
+ (id) => rootDep.constraint.allows(id.version));
+ break;
+ }
+ }
+
return versions.length;
}).catchError((error, trace) {
// If it fails for any reason, just treat that as no versions. This
diff --git a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
index 6ac4aad..3fc55d0 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
@@ -116,19 +116,19 @@
/// The number of times a version list was requested and it wasn't cached and
/// had to be requested from the source.
- int versionCacheMisses = 0;
+ int _versionCacheMisses = 0;
/// The number of times a version list was requested and the cached version
/// was returned.
- int versionCacheHits = 0;
+ int _versionCacheHits = 0;
/// The number of times a pubspec was requested and it wasn't cached and had
/// to be requested from the source.
- int pubspecCacheMisses = 0;
+ int _pubspecCacheMisses = 0;
/// The number of times a pubspec was requested and the cached version was
/// returned.
- int pubspecCacheHits = 0;
+ int _pubspecCacheHits = 0;
PubspecCache(this._sources);
@@ -141,11 +141,11 @@
Future<Pubspec> getPubspec(PackageId id) {
// Complete immediately if it's already cached.
if (_pubspecs.containsKey(id)) {
- pubspecCacheHits++;
+ _pubspecCacheHits++;
return new Future<Pubspec>.value(_pubspecs[id]);
}
- pubspecCacheMisses++;
+ _pubspecCacheMisses++;
var source = _sources[id.source];
return source.describe(id).then((pubspec) {
@@ -172,11 +172,10 @@
// See if we have it cached.
var versions = _versions[package];
if (versions != null) {
- versionCacheHits++;
+ _versionCacheHits++;
return new Future.value(versions);
}
-
- versionCacheMisses++;
+ _versionCacheMisses++;
var source = _sources[package.source];
return source.getVersions(package.name, package.description)
@@ -194,6 +193,49 @@
/// Returns the previously cached list of versions for the package identified
/// by [package] or returns `null` if not in the cache.
List<PackageId> getCachedVersions(PackageRef package) => _versions[package];
+
+ /// Returns a user-friendly output string describing metrics of the solve.
+ String describeResults() {
+ var results = '''- Requested $_versionCacheMisses version lists
+- Looked up $_versionCacheHits cached version lists
+- Requested $_pubspecCacheMisses pubspecs
+- Looked up $_pubspecCacheHits cached pubspecs
+''';
+
+ // Uncomment this to dump the visited package graph to JSON.
+ //results += _debugWritePackageGraph();
+
+ return results;
+ }
+
+ /// This dumps the set of packages that were looked at by the solver to a
+ /// JSON map whose format matches the map passed to [testResolve] in the
+ /// version solver unit tests.
+ ///
+ /// If a real-world version solve is failing, this can be used to mirror that
+ /// data to build a regression test using mock packages.
+ String _debugDescribePackageGraph() {
+ var packages = {};
+ _pubspecs.forEach((id, pubspec) {
+ var deps = {};
+ packages["${id.name} ${id.version}"] = deps;
+
+ for (var dep in pubspec.dependencies) {
+ deps[dep.name] = dep.constraint.toString();
+ }
+ });
+
+ // Add in the packages that we know of but didn't need their pubspecs.
+ _versions.forEach((ref, versions) {
+ for (var id in versions) {
+ packages.putIfAbsent("${id.name} ${id.version}", () => {});
+ }
+ });
+
+ // TODO(rnystrom): Include dev dependencies and dependency overrides.
+
+ return JSON.encode(packages);
+ }
}
/// A reference from a depending package to a package that it depends on.
@@ -201,12 +243,17 @@
/// The name of the package that has this dependency.
final String depender;
+ /// The version of the depender that has this dependency.
+ ///
+ /// This will be `null` when [depender] is the magic "pub itself" dependency.
+ final Version dependerVersion;
+
/// The package being depended on.
final PackageDep dep;
- Dependency(this.depender, this.dep);
+ Dependency(this.depender, this.dependerVersion, this.dep);
- String toString() => '$depender -> $dep';
+ String toString() => '$depender $dependerVersion -> $dep';
}
/// Base class for all failures that can occur while trying to resolve versions.
@@ -238,16 +285,16 @@
var buffer = new StringBuffer();
buffer.write("$_message:");
- var map = {};
- for (var dep in dependencies) {
- map[dep.depender] = dep.dep;
- }
+ var sorted = dependencies.toList();
+ sorted.sort((a, b) => a.depender.compareTo(b.depender));
- var names = ordered(map.keys);
-
- for (var name in names) {
+ for (var dep in sorted) {
buffer.writeln();
- buffer.write("- $name ${_describeDependency(map[name])}");
+ buffer.write("- ${log.bold(dep.depender)}");
+ if (dep.dependerVersion != null) {
+ buffer.write(" ${dep.dependerVersion}");
+ }
+ buffer.write(" ${_describeDependency(dep.dep)}");
}
return buffer.toString();
@@ -275,12 +322,27 @@
class NoVersionException extends SolveFailure {
final VersionConstraint constraint;
- NoVersionException(String package, this.constraint,
+ /// The last selected version of the package that failed to meet the new
+ /// constraint.
+ ///
+ /// This will be `null` when the failure occurred because there are no
+ /// versions of the package *at all* that match the constraint. It will be
+ /// non-`null` when a version was selected, but then the solver tightened a
+ /// constraint such that that version was no longer allowed.
+ final Version version;
+
+ NoVersionException(String package, this.version, this.constraint,
Iterable<Dependency> dependencies)
: super(package, dependencies);
- String get _message => "Package $package has no versions that match "
- "$constraint derived from";
+ String get _message {
+ if (version == null) {
+ return "Package $package has no versions that match $constraint derived "
+ "from";
+ }
+
+ return "Package $package $version does not match $constraint derived from";
+ }
}
// TODO(rnystrom): Report the list of depending packages and their constraints.
diff --git a/sdk/lib/_internal/pub/lib/src/source.dart b/sdk/lib/_internal/pub/lib/src/source.dart
index 2927cc4..06181c5 100644
--- a/sdk/lib/_internal/pub/lib/src/source.dart
+++ b/sdk/lib/_internal/pub/lib/src/source.dart
@@ -160,16 +160,25 @@
///
/// This is only called for sources with [shouldCache] set to true. By
/// default, this uses [systemCacheDirectory] and [get].
- Future<Package> downloadToSystemCache(PackageId id) {
+ ///
+ /// If [force] is `true`, then the package is downloaded even if it already
+ /// exists in the cache. The previous one will be deleted.
+ Future<Package> downloadToSystemCache(PackageId id, {bool force}) {
+ if (force == null) force = false;
+
var packageDir;
return systemCacheDirectory(id).then((p) {
packageDir = p;
// See if it's already cached.
if (dirExists(packageDir)) {
- if (!_isCachedPackageCorrupted(packageDir)) return true;
- // Busted, so wipe out the package and re-download.
- deleteEntry(packageDir);
+ if (force || _isCachedPackageCorrupted(packageDir)) {
+ // Wipe it out and re-install it.
+ deleteEntry(packageDir);
+ } else {
+ // Already downloaded.
+ return true;
+ }
}
ensureDir(path.dirname(packageDir));
@@ -220,6 +229,19 @@
new Chain.current());
}
+ /// Reinstalls all packages that have been previously installed into the
+ /// system cache by this source.
+ ///
+ /// Returns a [Pair] whose first element is the number of packages
+ /// successfully repaired and the second is the number of failures.
+ Future<Pair<int, int>> repairCachedPackages() {
+ if (shouldCache) {
+ throw new UnimplementedError("Source $name must implement this.");
+ }
+ throw new UnsupportedError("Cannot call repairCachedPackages() on an "
+ "uncached source.");
+ }
+
/// When a [Pubspec] or [LockFile] is parsed, it reads in the description for
/// each dependency. It is up to the dependency's [Source] to determine how
/// that should be interpreted. This will be called during parsing to validate
diff --git a/sdk/lib/_internal/pub/lib/src/source/git.dart b/sdk/lib/_internal/pub/lib/src/source/git.dart
index 3b47cb4..563ea57 100644
--- a/sdk/lib/_internal/pub/lib/src/source/git.dart
+++ b/sdk/lib/_internal/pub/lib/src/source/git.dart
@@ -10,6 +10,7 @@
import '../git.dart' as git;
import '../io.dart';
+import '../log.dart' as log;
import '../package.dart';
import '../source.dart';
import '../utils.dart';
@@ -38,7 +39,11 @@
/// `<package name>-<url hash>`. These are used to check out the repository
/// itself; each of the commit-specific directories are clones of a directory
/// in `cache/`.
- Future<Package> downloadToSystemCache(PackageId id) {
+ Future<Package> downloadToSystemCache(PackageId id, {bool force}) {
+ // Force is not supported because the cache repair command doesn't need it.
+ // Instead, it uses [resetCachedPackages].
+ assert(force != true);
+
var revisionCachePath;
return git.isInstalled.then((installed) {
@@ -118,6 +123,45 @@
});
}
+ /// Resets all cached packages back to the pristine state of the Git
+ /// repository at the revision they are pinned to.
+ Future<Pair<int, int>> repairCachedPackages() {
+ if (!dirExists(systemCacheRoot)) return new Future.value(new Pair(0, 0));
+
+ var successes = 0;
+ var failures = 0;
+
+ var packages = listDir(systemCacheRoot)
+ .where((entry) => dirExists(path.join(entry, ".git")))
+ .map((packageDir) => new Package.load(null, packageDir,
+ systemCache.sources))
+ .toList();
+
+ // Note that there may be multiple packages with the same name and version
+ // (pinned to different commits). The sort order of those is unspecified.
+ packages.sort(Package.orderByNameAndVersion);
+
+ return Future.wait(packages.map((package) {
+ log.message("Resetting Git repository for "
+ "${log.bold(package.name)} ${package.version}...");
+
+ // Remove all untracked files.
+ return git.run(["clean", "-d", "--force", "-x"],
+ workingDir: package.dir).then((_) {
+ // Discard all changes to tracked files.
+ return git.run(["reset", "--hard", "HEAD"], workingDir: package.dir);
+ }).then((_) {
+ successes++;
+ }).catchError((error, stackTrace) {
+ failures++;
+ log.error("Failed to reset ${log.bold(package.name)} "
+ "${package.version}. Error:\n$error");
+ log.fine(stackTrace);
+ failures++;
+ }, test: (error) => error is git.GitException);
+ })).then((_) => new Pair(successes, failures));
+ }
+
// TODO(keertip): Implement getCachedPackages().
/// Ensure that the canonical clone of the repository referred to by [id] (the
diff --git a/sdk/lib/_internal/pub/lib/src/source/hosted.dart b/sdk/lib/_internal/pub/lib/src/source/hosted.dart
index 781ab0c..dd5cb0f 100644
--- a/sdk/lib/_internal/pub/lib/src/source/hosted.dart
+++ b/sdk/lib/_internal/pub/lib/src/source/hosted.dart
@@ -78,28 +78,8 @@
/// Downloads a package from the site and unpacks it.
Future<bool> get(PackageId id, String destPath) {
- return syncFuture(() {
- var url = _makeVersionUrl(id, (server, package, version) =>
- "$server/packages/$package/versions/$version.tar.gz");
- log.io("Get package from $url.");
-
- log.message('Downloading ${id.name} ${id.version}...');
-
- // Download and extract the archive to a temp directory.
- var tempDir = systemCache.createTempDir();
- return httpClient.send(new http.Request("GET", url))
- .then((response) => response.stream)
- .then((stream) {
- return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT,
- 'fetching URL "$url"');
- }).then((_) {
- // Now that the get has succeeded, move it to the real location in the
- // cache. This ensures that we don't leave half-busted ghost
- // directories in the user's pub cache if a get fails.
- renameDir(tempDir, destPath);
- return true;
- });
- });
+ var parsed = _parseDescription(id.description);
+ return _download(parsed.last, parsed.first, id.version, destPath);
}
/// The system cache directory for the hosted source contains subdirectories
@@ -108,7 +88,7 @@
/// from that site.
Future<String> systemCacheDirectory(PackageId id) {
var parsed = _parseDescription(id.description);
- var dir = _getSourceDirectory(parsed.last);
+ var dir = _urlToDirectory(parsed.last);
return new Future.value(
path.join(systemCacheRoot, dir, "${parsed.first}-${id.version}"));
@@ -130,25 +110,80 @@
return description;
}
- List<Package> getCachedPackages([String url]) {
- if (url == null) url = defaultUrl;
+ /// Re-downloads all packages that have been previously downloaded into the
+ /// system cache from any server.
+ Future<Pair<int, int>> repairCachedPackages() {
+ if (!dirExists(systemCacheRoot)) return new Future.value(new Pair(0, 0));
- var cacheDir = path.join(systemCacheRoot,
- _getSourceDirectory(url));
- if (!dirExists(cacheDir)) return [];
+ var successes = 0;
+ var failures = 0;
- return listDir(path.join(cacheDir)).map((entry) {
- // TODO(keertip): instead of catching exception in pubspec parse with
- // sdk dependency, fix to parse and report usage of sdk dependency.
- // dartbug.com/10190
- try {
- return new Package.load(null, entry, systemCache.sources);
- } on ArgumentError catch (e) {
- log.error(e);
- }
- }).where((package) => package != null).toList();
+ return Future.wait(listDir(systemCacheRoot).map((serverDir) {
+ var url = _directoryToUrl(path.basename(serverDir));
+ var packages = _getCachedPackagesInDirectory(path.basename(serverDir));
+ packages.sort(Package.orderByNameAndVersion);
+ return Future.wait(packages.map((package) {
+ return _download(url, package.name, package.version, package.dir)
+ .then((_) {
+ successes++;
+ }).catchError((error, stackTrace) {
+ failures++;
+ var message = "Failed to repair ${log.bold(package.name)} "
+ "${package.version}";
+ if (url != defaultUrl) message += " from $url";
+ log.error("$message. Error:\n$error");
+ log.fine(stackTrace);
+ });
+ }));
+ })).then((_) => new Pair(successes, failures));
}
+ /// Gets all of the packages that have been downloaded into the system cache
+ /// from the default server.
+ List<Package> getCachedPackages() {
+ return _getCachedPackagesInDirectory(_urlToDirectory(defaultUrl));
+ }
+
+ /// Gets all of the packages that have been downloaded into the system cache
+ /// into [dir].
+ List<Package> _getCachedPackagesInDirectory(String dir) {
+ var cacheDir = path.join(systemCacheRoot, dir);
+ if (!dirExists(cacheDir)) return [];
+
+ return listDir(cacheDir)
+ .map((entry) => new Package.load(null, entry, systemCache.sources))
+ .toList();
+ }
+
+ /// Downloads package [package] at [version] from [server], and unpacks it
+ /// into [destPath].
+ Future<bool> _download(String server, String package, Version version,
+ String destPath) {
+ return syncFuture(() {
+ var url = Uri.parse("$server/packages/$package/versions/$version.tar.gz");
+ log.io("Get package from $url.");
+ log.message('Downloading ${log.bold(package)} ${version}...');
+
+ // Download and extract the archive to a temp directory.
+ var tempDir = systemCache.createTempDir();
+ return httpClient.send(new http.Request("GET", url))
+ .then((response) => response.stream)
+ .then((stream) {
+ return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT,
+ 'fetching URL "$url"');
+ }).then((_) {
+ // Remove the existing directory if it exists. This will happen if
+ // we're forcing a download to repair the cache.
+ if (dirExists(destPath)) deleteEntry(destPath);
+
+ // Now that the get has succeeded, move it to the real location in the
+ // cache. This ensures that we don't leave half-busted ghost
+ // directories in the user's pub cache if a get fails.
+ renameDir(tempDir, destPath);
+ return true;
+ });
+ });
+ }
/// When an error occurs trying to read something about [package] from [url],
/// this tries to translate into a more user friendly error message. Always
/// throws an error, either the original one or a better one.
@@ -185,8 +220,8 @@
var parsed = _parseDescription(description);
var server = parsed.last;
log.io("Finding versions of $name in "
- "${systemCache.rootDir}/${_getSourceDirectory(server)}");
- return getCachedPackages(server)
+ "$systemCacheRoot/${_urlToDirectory(server)}");
+ return _getCachedPackagesInDirectory(_urlToDirectory(server))
.where((package) => package.name == name)
.map((package) => package.version)
.toList();
@@ -212,12 +247,58 @@
}
}
-String _getSourceDirectory(String url) {
+/// Given a URL, returns a "normalized" string to be used as a directory name
+/// for packages downloaded from the server at that URL.
+///
+/// This normalization strips off the scheme (which is presumed to be HTTP or
+/// HTTPS) and *sort of* URL-encodes it. I say "sort of" because it does it
+/// incorrectly: it uses the character's *decimal* ASCII value instead of hex.
+///
+/// This could cause an ambiguity since some characters get encoded as three
+/// digits and others two. It's possible for one to be a prefix of the other.
+/// In practice, the set of characters that are encoded don't happen to have
+/// any collisions, so the encoding is reversible.
+///
+/// This behavior is a bug, but is being preserved for compatibility.
+String _urlToDirectory(String url) {
url = url.replaceAll(new RegExp(r"^https?://"), "");
return replace(url, new RegExp(r'[<>:"\\/|?*%]'),
(match) => '%${match[0].codeUnitAt(0)}');
}
+/// Given a directory name in the system cache, returns the URL of the server
+/// whose packages it contains.
+///
+/// See [_urlToDirectory] for details on the mapping. Note that because the
+/// directory name does not preserve the scheme, this has to guess at it. It
+/// chooses "http" for loopback URLs (mainly to support the pub tests) and
+/// "https" for all others.
+String _directoryToUrl(String url) {
+ // Decode the pseudo-URL-encoded characters.
+ var chars = '<>:"\\/|?*%';
+ for (var i = 0; i < chars.length; i++) {
+ var c = chars.substring(i, i + 1);
+ url = url.replaceAll("%${c.codeUnitAt(0)}", c);
+ }
+
+ // Figure out the scheme.
+ var scheme = "https";
+
+ // See if it's a loopback IP address.
+ try {
+ var urlWithoutPort = url.replaceAll(new RegExp(":.*"), "");
+ var address = new io.InternetAddress(urlWithoutPort);
+ if (address.isLoopback) scheme = "http";
+ } on ArgumentError catch(error) {
+ // If we got here, it's not a raw IP address, so it's probably a regular
+ // URL.
+ }
+
+ if (url == "localhost") scheme = "http";
+
+ return "$scheme://$url";
+}
+
/// Parses [description] into its server and package name components, then
/// converts that to a Uri given [pattern]. Ensures the package name is
/// properly URL encoded.
diff --git a/sdk/lib/_internal/pub/lib/src/source_registry.dart b/sdk/lib/_internal/pub/lib/src/source_registry.dart
index cd3dc3e..9d05e15 100644
--- a/sdk/lib/_internal/pub/lib/src/source_registry.dart
+++ b/sdk/lib/_internal/pub/lib/src/source_registry.dart
@@ -4,10 +4,12 @@
library pub.source_registry;
+import 'dart:collection';
+
import 'source.dart';
/// A class that keeps track of [Source]s used for getting packages.
-class SourceRegistry {
+class SourceRegistry extends IterableBase<Source> {
final Map<String, Source> _map;
Source _default;
@@ -17,6 +19,13 @@
/// Returns the default source, which is used when no source is specified.
Source get defaultSource => _default;
+ /// Iterates over the registered sources in name order.
+ Iterator<Source> get iterator {
+ var sources = _map.values.toList();
+ sources.sort((a, b) => a.name.compareTo(b.name));
+ return sources.iterator;
+ }
+
/// Sets the default source. This takes a string, which must be the name of a
/// registered source.
void setDefault(String name) {
diff --git a/sdk/lib/_internal/pub/lib/src/system_cache.dart b/sdk/lib/_internal/pub/lib/src/system_cache.dart
index 4e011dd..9cfca43 100644
--- a/sdk/lib/_internal/pub/lib/src/system_cache.dart
+++ b/sdk/lib/_internal/pub/lib/src/system_cache.dart
@@ -82,7 +82,10 @@
///
/// It is an error to try downloading a package from a source with
/// `shouldCache == false`.
- Future<Package> download(PackageId id) {
+ ///
+ /// If [force] is `true`, then the package is downloaded even if it already
+ /// exists in the cache. The previous one will be deleted.
+ Future<Package> download(PackageId id, {bool force}) {
var source = sources[id.source];
if (!source.shouldCache) {
@@ -92,7 +95,8 @@
var pending = _pendingDownloads[id];
if (pending != null) return pending;
- var future = source.downloadToSystemCache(id).whenComplete(() {
+ var future = source.downloadToSystemCache(id, force: force)
+ .whenComplete(() {
_pendingDownloads.remove(id);
});
diff --git a/sdk/lib/_internal/pub/lib/src/utils.dart b/sdk/lib/_internal/pub/lib/src/utils.dart
index 2dec803..d9ab8a9 100644
--- a/sdk/lib/_internal/pub/lib/src/utils.dart
+++ b/sdk/lib/_internal/pub/lib/src/utils.dart
@@ -117,9 +117,9 @@
} else {
runZoned(wrappedCallback, onError: (e, stackTrace) {
if (stackTrace == null) {
- stackTrace = new Chain([new Trace.from(stackTrace)]);
+ stackTrace = new Chain.current();
} else {
- stackTrace = new Chain([]);
+ stackTrace = new Chain([new Trace.from(stackTrace)]);
}
completer.completeError(e, stackTrace);
});
diff --git a/sdk/lib/_internal/pub/pub.status b/sdk/lib/_internal/pub/pub.status
index 35bb8c3..9cb5d30 100644
--- a/sdk/lib/_internal/pub/pub.status
+++ b/sdk/lib/_internal/pub/pub.status
@@ -3,6 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
test/hosted/version_negotiation_test: Pass, Timeout # Issue 14346
+test/serve/web_socket/url_to_asset_id_test: Pass, Slow
[ $runtime == vm && $system == windows ]
test/serve/warns_on_assets_paths_test: Pass, Fail # Issue 15741
diff --git a/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart b/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart
deleted file mode 100644
index bd3bb4e..0000000
--- a/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library pub_tests;
-
-import 'package:path/path.dart' as path;
-
-import '../descriptor.dart' as d;
-import '../test_pub.dart';
-
-main() {
- initConfig();
-
- integration('warns user about top-level "assets" directories', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [
- d.dir('assets')
- ]),
- d.dir('test', [
- d.dir('assets')
- ]),
- d.dir('web', [
- d.file('index.html'),
- d.dir('assets')
- ])
- ]).create();
-
- schedulePub(args: ['build', 'bin', 'test', 'web'],
- error: """
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the directory "${path.join('bin', 'assets')}".
-Please rename the directory "${path.join('test', 'assets')}".
-Please rename the directory "${path.join('web', 'assets')}".
-""");
- });
-
- integration('warns user about top-level "assets" files', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [
- d.file('assets', '...')
- ]),
- d.dir('test', [
- d.file('assets', '...')
- ]),
- d.dir('web', [
- d.file('index.html'),
- d.file('assets', '...')
- ])
- ]).create();
-
- schedulePub(args: ['build', 'bin', 'test', 'web'],
- error: """
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the file "${path.join('bin', 'assets')}".
-Please rename the file "${path.join('test', 'assets')}".
-Please rename the file "${path.join('web', 'assets')}".
-""");
- });
-
- integration('does not warn on "assets" in subdirectories', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('web', [
- d.file('index.html'),
- d.dir('foo', [
- d.dir('assets')
- ]),
- d.dir('bar', [
- d.file('assets', '...')
- ])
- ])
- ]).create();
-
- schedulePub(args: ['build'], error: new RegExp(r"^$"));
- });
-}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart b/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart
new file mode 100644
index 0000000..9f398b7
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+library pub_tests;
+
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('does nothing if the cache is empty', () {
+ // Repair them.
+ schedulePub(args: ["cache", "repair"],
+ output: "No packages in cache, so nothing to repair.");
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart b/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart
new file mode 100644
index 0000000..8519a74
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart
@@ -0,0 +1,58 @@
+// 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.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:scheduled_test/scheduled_stream.dart';
+
+import '../../../lib/src/exit_codes.dart' as exit_codes;
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('handles failure to reinstall some packages', () {
+ // Only serve two packages so repairing will have a failure.
+ servePackages([
+ packageMap("foo", "1.2.3"),
+ packageMap("foo", "1.2.5")
+ ]);
+
+ // Set up a cache with some packages.
+ d.dir(cachePath, [
+ d.dir('hosted', [
+ d.async(port.then((p) => d.dir('127.0.0.1%58$p', [
+ d.dir("foo-1.2.3", [
+ d.libPubspec("foo", "1.2.3"),
+ d.file("broken.txt")
+ ]),
+ d.dir("foo-1.2.4", [
+ d.libPubspec("foo", "1.2.4"),
+ d.file("broken.txt")
+ ]),
+ d.dir("foo-1.2.5", [
+ d.libPubspec("foo", "1.2.5"),
+ d.file("broken.txt")
+ ])
+ ])))
+ ])
+ ]).create();
+
+ // Repair them.
+ var pub = startPub(args: ["cache", "repair"]);
+
+ pub.stdout.expect("Downloading foo 1.2.3...");
+ pub.stdout.expect("Downloading foo 1.2.4...");
+ pub.stdout.expect("Downloading foo 1.2.5...");
+
+ pub.stderr.expect(startsWith("Failed to repair foo 1.2.4. Error:"));
+ pub.stderr.expect("HTTP error 404: Not Found");
+
+ pub.stdout.expect("Reinstalled 2 packages.");
+ pub.stdout.expect("Failed to reinstall 1 package.");
+
+ pub.shouldExit(exit_codes.UNAVAILABLE);
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart
new file mode 100644
index 0000000..138cd04
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart
@@ -0,0 +1,68 @@
+// 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.
+
+library pub_tests;
+
+import 'package:path/path.dart' as path;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../../../lib/src/io.dart';
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('reinstalls previously cached git packages', () {
+ // Create two cached revisions of foo.
+ d.git('foo.git', [
+ d.libDir('foo'),
+ d.libPubspec('foo', '1.0.0')
+ ]).create();
+
+ d.appDir({"foo": {"git": "../foo.git"}}).create();
+ pubGet();
+
+ d.git('foo.git', [
+ d.libDir('foo'),
+ d.libPubspec('foo', '1.0.1')
+ ]).commit();
+
+ pubUpgrade();
+
+ // Break them.
+ var fooDirs;
+ schedule(() {
+ // Find the cached foo packages for each revision.
+ var gitCacheDir = path.join(sandboxDir, cachePath, "git");
+ fooDirs = listDir(gitCacheDir)
+ .where((dir) => path.basename(dir).startsWith("foo-")).toList();
+
+ // Delete "foo.dart" from them.
+ for (var dir in fooDirs) {
+ deleteEntry(path.join(dir, "lib", "foo.dart"));
+ }
+ });
+
+ // Repair them.
+ schedulePub(args: ["cache", "repair"],
+ output: '''
+ Resetting Git repository for foo 1.0.0...
+ Resetting Git repository for foo 1.0.1...
+ Reinstalled 2 packages.''');
+
+ // The missing libraries should have been replaced.
+ schedule(() {
+ var fooLibs = fooDirs.map((dir) {
+ var fooDirName = path.basename(dir);
+ return d.dir(fooDirName, [
+ d.dir("lib", [d.file("foo.dart", 'main() => "foo";')])
+ ]);
+ }).toList();
+
+ d.dir(cachePath, [
+ d.dir("git", fooLibs)
+ ]).validate();
+ });
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart
new file mode 100644
index 0000000..5df38af
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart
@@ -0,0 +1,56 @@
+// 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.
+
+library pub_tests;
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('reinstalls previously cached hosted packages', () {
+ servePackages([
+ packageMap("foo", "1.2.3"),
+ packageMap("foo", "1.2.4"),
+ packageMap("foo", "1.2.5"),
+ packageMap("bar", "1.2.3"),
+ packageMap("bar", "1.2.4")
+ ]);
+
+ // Set up a cache with some broken packages.
+ d.dir(cachePath, [
+ d.dir('hosted', [
+ d.async(port.then((p) => d.dir('127.0.0.1%58$p', [
+ d.dir("foo-1.2.3", [
+ d.libPubspec("foo", "1.2.3"),
+ d.file("broken.txt")
+ ]),
+ d.dir("foo-1.2.5", [
+ d.libPubspec("foo", "1.2.5"),
+ d.file("broken.txt")
+ ]),
+ d.dir("bar-1.2.4", [
+ d.libPubspec("bar", "1.2.4"),
+ d.file("broken.txt")
+ ])
+ ])))
+ ])
+ ]).create();
+
+ // Repair them.
+ schedulePub(args: ["cache", "repair"],
+ output: '''
+ Downloading bar 1.2.4...
+ Downloading foo 1.2.3...
+ Downloading foo 1.2.5...
+ Reinstalled 3 packages.''');
+
+ // The broken versions should have been replaced.
+ d.hostedCache([
+ d.dir("bar-1.2.4", [d.nothing("broken.txt")]),
+ d.dir("foo-1.2.3", [d.nothing("broken.txt")]),
+ d.dir("foo-1.2.5", [d.nothing("broken.txt")])
+ ]).validate();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart b/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
index 35552bb..95698fc 100644
--- a/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
+++ b/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
@@ -20,6 +20,6 @@
pubGet(error: """Could not find package foo at "$badPath".
Depended on by:
-- myapp""");
+- myapp 0.0.0""");
});
}
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/hosted/offline_test.dart b/sdk/lib/_internal/pub/test/hosted/offline_test.dart
index b799198..981d24d 100644
--- a/sdk/lib/_internal/pub/test/hosted/offline_test.dart
+++ b/sdk/lib/_internal/pub/test/hosted/offline_test.dart
@@ -61,7 +61,7 @@
pubCommand(command, args: ['--offline'], error:
"Package foo has no versions that match >2.0.0 derived from:\n"
- "- myapp depends on version >2.0.0");
+ "- myapp 0.0.0 depends on version >2.0.0");
});
});
}
diff --git a/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart b/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
index 3bc488a..a12ab1e 100644
--- a/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
+++ b/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
@@ -115,7 +115,7 @@
pubGet(error: """
Package barback has no versions that match >=$current <$max derived from:
-- myapp depends on version any
+- myapp 0.0.0 depends on version any
- pub itself depends on version >=$current <$max""");
});
@@ -129,7 +129,7 @@
pubGet(error: """
Incompatible version constraints on barback:
-- myapp depends on version $previous
+- myapp 0.0.0 depends on version $previous
- pub itself depends on version >=$current <$max""");
});
}
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/pub_test.dart b/sdk/lib/_internal/pub/test/pub_test.dart
index a7af4aa..168008c 100644
--- a/sdk/lib/_internal/pub/test/pub_test.dart
+++ b/sdk/lib/_internal/pub/test/pub_test.dart
@@ -93,7 +93,8 @@
-h, --help Print usage information for this command.
Available subcommands:
- add Install a package.
+ add Install a package.
+ repair Reinstall cached packages.
''');
});
@@ -131,7 +132,8 @@
-h, --help Print usage information for this command.
Available subcommands:
- add Install a package.
+ add Install a package.
+ repair Reinstall cached packages.
''',
exitCode: exit_codes.USAGE);
});
@@ -176,7 +178,8 @@
-h, --help Print usage information for this command.
Available subcommands:
- add Install a package.
+ add Install a package.
+ repair Reinstall cached packages.
''',
exitCode: exit_codes.USAGE);
});
@@ -277,7 +280,8 @@
-h, --help Print usage information for this command.
Available subcommands:
- add Install a package.
+ add Install a package.
+ repair Reinstall cached packages.
''',
exitCode: exit_codes.USAGE);
});
diff --git a/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart b/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart
new file mode 100644
index 0000000..d066b2d
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+const LAZY_TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class LazyRewriteTransformer extends Transformer implements LazyTransformer {
+ LazyRewriteTransformer.asPlugin();
+
+ String get allowedExtensions => '.in';
+
+ Future apply(Transform transform) {
+ transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".mid");
+ transform.addOutput(new Asset.fromString(id, "\$contents.mid"));
+ });
+ }
+
+ Future declareOutputs(DeclaringTransform transform) {
+ transform.declareOutput(transform.primaryId.changeExtension(".mid"));
+ return new Future.value();
+ }
+}
+""";
+
+const DECLARING_TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class DeclaringRewriteTransformer extends Transformer
+ implements DeclaringTransformer {
+ DeclaringRewriteTransformer.asPlugin();
+
+ String get allowedExtensions => '.mid';
+
+ Future apply(Transform transform) {
+ transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".final");
+ transform.addOutput(new Asset.fromString(id, "\$contents.final"));
+ });
+ }
+
+ Future declareOutputs(DeclaringTransform transform) {
+ transform.declareOutput(transform.primaryId.changeExtension(".final"));
+ return new Future.value();
+ }
+}
+""";
+
+main() {
+ initConfig();
+ integration("supports a user-defined declaring transformer", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["myapp/src/lazy", "myapp/src/declaring"]
+ }),
+ d.dir("lib", [d.dir("src", [
+ // Include a lazy transformer before the declaring transformer, because
+ // otherwise its behavior is indistinguishable from a normal
+ // transformer.
+ d.file("lazy.dart", LAZY_TRANSFORMER),
+ d.file("declaring.dart", DECLARING_TRANSFORMER)
+ ])]),
+ d.dir("web", [
+ d.file("foo.in", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var server = pubServe();
+ // The build should complete without either transformer logging anything.
+ server.stdout.expect('Build completed successfully');
+
+ requestShouldSucceed("foo.final", "foo.mid.final");
+ server.stdout.expect(emitsLines(
+ '[Info from LazyRewrite]:\n'
+ 'Rewriting myapp|web/foo.in.\n'
+ '[Info from DeclaringRewrite]:\n'
+ 'Rewriting myapp|web/foo.mid.'));
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart b/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart
new file mode 100644
index 0000000..fd88315
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+const TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class LazyRewriteTransformer extends Transformer implements LazyTransformer {
+ LazyRewriteTransformer.asPlugin();
+
+ String get allowedExtensions => '.txt';
+
+ Future apply(Transform transform) {
+ transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".out");
+ transform.addOutput(new Asset.fromString(id, "\$contents.out"));
+ });
+ }
+
+ Future declareOutputs(DeclaringTransform transform) {
+ transform.declareOutput(transform.primaryId.changeExtension(".out"));
+ return new Future.value();
+ }
+}
+""";
+
+main() {
+ initConfig();
+ integration("supports a user-defined lazy transformer", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["myapp/src/transformer"]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", TRANSFORMER)
+ ])]),
+ d.dir("web", [
+ d.file("foo.txt", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var server = pubServe();
+ // The build should complete without the transformer logging anything.
+ server.stdout.expect('Build completed successfully');
+
+ requestShouldSucceed("foo.out", "foo.out");
+ server.stdout.expect(emitsLines(
+ '[Info from LazyRewrite]:\n'
+ 'Rewriting myapp|web/foo.txt.'));
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart b/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart
deleted file mode 100644
index 3d16fd1..0000000
--- a/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library pub_tests;
-
-import 'package:path/path.dart' as path;
-import 'package:scheduled_test/scheduled_stream.dart';
-import 'package:scheduled_test/scheduled_test.dart';
-
-import '../descriptor.dart' as d;
-import '../test_pub.dart';
-import 'utils.dart';
-
-main() {
- initConfig();
- integration('warns user about top-level "assets" directories', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [
- d.dir('assets')
- ]),
- d.dir('test', [
- d.dir('assets')
- ]),
- d.dir('web', [
- d.file('index.html'),
- d.dir('assets')
- ])
- ]).create();
-
- var pub = pubServe();
- waitForBuildSuccess();
-
- pub.stderr.expect(emitsLines('''
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the directory "${path.join('bin', 'assets')}".
-Please rename the directory "${path.join('test', 'assets')}".
-Please rename the directory "${path.join('web', 'assets')}".'''));
- endPubServe();
- });
-
- integration('warns user about top-level "assets" files', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [
- d.file('assets', '...')
- ]),
- d.dir('test', [
- d.file('assets', '...')
- ]),
- d.dir('web', [
- d.file('index.html'),
- d.file('assets', '...')
- ])
- ]).create();
-
- var pub = pubServe();
- waitForBuildSuccess();
- pub.stderr.expect(emitsLines('''
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the file "${path.join('bin', 'assets')}".
-Please rename the file "${path.join('test', 'assets')}".
-Please rename the file "${path.join('web', 'assets')}".'''));
- endPubServe();
- });
-
- integration('does not warn on "assets" in subdirectories', () {
- d.dir(appPath, [
- d.appPubspec(),
- d.dir('web', [
- d.file('index.html'),
- d.dir('foo', [
- d.dir('assets')
- ]),
- d.dir('bar', [
- d.file('assets', '...')
- ])
- ])
- ]).create();
-
- var pub = pubServe();
- waitForBuildSuccess();
- endPubServe();
- pub.stderr.expect(never(contains("Warning")));
- });
-}
diff --git a/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart b/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart
new file mode 100644
index 0000000..2f4fc497c
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_stream.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("warns if an asset from the entrypoint package's 'asset' "
+ "directory is requested", () {
+ d.dir(appPath, [
+ d.appPubspec(),
+ d.dir("asset", [
+ d.file("file.txt", "body")
+ ])
+ ]).create();
+
+ var pub = pubServe();
+ requestShouldSucceed("assets/myapp/file.txt", "body");
+
+ pub.stderr.expect(consumeThrough(emitsLines('''
+Warning: Support for the "asset" directory is deprecated and will be removed soon.
+Please move "asset/file.txt" to "lib/file.txt".''')));
+ endPubServe();
+ });
+
+ integration("warns if an asset from a dependency's 'asset' directory is "
+ "requested", () {
+ d.dir("foo", [
+ d.libPubspec("foo", "0.0.1"),
+ d.dir("asset", [
+ d.file("file.txt", "body")
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.appPubspec({
+ "foo": {"path": "../foo"}
+ })
+ ]).create();
+
+ var pub = pubServe(shouldGetFirst: true);
+ requestShouldSucceed("assets/foo/file.txt", "body");
+
+ pub.stderr.expect(consumeThrough(emitsLines('''
+Warning: Support for the "asset" directory is deprecated and will be removed soon.
+Please ask the maintainer of "foo" to move "asset/file.txt" to "lib/file.txt".''')));
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/test_pub.dart b/sdk/lib/_internal/pub/test/test_pub.dart
index 42d0aa0..0eb2417 100644
--- a/sdk/lib/_internal/pub/test/test_pub.dart
+++ b/sdk/lib/_internal/pub/test/test_pub.dart
@@ -611,7 +611,7 @@
/// [hosted] is a list of package names to version strings for dependencies on
/// hosted packages.
void createLockFile(String package, {Iterable<String> sandbox,
- Iterable<String> pkg, Map<String, Version> hosted}) {
+ Iterable<String> pkg, Map<String, String> hosted}) {
var dependencies = {};
if (sandbox != null) {
diff --git a/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart b/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart
new file mode 100644
index 0000000..d38baa6
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import '../serve/utils.dart';
+
+main() {
+ initConfig();
+ integration("loads a transformer defined in an exported library", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["myapp"]
+ }),
+ d.dir("lib", [
+ d.file("myapp.dart", "export 'src/transformer.dart';"),
+ d.dir("src", [
+ d.file("transformer.dart", REWRITE_TRANSFORMER)
+ ])
+ ]),
+ d.dir("web", [
+ d.file("foo.txt", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ pubServe();
+ requestShouldSucceed("foo.out", "foo.out");
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/version_solver_test.dart b/sdk/lib/_internal/pub/test/version_solver_test.dart
index 11afd79..0ea5ea5 100644
--- a/sdk/lib/_internal/pub/test/version_solver_test.dart
+++ b/sdk/lib/_internal/pub/test/version_solver_test.dart
@@ -413,6 +413,32 @@
'a 1.0.0': {},
'b 1.0.0': {}
}, error: noVersion(['myapp', 'b']), maxTries: 1);
+
+
+ // This is a regression test for #18300.
+ testResolve('...', {
+ "myapp 0.0.0": {
+ "angular": "any",
+ "collection": "any"
+ },
+ "analyzer 0.12.2": {},
+ "angular 0.10.0": {
+ "di": ">=0.0.32 <0.1.0",
+ "collection": ">=0.9.1 <1.0.0"
+ },
+ "angular 0.9.11": {
+ "di": ">=0.0.32 <0.1.0",
+ "collection": ">=0.9.1 <1.0.0"
+ },
+ "angular 0.9.10": {
+ "di": ">=0.0.32 <0.1.0",
+ "collection": ">=0.9.1 <1.0.0"
+ },
+ "collection 0.9.0": {},
+ "collection 0.9.1": {},
+ "di 0.0.37": {"analyzer": ">=0.13.0 <0.14.0"},
+ "di 0.0.36": {"analyzer": ">=0.13.0 <0.14.0"}
+ }, error: noVersion(['myapp', 'angular', 'collection']), maxTries: 9);
}
badSource() {
@@ -681,6 +707,27 @@
'c': '2.0.0'
}, maxTries: 2);
+ // This is similar to the above test. When getting the number of versions of
+ // a package to determine which to traverse first, versions that are
+ // disallowed by the root package's constraints should not be considered.
+ // Here, foo has more versions of bar in total (4), but fewer that meet
+ // myapp's constraints (only 2). There is no solution, but we will do less
+ // backtracking if foo is tested first.
+ testResolve('take root package constraints into counting versions', {
+ "myapp 0.0.0": {
+ "foo": ">2.0.0",
+ "bar": "any"
+ },
+ "foo 1.0.0": {"none": "2.0.0"},
+ "foo 2.0.0": {"none": "2.0.0"},
+ "foo 3.0.0": {"none": "2.0.0"},
+ "foo 4.0.0": {"none": "2.0.0"},
+ "bar 1.0.0": {},
+ "bar 2.0.0": {},
+ "bar 3.0.0": {},
+ "none 1.0.0": {}
+ }, error: noVersion(["foo", "none"]), maxTries: 2);
+
// This sets up a hundred versions of foo and bar, 0.0.0 through 9.9.0. Each
// version of foo depends on a baz with the same major version. Each version
// of bar depends on a baz with the same minor version. There is only one
@@ -1372,7 +1419,7 @@
/// The "from mock" optional suffix is the name of a source for the package.
/// If omitted, it defaults to "mock1".
PackageId parseSpec(String text, [String version]) {
- var pattern = new RegExp(r"(([a-z]*)(-[a-z]+)?)( ([^ ]+))?( from (.*))?$");
+ var pattern = new RegExp(r"(([a-z_]*)(-[a-z_]+)?)( ([^ ]+))?( from (.*))?$");
var match = pattern.firstMatch(text);
if (match == null) {
throw new FormatException("Could not parse spec '$text'.");
diff --git a/sdk/lib/async/schedule_microtask.dart b/sdk/lib/async/schedule_microtask.dart
index 02114d4..7581c0a 100644
--- a/sdk/lib/async/schedule_microtask.dart
+++ b/sdk/lib/async/schedule_microtask.dart
@@ -31,13 +31,6 @@
try {
_asyncRunCallbackLoop();
} catch (e, s) {
- // TODO(efortuna): Remove these debugging statements.
- if (e is Error) {
- print('microtask error ${e.stackTrace}');
- } else {
- print('microtask error $e');
- }
- print('microtask error stack trace: $s');
_AsyncRun._scheduleImmediate(_asyncRunCallback);
_nextCallback = _nextCallback.next;
rethrow;
diff --git a/sdk/lib/core/expando.dart b/sdk/lib/core/expando.dart
index 5b239de..2ecd2dc 100644
--- a/sdk/lib/core/expando.dart
+++ b/sdk/lib/core/expando.dart
@@ -6,6 +6,21 @@
/**
* An [Expando] allows adding new properties to objects.
+ *
+ * Does not work on numbers, strings, booleans or null.
+ *
+ * An `Expando` does not hold on to the added property value after an object
+ * becomes inacessible.
+ *
+ * Since you can always create a new number that is identical to an existing
+ * number, it means that an expando property on a number could never be
+ * released. To avoid this, expando properties cannot be added to numbers.
+ * The same argument applies to strings, booleans and null, which also have
+ * literals that evaluate to identical values when they occur more than once.
+ *
+ * There is no restriction on other classes, even for compile time constant
+ * objects. Be careful if adding expando properties to compile time constants,
+ * since they will stay alive forever.
*/
class Expando<T> {
@@ -32,6 +47,8 @@
* Gets the value of this [Expando]'s property on the given
* object. If the object hasn't been expanded, the method returns
* [:null:].
+ *
+ * The object must not be a number, a string, a boolean or null.
*/
external T operator [](Object object);
@@ -39,6 +56,8 @@
* Sets the value of this [Expando]'s property on the given
* object. Properties can effectively be removed again by setting
* their value to null.
+ *
+ * The object must not be a number, a string, a boolean or null.
*/
external void operator []=(Object object, T value);
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 1bc1d73..a8b6082 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -10170,7 +10170,7 @@
// Workaround for Chrome bug 229142- URIs are not resolved in new doc.
var base = _parseDocument.createElement('base');
- base.href = document._baseUri;
+ base.href = document.baseUri;
_parseDocument.head.append(base);
}
var contextElement;
@@ -14353,32 +14353,6 @@
* This custom element can also be instantiated via HTML using the syntax
* `<input is="x-bar"></input>`
*
- * The [nativeTagName] parameter is needed by platforms without native support
- * when subclassing a native type other than:
- *
- * * HtmlElement
- * * SvgElement
- * * AnchorElement
- * * AudioElement
- * * ButtonElement
- * * CanvasElement
- * * DivElement
- * * ImageElement
- * * InputElement
- * * LIElement
- * * LabelElement
- * * MenuElement
- * * MeterElement
- * * OListElement
- * * OptionElement
- * * OutputElement
- * * ParagraphElement
- * * PreElement
- * * ProgressElement
- * * SelectElement
- * * SpanElement
- * * UListElement
- * * VideoElement
*/
void register(String tag, Type customElementClass, {String extendsTag}) {
_registerCustomElement(JS('', 'window'), this, tag, customElementClass,
@@ -14420,6 +14394,19 @@
@Experimental()
Stream<Event> get onVisibilityChange =>
visibilityChangeEvent.forTarget(this);
+
+ /// Creates an element upgrader which can be used to change the Dart wrapper
+ /// type for elements.
+ ///
+ /// The type specified must be a subclass of HtmlElement, when an element is
+ /// upgraded then the created constructor will be invoked on that element.
+ ///
+ /// If the type is not a direct subclass of HtmlElement then the extendsTag
+ /// parameter must be provided.
+ @Experimental()
+ ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+ return new _JSElementUpgrader(this, type, extendsTag);
+ }
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -15298,7 +15285,7 @@
@DomName('XMLHttpRequestUpload')
// http://xhr.spec.whatwg.org/#xmlhttprequestupload
@Experimental()
-class HttpRequestUpload extends HttpRequestEventTarget native "XMLHttpRequestUpload,XMLHttpRequestEventTarget" {
+class HttpRequestUpload extends HttpRequestEventTarget native "XMLHttpRequestUpload" {
// To suppress missing implicit constructor warnings.
factory HttpRequestUpload._() { throw new UnsupportedError("Not supported"); }
}
@@ -19904,7 +19891,7 @@
@JSName('baseURI')
@DomName('Node.baseURI')
@DocsEditable()
- final String _baseUri;
+ final String baseUri;
/**
* A list of this node's children.
@@ -27469,59 +27456,29 @@
}
// API level getter and setter for Location.
- // TODO: The cross domain safe wrapper can be inserted here or folded into
- // _LocationWrapper.
+ // TODO: The cross domain safe wrapper can be inserted here.
/**
* The current location of this window.
*
* Location currentLocation = window.location;
* print(currentLocation.href); // 'http://www.example.com:80/'
*/
- Location get location {
- // Firefox work-around for Location. The Firefox location object cannot be
- // made to behave like a Dart object so must be wrapped.
- var result = _location;
- if (_isDartLocation(result)) return result; // e.g. on Chrome.
- if (null == _location_wrapper) {
- _location_wrapper = new _LocationWrapper(result);
- }
- return _location_wrapper;
- }
+ Location get location => _location;
// TODO: consider forcing users to do: window.location.assign('string').
/**
* Sets the window's location, which causes the browser to navigate to the new
- * location. [value] may be a Location object or a string.
+ * location. [value] may be a Location object or a String.
*/
void set location(value) {
- if (value is _LocationWrapper) {
- _location = value._ptr;
- } else {
- _location = value;
- }
+ _location = value;
}
- _LocationWrapper _location_wrapper; // Cached wrapped Location object.
-
// Native getter and setter to access raw Location object.
- dynamic get _location => JS('Location|=Object', '#.location', this);
+ dynamic get _location => JS('Location|Null', '#.location', this);
void set _location(value) {
JS('void', '#.location = #', this, value);
}
- // Prevent compiled from thinking 'location' property is available for a Dart
- // member.
- @JSName('location')
- _protect_location() native;
-
- static _isDartLocation(thing) {
- // On Firefox the code that implements 'is Location' fails to find the patch
- // stub on Object.prototype and throws an exception.
- try {
- return thing is Location;
- } catch (e) {
- return false;
- }
- }
/**
* Called to draw an animation frame and then request the window to repaint
@@ -35328,7 +35285,7 @@
if (extendsTagName == null) {
if (baseClassName != 'HTMLElement') {
throw new UnsupportedError('Class must provide extendsTag if base '
- 'native class is not HTMLElement');
+ 'native class is not HtmlElement');
}
} else {
if (!JS('bool', '(#.createElement(#) instanceof window[#])',
@@ -35369,6 +35326,63 @@
void _initializeCustomElement(Element e) {
// TODO(blois): Add validation that this is only in response to an upgrade.
}
+
+/// Dart2JS implementation of ElementUpgrader
+class _JSElementUpgrader implements ElementUpgrader {
+ var _interceptor;
+ var _constructor;
+ var _nativeType;
+
+ _JSElementUpgrader(Document document, Type type, String extendsTag) {
+ var interceptorClass = findInterceptorConstructorForType(type);
+ if (interceptorClass == null) {
+ throw new ArgumentError(type);
+ }
+
+ _constructor = findConstructorForNativeSubclassType(type, 'created');
+ if (_constructor == null) {
+ throw new ArgumentError("$type has no constructor called 'created'");
+ }
+
+ // Workaround for 13190- use an article element to ensure that HTMLElement's
+ // interceptor is resolved correctly.
+ getNativeInterceptor(new Element.tag('article'));
+
+ var baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
+ if (baseClassName == null) {
+ throw new ArgumentError(type);
+ }
+
+ if (extendsTag == null) {
+ if (baseClassName != 'HTMLElement') {
+ throw new UnsupportedError('Class must provide extendsTag if base '
+ 'native class is not HtmlElement');
+ }
+ _nativeType = HtmlElement;
+ } else {
+ var element = document.createElement(extendsTag);
+ if (!JS('bool', '(# instanceof window[#])',
+ element, baseClassName)) {
+ throw new UnsupportedError(
+ 'extendsTag does not match base native class');
+ }
+ _nativeType = element.runtimeType;
+ }
+
+ _interceptor = JS('=Object', '#.prototype', interceptorClass);
+ }
+
+ Element upgrade(Element element) {
+ // Only exact type matches are supported- cannot be a subclass.
+ if (element.runtimeType != _nativeType) {
+ throw new ArgumentError('element is not subclass of $_nativeType');
+ }
+
+ setNativeSubclassDispatchRecord(element, _interceptor);
+ JS('', '#(#)', _constructor, element);
+ return element;
+ }
+}
// Copyright (c) 2012, 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.
@@ -35723,94 +35737,6 @@
bool get repeat => throw new UnimplementedError();
dynamic get _get_view => throw new UnimplementedError();
}
-// Copyright (c) 2011, 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.
-
-
-// On Firefox 11, the object obtained from 'window.location' is very strange.
-// It can't be monkey-patched and seems immune to putting methods on
-// Object.prototype. We are forced to wrap the object.
-
-class _LocationWrapper implements Location {
-
- final _ptr; // Opaque reference to real location.
-
- _LocationWrapper(this._ptr);
-
- // TODO(sra): Replace all the _set and _get calls with 'JS' forms.
-
- // final List<String> ancestorOrigins;
- List<String> get ancestorOrigins => _get(_ptr, 'ancestorOrigins');
-
- // String hash;
- String get hash => _get(_ptr, 'hash');
- void set hash(String value) {
- _set(_ptr, 'hash', value);
- }
-
- // String host;
- String get host => _get(_ptr, 'host');
- void set host(String value) {
- _set(_ptr, 'host', value);
- }
-
- // String hostname;
- String get hostname => _get(_ptr, 'hostname');
- void set hostname(String value) {
- _set(_ptr, 'hostname', value);
- }
-
- // String href;
- String get href => _get(_ptr, 'href');
- void set href(String value) {
- _set(_ptr, 'href', value);
- }
-
- // final String origin;
- String get origin {
- if (JS('bool', '("origin" in #)', _ptr)) {
- return JS('String', '#.origin', _ptr);
- }
- return '${this.protocol}//${this.host}';
- }
-
- // String pathname;
- String get pathname => _get(_ptr, 'pathname');
- void set pathname(String value) {
- _set(_ptr, 'pathname', value);
- }
-
- // String port;
- String get port => _get(_ptr, 'port');
- void set port(String value) {
- _set(_ptr, 'port', value);
- }
-
- // String protocol;
- String get protocol => _get(_ptr, 'protocol');
- void set protocol(String value) {
- _set(_ptr, 'protocol', value);
- }
-
- // String search;
- String get search => _get(_ptr, 'search');
- void set search(String value) {
- _set(_ptr, 'search', value);
- }
-
- void assign(String url) => JS('void', '#.assign(#)', _ptr, url);
-
- void reload() => JS('void', '#.reload()', _ptr);
-
- void replace(String url) => JS('void', '#.replace(#)', _ptr, url);
-
- String toString() => JS('String', '#.toString()', _ptr);
-
-
- static _get(p, m) => JS('var', '#[#]', p, m);
- static _set(p, m, v) => JS('void', '#[#] = #', p, m, v);
-}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -35900,6 +35826,15 @@
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+ /// Upgrade the specified element to be of the Dart type this was created for.
+ ///
+ /// After upgrading the element passed in is invalid and the returned value
+ /// should be used instead.
+ Element upgrade(Element element);
+}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 1a2f68a..3b10f34 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -106,7 +106,8 @@
Future<Isolate> spawnDomUri(Uri uri, List<String> args, message) {
// TODO(17738): Plumb arguments and return value through.
return _Utils.spawnDomUri(uri.toString());
-}// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+}
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -10469,7 +10470,7 @@
// Workaround for Chrome bug 229142- URIs are not resolved in new doc.
var base = _parseDocument.createElement('base');
- base.href = document._baseUri;
+ base.href = document.baseUri;
_parseDocument.head.append(base);
}
var contextElement;
@@ -14766,32 +14767,6 @@
* This custom element can also be instantiated via HTML using the syntax
* `<input is="x-bar"></input>`
*
- * The [nativeTagName] parameter is needed by platforms without native support
- * when subclassing a native type other than:
- *
- * * HtmlElement
- * * SvgElement
- * * AnchorElement
- * * AudioElement
- * * ButtonElement
- * * CanvasElement
- * * DivElement
- * * ImageElement
- * * InputElement
- * * LIElement
- * * LabelElement
- * * MenuElement
- * * MeterElement
- * * OListElement
- * * OptionElement
- * * OutputElement
- * * ParagraphElement
- * * PreElement
- * * ProgressElement
- * * SelectElement
- * * SpanElement
- * * UListElement
- * * VideoElement
*/
void register(String tag, Type customElementClass, {String extendsTag}) {
_Utils.register(this, tag, customElementClass, extendsTag);
@@ -14822,6 +14797,19 @@
@Experimental()
Stream<Event> get onVisibilityChange =>
visibilityChangeEvent.forTarget(this);
+
+ /// Creates an element upgrader which can be used to change the Dart wrapper
+ /// type for elements.
+ ///
+ /// The type specified must be a subclass of HtmlElement, when an element is
+ /// upgraded then the created constructor will be invoked on that element.
+ ///
+ /// If the type is not a direct subclass of HtmlElement then the extendsTag
+ /// parameter must be provided.
+ @Experimental()
+ ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+ return new _VMElementUpgrader(this, type, extendsTag);
+ }
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -21631,7 +21619,7 @@
@DomName('Node.baseURI')
@DocsEditable()
- String get _baseUri native "Node_baseURI_Getter";
+ String get baseUri native "Node_baseURI_Getter";
/**
* A list of this node's children.
@@ -28952,13 +28940,13 @@
if ((blob_OR_source_OR_stream is Blob || blob_OR_source_OR_stream == null)) {
return _createObjectURL_1(blob_OR_source_OR_stream);
}
- if ((blob_OR_source_OR_stream is MediaSource || blob_OR_source_OR_stream == null)) {
+ if ((blob_OR_source_OR_stream is MediaStream || blob_OR_source_OR_stream == null)) {
return _createObjectURL_2(blob_OR_source_OR_stream);
}
- if ((blob_OR_source_OR_stream is _WebKitMediaSource || blob_OR_source_OR_stream == null)) {
+ if ((blob_OR_source_OR_stream is MediaSource || blob_OR_source_OR_stream == null)) {
return _createObjectURL_3(blob_OR_source_OR_stream);
}
- if ((blob_OR_source_OR_stream is MediaStream || blob_OR_source_OR_stream == null)) {
+ if ((blob_OR_source_OR_stream is _WebKitMediaSource || blob_OR_source_OR_stream == null)) {
return _createObjectURL_4(blob_OR_source_OR_stream);
}
throw new ArgumentError("Incorrect number or type of arguments");
@@ -38002,6 +37990,94 @@
T get current => _current;
}
+// 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.
+
+
+/// Dartium ElementUpgrader implementation.
+class _VMElementUpgrader implements ElementUpgrader {
+ final Type _type;
+ final Type _nativeType;
+
+ _VMElementUpgrader(Document document, Type type, String extendsTag) :
+ _type = type,
+ _nativeType = _validateCustomType(type).reflectedType {
+
+ if (extendsTag == null) {
+ if (_nativeType != HtmlElement) {
+ throw new UnsupportedError('Class must provide extendsTag if base '
+ 'native class is not HtmlElement');
+ }
+ } else {
+ if (document.createElement(extendsTag).runtimeType != _nativeType) {
+ throw new UnsupportedError(
+ 'extendsTag does not match base native class');
+ }
+ }
+ }
+
+ Element upgrade(Element element) {
+ if (element.runtimeType != _nativeType) {
+ throw new UnsupportedError('Element is incorrect type');
+ }
+ return _Utils.changeElementWrapper(element, _type);
+ return null;
+ }
+}
+
+/// Validates that the custom type is properly formed-
+///
+/// * Is a user-defined class.
+/// * Has a created constructor with zero args.
+/// * Derives from an Element subclass.
+///
+/// Then returns the native base class.
+ClassMirror _validateCustomType(Type type) {
+ ClassMirror cls = reflectClass(type);
+ if (_isBuiltinType(cls)) {
+ throw new UnsupportedError('Invalid custom element from '
+ '${(cls.owner as LibraryMirror).uri}.');
+ }
+
+ var className = MirrorSystem.getName(cls.simpleName);
+ var createdConstructor = cls.declarations[new Symbol('$className.created')];
+ if (createdConstructor == null ||
+ createdConstructor is! MethodMirror ||
+ !createdConstructor.isConstructor) {
+ throw new UnsupportedError(
+ 'Class is missing constructor $className.created');
+ }
+
+ if (createdConstructor.parameters.length > 0) {
+ throw new UnsupportedError(
+ 'Constructor $className.created must take zero arguments');
+ }
+
+ Symbol objectName = reflectClass(Object).qualifiedName;
+ bool isRoot(ClassMirror cls) =>
+ cls == null || cls.qualifiedName == objectName;
+ Symbol elementName = reflectClass(HtmlElement).qualifiedName;
+ bool isElement(ClassMirror cls) =>
+ cls != null && cls.qualifiedName == elementName;
+ ClassMirror superClass = cls.superclass;
+ ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
+ while(!isRoot(superClass) && !isElement(superClass)) {
+ superClass = superClass.superclass;
+ if (nativeClass == null && _isBuiltinType(superClass)) {
+ nativeClass = superClass;
+ }
+ }
+ return nativeClass;
+}
+
+
+bool _isBuiltinType(ClassMirror cls) {
+ // TODO(vsm): Find a less hackish way to do this.
+ LibraryMirror lib = cls.owner;
+ String libName = lib.uri.toString();
+ return libName.startsWith('dart:');
+}
/**
* A custom KeyboardEvent that attempts to eliminate cross-browser
* inconsistencies, and also provide both keyCode and charCode information
@@ -38248,6 +38324,15 @@
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+ /// Upgrade the specified element to be of the Dart type this was created for.
+ ///
+ /// After upgrading the element passed in is invalid and the returned value
+ /// should be used instead.
+ Element upgrade(Element element);
+}
// Copyright (c) 2012, 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.
@@ -38645,48 +38730,10 @@
static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
- static bool _isBuiltinType(ClassMirror cls) {
- // TODO(vsm): Find a less hackish way to do this.
- LibraryMirror lib = cls.owner;
- String libName = lib.uri.toString();
- return libName.startsWith('dart:');
- }
-
static void register(Document document, String tag, Type type,
String extendsTagName) {
- // TODO(vsm): Move these checks into native code.
- ClassMirror cls = reflectClass(type);
- if (_isBuiltinType(cls)) {
- throw new UnsupportedError("Invalid custom element from ${(cls.owner as LibraryMirror).uri}.");
- }
- var className = MirrorSystem.getName(cls.simpleName);
- var createdConstructor = cls.declarations[new Symbol('$className.created')];
- if (createdConstructor == null ||
- createdConstructor is! MethodMirror ||
- !createdConstructor.isConstructor) {
- throw new UnsupportedError(
- 'Class is missing constructor $className.created');
- }
+ var nativeClass = _validateCustomType(type);
- if (createdConstructor.parameters.length > 0) {
- throw new UnsupportedError(
- 'Constructor $className.created must take zero arguments');
- }
-
- Symbol objectName = reflectClass(Object).qualifiedName;
- bool isRoot(ClassMirror cls) =>
- cls == null || cls.qualifiedName == objectName;
- Symbol elementName = reflectClass(HtmlElement).qualifiedName;
- bool isElement(ClassMirror cls) =>
- cls != null && cls.qualifiedName == elementName;
- ClassMirror superClass = cls.superclass;
- ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
- while(!isRoot(superClass) && !isElement(superClass)) {
- superClass = superClass.superclass;
- if (nativeClass == null && _isBuiltinType(superClass)) {
- nativeClass = superClass;
- }
- }
if (extendsTagName == null) {
if (nativeClass.reflectedType != HtmlElement) {
throw new UnsupportedError('Class must provide extendsTag if base '
@@ -38703,6 +38750,8 @@
static Element createElement(Document document, String tagName) native "Utils_createElement";
static void initializeCustomElement(HtmlElement element) native "Utils_initializeCustomElement";
+
+ static void changeElementWrapper(HtmlElement element, Type type) native "Utils_changeElementWrapper";
}
class _DOMWindowCrossFrame extends NativeFieldWrapperClass2 implements
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index a262c21..1920c37 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -126,6 +126,7 @@
if (_position < 0) {
_controller.addError(new RangeError("Bad start position: $_position"));
_controller.close();
+ _closeCompleter.complete();
return;
}
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 4b8950f..3f0cf7c 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -659,7 +659,7 @@
static String _trimTrailingPathSeparators(String path) {
// Don't handle argument errors here.
if (path is! String) return path;
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
while (path.length > 1 &&
(path.endsWith(Platform.pathSeparator) ||
path.endsWith('/'))) {
@@ -677,7 +677,7 @@
// Don't handle argument errors here.
if (path is! String) return path;
if (path.isEmpty) path = '.';
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
while (!path.endsWith(Platform.pathSeparator) && !path.endsWith('/')) {
path = "$path${Platform.pathSeparator}";
}
diff --git a/sdk/lib/io/http.dart b/sdk/lib/io/http.dart
index 765f583..b869f39 100644
--- a/sdk/lib/io/http.dart
+++ b/sdk/lib/io/http.dart
@@ -1317,6 +1317,86 @@
Future<HttpClientRequest> postUrl(Uri url);
/**
+ * Opens a HTTP connection using the PUT method.
+ *
+ * The server is specified using [host] and [port], and the path
+ * (including possible fragment and query) is specified using
+ * [path].
+ *
+ * See [open] for details.
+ */
+ Future<HttpClientRequest> put(String host, int port, String path);
+
+ /**
+ * Opens a HTTP connection using the PUT method.
+ *
+ * The URL to use is specified in [url].
+ *
+ * See [openUrl] for details.
+ */
+ Future<HttpClientRequest> putUrl(Uri url);
+
+ /**
+ * Opens a HTTP connection using the DELETE method.
+ *
+ * The server is specified using [host] and [port], and the path
+ * (including possible fragment and query) is specified using
+ * [path].
+ *
+ * See [open] for details.
+ */
+ Future<HttpClientRequest> delete(String host, int port, String path);
+
+ /**
+ * Opens a HTTP connection using the DELETE method.
+ *
+ * The URL to use is specified in [url].
+ *
+ * See [openUrl] for details.
+ */
+ Future<HttpClientRequest> deleteUrl(Uri url);
+
+ /**
+ * Opens a HTTP connection using the PATCH method.
+ *
+ * The server is specified using [host] and [port], and the path
+ * (including possible fragment and query) is specified using
+ * [path].
+ *
+ * See [open] for details.
+ */
+ Future<HttpClientRequest> patch(String host, int port, String path);
+
+ /**
+ * Opens a HTTP connection using the PATCH method.
+ *
+ * The URL to use is specified in [url].
+ *
+ * See [openUrl] for details.
+ */
+ Future<HttpClientRequest> patchUrl(Uri url);
+
+ /**
+ * Opens a HTTP connection using the HEAD method.
+ *
+ * The server is specified using [host] and [port], and the path
+ * (including possible fragment and query) is specified using
+ * [path].
+ *
+ * See [open] for details.
+ */
+ Future<HttpClientRequest> head(String host, int port, String path);
+
+ /**
+ * Opens a HTTP connection using the HEAD method.
+ *
+ * The URL to use is specified in [url].
+ *
+ * See [openUrl] for details.
+ */
+ Future<HttpClientRequest> headUrl(Uri url);
+
+ /**
* Sets the function to be called when a site is requesting
* authentication. The URL requested and the security realm from the
* server are passed in the arguments [url] and [realm].
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 5d13f54..d3325229 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -1549,25 +1549,35 @@
return _openUrl(method, url);
}
- Future<HttpClientRequest> get(String host,
- int port,
- String path) {
- return open("get", host, port, path);
- }
+ Future<HttpClientRequest> get(String host, int port, String path)
+ => open("get", host, port, path);
- Future<HttpClientRequest> getUrl(Uri url) {
- return _openUrl("get", url);
- }
+ Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url);
- Future<HttpClientRequest> post(String host,
- int port,
- String path) {
- return open("post", host, port, path);
- }
+ Future<HttpClientRequest> post(String host, int port, String path)
+ => open("post", host, port, path);
- Future<HttpClientRequest> postUrl(Uri url) {
- return _openUrl("post", url);
- }
+ Future<HttpClientRequest> postUrl(Uri url) => _openUrl("post", url);
+
+ Future<HttpClientRequest> put(String host, int port, String path)
+ => open("put", host, port, path);
+
+ Future<HttpClientRequest> putUrl(Uri url) => _openUrl("put", url);
+
+ Future<HttpClientRequest> delete(String host, int port, String path)
+ => open("delete", host, port, path);
+
+ Future<HttpClientRequest> deleteUrl(Uri url) => _openUrl("delete", url);
+
+ Future<HttpClientRequest> head(String host, int port, String path)
+ => open("head", host, port, path);
+
+ Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url);
+
+ Future<HttpClientRequest> patch(String host, int port, String path)
+ => open("patch", host, port, path);
+
+ Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url);
void close({bool force: false}) {
_closing = true;
diff --git a/sdk/lib/io/link.dart b/sdk/lib/io/link.dart
index 5833ac3..13c1869 100644
--- a/sdk/lib/io/link.dart
+++ b/sdk/lib/io/link.dart
@@ -163,7 +163,7 @@
FileStat statSync() => FileStat.statSync(path);
Future<Link> create(String target, {bool recursive: false}) {
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
target = _makeWindowsLinkTarget(target);
}
var result = recursive ? parent.create(recursive: true)
@@ -183,7 +183,7 @@
if (recursive) {
parent.createSync(recursive: true);
}
- if (Platform.operatingSystem == 'windows') {
+ if (Platform.isWindows) {
target = _makeWindowsLinkTarget(target);
}
var result = _File._createLink(path, target);
diff --git a/sdk/lib/io/platform.dart b/sdk/lib/io/platform.dart
index d6109a2..c1447c4 100644
--- a/sdk/lib/io/platform.dart
+++ b/sdk/lib/io/platform.dart
@@ -100,22 +100,22 @@
/**
* Returns true if the operating system is Linux.
*/
- static bool get isLinux => _operatingSystem == "linux";
+ static final bool isLinux = (_operatingSystem == "linux");
/**
* Returns true if the operating system is Mac OS.
*/
- static bool get isMacOS => _operatingSystem == "macos";
+ static final bool isMacOS = (_operatingSystem == "macos");
/**
* Returns true if the operating system is Windows.
*/
- static bool get isWindows => _operatingSystem == "windows";
+ static final bool isWindows = (_operatingSystem == "windows");
/**
* Returns true if the operating system is Android.
*/
- static bool get isAndroid => _operatingSystem == "android";
+ static final bool isAndroid = (_operatingSystem == "android");
/**
* Get the environment for this process.
diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart
index 9c034f4..f36dd83 100644
--- a/sdk/lib/io/stdio.dart
+++ b/sdk/lib/io/stdio.dart
@@ -40,84 +40,73 @@
/**
* Synchronously read a line from stdin. This call will block until a full
- * line is available. The line will contain the newline character(s).
+ * line is available.
*
- * If end-of-file is reached, `null` is returned.
+ * The argument [encoding] can be used to changed how the input should be
+ * decoded. Default is [SYSTEM_ENCODING].
*
- * If end-of-file is reached after some data has already been read, that data
- * is returned.
+ * If [retainNewlines] is `false`, the returned String will not contain the
+ * final newline. If `true`, the returned String will contain the line
+ * terminator. Default is `false`.
+ *
+ * If end-of-file is reached after any bytes have been read from stdin,
+ * that data is returned.
+ * Returns `null` if no bytes preceeded the end of input.
*/
String readLineSync({Encoding encoding: SYSTEM_ENCODING,
bool retainNewlines: false}) {
const CR = 13;
const LF = 10;
- var line = new StringBuffer();
- bool end = false;
- bool lastCharWasCR = false;
- var error;
-
- StreamController<List<int>> controller =
- new StreamController<List<int>>(sync: true);
- Stream stream = controller.stream.transform(encoding.decoder);
- stream.listen((String str) {
- line.write(str);
- }, onError: (e) {
- error = e;
- }, onDone: () {
- end = true;
- });
-
- bool empty = true;
- while (!end) {
- int b = readByteSync();
-
- if (b < 0) {
- // We didn't write the carriage return in case a line feed would be
- // the next character. Add it now.
- if (lastCharWasCR && !retainNewlines) controller.add([CR]);
- controller.close();
- } else {
- empty = false;
- // We consider \r\n and \n as new lines.
- // A \r on its own is treated like a normal character.
-
- if (b == CR) {
- if (lastCharWasCR && !retainNewlines) {
- // We didn't write the carriage return in case a line feed would be
- // the next character.
- // Add it now (since we treat it like a normal character now).
- controller.add([CR]);
- }
- // We add the carriage return only if we keep new lines.
- // Otherwise we need to wait for the next character (in case it is
- // a line feed).
- if (retainNewlines) controller.add([b]);
- lastCharWasCR = true;
- } else if (b == LF) {
- end = true;
- // We don't care if there was a carriage return before. If we keep
- // the line separators it has already been added to the controller.
- // Otherwise we don't want it anyway.
- if (retainNewlines) controller.add([b]);
- controller.close();
- } else {
- // Since the current character is not a line feed we flush the
- // carriage return we didn't write last iteration.
- if (lastCharWasCR) {
- controller.add([CR]);
- lastCharWasCR = false;
- }
- controller.add([b]);
+ final List line = [];
+ // On Windows, if lineMode is disabled, only CR is received.
+ bool crIsNewline = Platform.isWindows &&
+ (stdioType(stdin) == StdioType.TERMINAL) &&
+ !lineMode;
+ if (retainNewlines) {
+ int byte;
+ do {
+ byte = readByteSync();
+ if (byte < 0) {
+ break;
}
+ line.add(byte);
+ } while (byte != LF && !(byte == CR && crIsNewline));
+ if (line.isEmpty) {
+ return null;
}
- if (error != null) {
- // Error during decoding.
- throw error;
+ } else if (crIsNewline) {
+ // CR and LF are both line terminators, neither is retained.
+ while (true) {
+ int byte = readByteSync();
+ if (byte < 0) {
+ if (line.isEmpty) return null;
+ break;
+ }
+ if (byte == LF || byte == CR) break;
+ line.add(byte);
+ }
+ } else {
+ // Case having to hande CR LF as a single unretained line terminator.
+ outer: while (true) {
+ int byte = readByteSync();
+ if (byte == LF) break;
+ if (byte == CR) {
+ do {
+ byte = readByteSync();
+ if (byte == LF) break outer;
+
+ line.add(CR);
+ } while (byte == CR);
+ // Fall through and handle non-CR character.
+ }
+ if (byte < 0) {
+ if (line.isEmpty) return null;
+ break;
+ }
+ line.add(byte);
}
}
-
- if (empty) return null;
- return line.toString();
+ return encoding.decode(line);
}
/**
diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart
index ea6a4df..65f0bcee 100644
--- a/sdk/lib/isolate/isolate.dart
+++ b/sdk/lib/isolate/isolate.dart
@@ -29,12 +29,12 @@
}
class Isolate {
- /** Argument to `ping`: Ask for immediate response. */
- static const int PING_ALIVE = 0;
- /** Argument to `ping`: Ask for response after control events. */
- static const int PING_CONTROL = 1;
- /** Argument to `ping`: Ask for response after normal events. */
- static const int PING_EVENT = 2;
+ /** Argument to `ping` and `kill`: Ask for immediate action. */
+ static const int IMMEDIATE = 0;
+ /** Argument to `ping` and `kill`: Ask for action before the next event. */
+ static const int BEFORE_NEXT_EVENT = 1;
+ /** Argument to `ping` and `kill`: Ask for action after normal events. */
+ static const int AS_EVENT = 2;
/**
* Control port used to send control messages to the isolate.
@@ -115,7 +115,6 @@
external static Future<Isolate> spawnUri(
Uri uri, List<String> args, var message, { bool paused: false });
-
/**
* Requests the isolate to pause.
*
@@ -232,6 +231,41 @@
}
/**
+ * Requests the isolate to shut down.
+ *
+ * WARNING: This method is experimental and not handled on every platform yet.
+ *
+ * The isolate is requested to terminate itself.
+ * The [priority] argument specifies when this must happen.
+ *
+ * The [priority] must be one of [IMMEDIATE], [BEFORE_NEXT_EVENT] or
+ * [AS_EVENT].
+ * The shutdown is performed at different times depending on the priority:
+ *
+ * * `IMMEDIATE`: The the isolate shuts down as soon as possible.
+ * Control messages are handled in order, so all previously sent control
+ * events from this isolate will all have been processed.
+ * The shutdown should happen no later than if sent with
+ * `BEFORE_NEXT_EVENT`.
+ * It may happen earlier if the system has a way to shut down cleanly
+ * at an earlier time, even during the execution of another event.
+ * * `BEFORE_NEXT_EVENT`: The shutdown is scheduled for the next time
+ * control returns to the event loop of the receiving isolate.
+ * If more than one such event are scheduled, they are executed in
+ * the order their control messages were received.
+ * * `AS_EVENT`: The shutdown does not happen until all prevously sent
+ * non-control messages from the current isolate to the receiving isolate
+ * have been processed.
+ * The kill operation effectively puts the shutdown into the normal event
+ * queue after previously sent messages, and it is affected by any control
+ * messages that affect normal events, including `pause`.
+ * This can be used to wait for a another event to be processed.
+ */
+ void kill([int priority = BEFORE_NEXT_EVENT]) {
+ controlPort.send(["kill", terminateCapability, priority]);
+ }
+
+ /**
* Request that the isolate send a response on the [responsePort].
*
* WARNING: This method is experimental and not handled on every platform yet.
@@ -239,24 +273,25 @@
* If the isolate is alive, it will eventually send a `null` response on
* the response port.
*
- * The [pingType] must be one of [PING_ALIVE], [PING_CONTROL] or [PING_EVENT].
+ * The [pingType] must be one of [IMMEDIATE], [BEFORE_NEXT_EVENT] or
+ * [AS_EVENT].
* The response is sent at different times depending on the ping type:
*
- * * `PING_ALIVE`: The the isolate responds as soon as possible.
- * The response should happen no later than if sent with `PING_CONTROL`.
- * It may be sent earlier if the system has a way to do so.
- * * `PING_CONTROL`: The response it not sent until all previously sent
- * control messages from the current isolate to the receiving isolate
- * have been processed. This can be used to wait for
- * previously sent control messages.
- * * `PING_EVENT`: The response is not sent until all prevously sent
+ * * `IMMEDIATE`: The the isolate responds as soon as it receives the
+ * control message.
+ * * `BEFORE_NEXT_EVENT`: The response is scheduled for the next time
+ * control returns to the event loop of the receiving isolate.
+ * If more than one such event are scheduled, they are executed in
+ * the order their control messages were received.
+ * * `AS_EVENT`: The response is not sent until all prevously sent
* non-control messages from the current isolate to the receiving isolate
* have been processed.
- * The ping effectively puts the resonse into the normal event queue after
- * previously sent messages.
+ * The ping effectively puts the response into the normal event queue
+ * after previously sent messages, and it is affected by any control
+ * messages that affect normal events, including `pause`.
* This can be used to wait for a another event to be processed.
*/
- void ping(SendPort responsePort, [int pingType = PING_ALIVE]) {
+ void ping(SendPort responsePort, [int pingType = IMMEDIATE]) {
var message = new List(3)
..[0] = "ping"
..[1] = responsePort
diff --git a/sdk/lib/profiler/profiler.dart b/sdk/lib/profiler/profiler.dart
new file mode 100644
index 0000000..56614dd
--- /dev/null
+++ b/sdk/lib/profiler/profiler.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library dart.profiler;
+
+/// A UserTag can be used to group samples in the Observatory profiler.
+abstract class UserTag {
+ /// The maximum number of UserTag instances that can be created by a program.
+ static const MAX_USER_TAGS = 64;
+
+ factory UserTag(String label) => new _FakeUserTag(label);
+
+ /// Label of [this].
+ String get label;
+
+ /// Make [this] the current tag for the isolate.
+ makeCurrent();
+}
+
+// This is a fake implementation of UserTag so that code can compile and run
+// in dart2js.
+class _FakeUserTag implements UserTag {
+ static List _instances = [];
+
+ _FakeUserTag.real(this.label);
+
+ factory _FakeUserTag(String label) {
+ // Canonicalize by name.
+ for (var tag in _instances) {
+ if (tag.label == label) {
+ return tag;
+ }
+ }
+ // Throw an exception if we've reached the maximum number of user tags.
+ if (_instances.length == UserTag.MAX_USER_TAGS) {
+ throw new UnsupportedError(
+ 'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+ }
+ // Create a new instance and add it to the instance list.
+ var instance = new _FakeUserTag.real(label);
+ _instances.add(instance);
+ return instance;
+ }
+
+ final String label;
+
+ makeCurrent() {
+ _currentTag = this;
+ }
+}
+
+var _currentTag = null;
+
+/// Returns the current [UserTag] for the isolate.
+UserTag getCurrentTag() {
+ return _currentTag;
+}
+
+/// Sets the current [UserTag] for the isolate to null. Returns current tag
+/// before clearing.
+UserTag clearCurrentTag() {
+ var old = _currentTag;
+ _currentTag = null;
+ return old;
+}
\ No newline at end of file
diff --git a/sdk/lib/profiler/profiler_sources.gypi b/sdk/lib/profiler/profiler_sources.gypi
new file mode 100644
index 0000000..910d798
--- /dev/null
+++ b/sdk/lib/profiler/profiler_sources.gypi
@@ -0,0 +1,10 @@
+# Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+{
+ 'sources': [
+ 'profiler.dart',
+ # The above file needs to be first if additional parts are added to the lib.
+ ],
+}
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index 34d3e3e..5f74033 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -15,9 +15,6 @@
# TBF: infinite look: class A {const A();final m = const A();}
Language/12_Expressions/01_Constants_A17_t03: fail
-# TBF: it seems that "12.15.3 Unqualied Invocation" was changed, so it may be not an error anymore to call unknown function from top-level
-Language/13_Statements/04_Local_Function_Declaration_A04_t02: Fail # co19-roll r641: Please triage this failure
-
# TBF: when we override "foo([x = 0]) {}" with "foo([x]) {}" we should report warning - different default value
Language/07_Classes/1_Instance_Methods_A04_t02: MissingStaticWarning
Language/07_Classes/4_Abstract_Instance_Members_A07_t04: MissingStaticWarning
@@ -28,18 +25,10 @@
# co19 issue #442, undefined name "Expect"
Language/15_Types/4_Interface_Types_A08_t03: fail, OK
-# co19 issue #455, undeclared identifier is static warning
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t10: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t12: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t15: fail, OK
-
# co19 issue #438, Static variables are initialized lazily, need not be constants
Language/12_Expressions/01_Constants_A16_t01: fail, OK
Language/12_Expressions/01_Constants_A16_t02: fail, OK
-# co19 issue #454 (wrongly closed)
-Language/12_Expressions/12_Instance_Creation/1_New_A01_t04: fail, OK
-
# co19 issue #543: invocation of a non-function
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
@@ -116,7 +105,6 @@
Language/05_Variables/05_Variables_A06_t01: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/05_Variables/05_Variables_A06_t02: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/05_Variables/05_Variables_A06_t03: MissingCompileTimeError # co19-roll r651: Please triage this failure
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t13: CompileTimeError # co19-roll r651: Please triage this failure
Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t17: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t18: MissingCompileTimeError # co19-roll r651: Please triage this failure
@@ -131,8 +119,8 @@
LibTest/typed_data/Float32x4/lessThan_A01_t01: Skip # co19 issue 656
LibTest/typed_data/Float32x4/lessThanOrEqual_A01_t01: Skip # co19 issue 656
-WebPlatformTest1/shadow-dom/*: CompileTimeError # co19 issue 677
-WebPlatformTest1/html-templates/*: CompileTimeError # co19 issue 677
+WebPlatformTest1/shadow-dom/*: StaticWarning # co19 issue 677
+WebPlatformTest1/html-templates/*: StaticWarning # co19 issue 677
WebPlatformTest1/custom-elements/*: Pass, StaticWarning # Issue 18095.
# co19 roll to r706: Please triage all these issues.
diff --git a/tests/co19/co19-analyzer2.status b/tests/co19/co19-analyzer2.status
index a0c96a0..a60ce5f 100644
--- a/tests/co19/co19-analyzer2.status
+++ b/tests/co19/co19-analyzer2.status
@@ -15,9 +15,6 @@
# TBF: infinite look: class A {const A();final m = const A();}
Language/12_Expressions/01_Constants_A17_t03: fail
-# TBF: it seems that "12.15.3 Unqualied Invocation" was changed, so it may be not an error anymore to call unknown function from top-level
-Language/13_Statements/04_Local_Function_Declaration_A04_t02: Fail # co19-roll r641: Please triage this failure
-
# TBF: when we override "foo([x = 0]) {}" with "foo([x]) {}" we should report warning - different default value
Language/07_Classes/1_Instance_Methods_A04_t02: MissingStaticWarning
Language/07_Classes/4_Abstract_Instance_Members_A07_t04: MissingStaticWarning
@@ -28,18 +25,10 @@
# co19 issue #442, undefined name "Expect"
Language/15_Types/4_Interface_Types_A08_t03: fail, OK
-# co19 issue #455, undeclared identifier is static warning
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t10: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t12: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t15: fail, OK
-
# co19 issue #438, Static variables are initialized lazily, need not be constants
Language/12_Expressions/01_Constants_A16_t01: fail, OK
Language/12_Expressions/01_Constants_A16_t02: fail, OK
-# co19 issue #454 (wrongly closed)
-Language/12_Expressions/12_Instance_Creation/1_New_A01_t04: fail, OK
-
# co19 issue #543: invocation of a non-function
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
@@ -116,7 +105,6 @@
Language/05_Variables/05_Variables_A06_t01: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/05_Variables/05_Variables_A06_t02: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/05_Variables/05_Variables_A06_t03: MissingCompileTimeError # co19-roll r651: Please triage this failure
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t13: CompileTimeError # co19-roll r651: Please triage this failure
Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t17: MissingCompileTimeError # co19-roll r651: Please triage this failure
Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t18: MissingCompileTimeError # co19-roll r651: Please triage this failure
@@ -131,8 +119,8 @@
LibTest/typed_data/Float32x4/lessThan_A01_t01: Skip # co19 issue 656
LibTest/typed_data/Float32x4/lessThanOrEqual_A01_t01: Skip # co19 issue 656
-WebPlatformTest1/shadow-dom/*: CompileTimeError # co19 issue 677
-WebPlatformTest1/html-templates/*: CompileTimeError # co19 issue 677
+WebPlatformTest1/shadow-dom/*: StaticWarning # co19 issue 677
+WebPlatformTest1/html-templates/*: StaticWarning # co19 issue 677
WebPlatformTest1/custom-elements/*: Pass, StaticWarning # Issue 18095.
# co19 roll to r706: Please triage all these issues.
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 3e8938e..c1aef28 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -907,53 +907,47 @@
LibTest/typed_data/Uint8List/toList_A01_t01: fail, timeout # co19-roll r559: Please triage this failure
[ $compiler == dart2js && ($runtime == d8 || $runtime == jsshell) ]
-LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Future/Future.delayed_A03_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/first_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/first_A02_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream.periodic_A02_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/cancel_A01_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer_A01_t01: fail # Issue 7728, timer not supported in d8/jsshell
-
LibTest/typed_data/Float32x4List/Float32x4List.view_A06_t01: fail # co19-roll r587: Please triage this failure
Language/12_Expressions/00_Object_Identity/1_Object_Identity_A05_t02: RuntimeError # co19-roll r607: Please triage this failure
Language/12_Expressions/17_Getter_Invocation_A07_t02: RuntimeError # co19-roll r607: Please triage this failure
-LibTest/isolate/ReceivePort/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/isActive_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Future/wait_A01_t07: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/isActive_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Zone/createPeriodicTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Zone/createTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/start_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/typed_data/Int32x4/operator_OR_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
+LibTest/typed_data/Int32x4/operator_OR_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+
+[ $compiler == dart2js && $runtime == jsshell ]
+LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Future/Future.delayed_A03_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/first_A01_t01: fail # co19-roll r546: Please triage this failure
+LibTest/async/Stream/first_A02_t02: fail # co19-roll r546: Please triage this failure
+LibTest/async/Stream/Stream.periodic_A02_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/cancel_A01_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer_A01_t01: fail # Issue 7728, timer not supported in jsshell
+
+LibTest/isolate/ReceivePort/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/isActive_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Future/wait_A01_t07: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/isActive_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Zone/createPeriodicTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Zone/createTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/start_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
LibTest/core/Stopwatch/start_A01_t01: RuntimeError # Please triage this failure
LibTest/core/Stopwatch/start_A01_t02: RuntimeError # Please triage this failure
LibTest/core/Stopwatch/elapsedTicks_A01_t01: RuntimeError # Please triage this failure
-LibTest/async/Stream/Stream.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
+LibTest/async/Stream/Stream.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # Issue 7728, timer not supported in jsshell
[ $compiler == dart2js && $runtime == d8 ]
-LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
LibTest/math/sin_A01_t02: RuntimeError # V8 issue 3006, https://code.google.com/p/v8/issues/detail?id=3006
[ $compiler == dart2js && $minified ]
diff --git a/tests/co19/co19-dartium.status b/tests/co19/co19-dartium.status
index ae27ff4..ba51748 100644
--- a/tests/co19/co19-dartium.status
+++ b/tests/co19/co19-dartium.status
@@ -202,7 +202,7 @@
WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-004_t01: RuntimeError # Issue 17758. Please triage this failure.
WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-004_t02: RuntimeError # Issue 17758. Please triage this failure.
WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/elements-001_t01: RuntimeError # Issue 17758. Please triage this failure.
-WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001_t01: RuntimeError # Issue 17758. Please triage this failure.
+WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001_t01: RuntimeError, Timeout # Issue 17758. Please triage this failure.
WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-content-html-element/test-006_t01: RuntimeError # Issue 17758. Please triage this failure.
WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-001_t01: RuntimeError # Issue 17758. Please triage this failure.
WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-005_t01: RuntimeError # Issue 17758. Please triage this failure.
@@ -233,9 +233,12 @@
LibTest/async/Timer/Timer_A01_t01: RuntimeError, Pass # Issue 16475
+[ $compiler == none && $mode == debug && ($runtime == dartium || $runtime == ContentShellOnAndroid ) && $checked ]
+LibTest/collection/ListMixin/ListMixin_class_A01_t01: Pass, Timeout # co19-roll r706: Please triage this failure
+LibTest/collection/ListBase/ListBase_class_A01_t01: Pass, Timeout # co19-roll r706: Please triage this failure
+
[ $compiler == none && ($runtime == dartium || $runtime == ContentShellOnAndroid ) && $checked ]
LibTest/core/List/removeAt_A02_t01: Fail # co19-roll r641: Please triage this failure
-
# New Dartium checked failures on new co19 browser tests in co19 revision 706.
LayoutTests/fast/html/article-element_t01: RuntimeError # Issue 17758. Please triage this failure.
LayoutTests/fast/html/aside-element_t01: RuntimeError # Issue 17758. Please triage this failure.
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index a325b82..15da15f 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -51,7 +51,6 @@
[ $runtime == vm ]
# These flaky tests also fail with dart2dart.
-LibTest/math/Rectangle/boundingBox_A01_t01: Pass, RuntimeError # co19-roll r607: Please triage this failure
LibTest/math/MutableRectangle/MutableRectangle.fromPoints_A01_t01: Pass, RuntimeError # co19-roll r607: Please triage this failure
[ $compiler == none && $runtime == vm && $checked ]
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index 770e62f..8af24c6 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -2,7 +2,6 @@
# 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.
-identity_test: Fail # Issue 6638
boolified_operator_test: Fail # Issue 8001
# simple_function_subtype_test is temporarily(?) disabled due to new method for
diff --git a/tests/compiler/dart2js/dart_backend_test.dart b/tests/compiler/dart2js/dart_backend_test.dart
index 48ba841..23fa7ac 100644
--- a/tests/compiler/dart2js/dart_backend_test.dart
+++ b/tests/compiler/dart2js/dart_backend_test.dart
@@ -183,8 +183,10 @@
}
testVariableDefinitions() {
- testDart2Dart('main(){var x,y;final String s=null;}');
- testDart2Dart('main(){final int x=0,y=0;final String s=null;}');
+ testDart2Dart('main(){var x,y;final String s=null;}',
+ continuation: (String s) { Expect.equals('main(){}', s); });
+ testDart2Dart('main(){final int x=0,y=0;final String s=null;}',
+ continuation: (String s) { Expect.equals('main(){}', s); });
testDart2Dart('foo(f,g){}main(){foo(1,2);}');
testDart2Dart('foo(f(arg)){}main(){foo(main);}');
// A couple of static/finals inside a class.
@@ -388,7 +390,7 @@
var expectedResult =
'topfoo(){}'
'class A{foo(){}}'
- 'A_topfoo(){var x=5;}'
+ 'A_topfoo(){}'
'class A_A{num foo(){}A_A.fromFoo(){}A myliba;List<A_A> mylist;}'
'A getA(){}'
'main(){var a=new A();a.foo();var b=new A_A.fromFoo();b.foo();'
diff --git a/tests/compiler/dart2js/identity_test.dart b/tests/compiler/dart2js/identity_test.dart
index 50c768b5..eec4136 100644
--- a/tests/compiler/dart2js/identity_test.dart
+++ b/tests/compiler/dart2js/identity_test.dart
@@ -20,7 +20,7 @@
// Check that no boolify code is generated.
RegExp regexp = new RegExp("=== true");
Iterator matches = regexp.allMatches(generated).iterator;
- Expect.isFalse(matches.hasNext);
+ Expect.isFalse(matches.moveNext());
regexp = new RegExp("===");
matches = regexp.allMatches(generated).iterator;
diff --git a/tests/compiler/dart2js/js_parser_test.dart b/tests/compiler/dart2js/js_parser_test.dart
index 5109ccf..db6f43a 100644
--- a/tests/compiler/dart2js/js_parser_test.dart
+++ b/tests/compiler/dart2js/js_parser_test.dart
@@ -96,7 +96,7 @@
// Empty object literal.
testExpression('foo({}, {})');
// *Can't handle non-empty object literals
- testError('foo({meaning: 42})', 'Expected RBRACE');
+ testExpression('foo({meaning: 42})');
// Literals.
testExpression('x(false, true, null)');
// *We should really throw here.
diff --git a/tests/compiler/dart2js/memory_compiler.dart b/tests/compiler/dart2js/memory_compiler.dart
index 392f51b..a279cad 100644
--- a/tests/compiler/dart2js/memory_compiler.dart
+++ b/tests/compiler/dart2js/memory_compiler.dart
@@ -165,7 +165,7 @@
{});
if (cachedCompiler != null) {
compiler.coreLibrary = cachedCompiler.libraries['dart:core'];
- compiler.types = cachedCompiler.types;
+ compiler.types = cachedCompiler.types.copy(compiler);
cachedCompiler.libraries.forEach((String uri, library) {
if (library.isPlatformLibrary) {
compiler.libraries[uri] = library;
@@ -193,6 +193,27 @@
treeElements;
}
});
+
+ // One potential problem that can occur when reusing elements is that there
+ // is a stale reference to an old compiler object. By nulling out the old
+ // compiler's fields, such stale references are easier to identify.
+ cachedCompiler.scanner = null;
+ cachedCompiler.dietParser = null;
+ cachedCompiler.parser = null;
+ cachedCompiler.patchParser = null;
+ cachedCompiler.libraryLoader = null;
+ cachedCompiler.validator = null;
+ cachedCompiler.resolver = null;
+ cachedCompiler.closureToClassMapper = null;
+ cachedCompiler.checker = null;
+ cachedCompiler.irBuilder = null;
+ cachedCompiler.typesTask = null;
+ cachedCompiler.backend = null;
+ cachedCompiler.enqueuer = null;
+ cachedCompiler.deferredLoadTask = null;
+ cachedCompiler.mirrorUsageAnalyzerTask = null;
+ cachedCompiler.dumpInfoTask = null;
+ cachedCompiler.buildId = null;
}
return compiler;
}
diff --git a/tests/compiler/dart2js/message_kind_test.dart b/tests/compiler/dart2js/message_kind_test.dart
index b2f1e46..b3cca95 100644
--- a/tests/compiler/dart2js/message_kind_test.dart
+++ b/tests/compiler/dart2js/message_kind_test.dart
@@ -48,18 +48,10 @@
asyncTest(() => Future.forEach(examples, (String name) {
print("Checking '$name'.");
Stopwatch sw = new Stopwatch()..start();
- bool useCachedCompiler = true;
- if (name == 'MISSING_LIBRARY_NAME') {
- // TODO(johnniwinther): Found out why we cannot use the cached compiler
- // for this message kind.
- cachedCompiler = null;
- }
- return check(kinds[name], cachedCompiler).
- then((var compiler) {
- cachedCompiler = compiler;
- sw.stop();
- print("Checked '$name' in ${sw.elapsedMilliseconds}ms.");
- });
- }
- ));
+ return check(kinds[name], cachedCompiler).then((var compiler) {
+ cachedCompiler = compiler;
+ sw.stop();
+ print("Checked '$name' in ${sw.elapsedMilliseconds}ms.");
+ });
+ }));
}
diff --git a/tests/compiler/dart2js/source_map_validator_helper.dart b/tests/compiler/dart2js/source_map_validator_helper.dart
index 934a63c..988c60c 100644
--- a/tests/compiler/dart2js/source_map_validator_helper.dart
+++ b/tests/compiler/dart2js/source_map_validator_helper.dart
@@ -14,9 +14,47 @@
Uri mapUri = getMapUri(targetUri);
SingleMapping sourceMap = getSourceMap(mapUri);
checkFileReferences(targetUri, mapUri, sourceMap);
+ checkIndexReferences(targetUri, mapUri, sourceMap);
checkRedundancy(sourceMap);
}
+checkIndexReferences(Uri targetUri, Uri mapUri, SingleMapping sourceMap) {
+ List<String> target =
+ new File.fromUri(targetUri).readAsStringSync().split('\n');
+ int urlsLength = sourceMap.urls.length;
+ List<List<String>> sources = new List(urlsLength);
+ print('Reading sources');
+ for (int i = 0; i < urlsLength; i++) {
+ sources[i] = new File.fromUri(mapUri.resolve(sourceMap.urls[i])).
+ readAsStringSync().split('\n');
+ }
+
+ sourceMap.lines.forEach((TargetLineEntry line) {
+ Expect.isTrue(line.line >= 0);
+ Expect.isTrue(line.line < target.length);
+ for (TargetEntry entry in line.entries) {
+ int urlIndex = entry.sourceUrlId;
+
+ // TODO(zarah): Entry columns sometimes point one or more characters too
+ // far. Incomment this check when this is fixed.
+ //
+ // Expect.isTrue(entry.column < target[line.line].length);
+ Expect.isTrue(entry.column >= 0);
+ Expect.isTrue(urlIndex == null ||
+ (urlIndex >= 0 && urlIndex < urlsLength));
+ Expect.isTrue(entry.sourceLine == null ||
+ (entry.sourceLine >= 0 &&
+ entry.sourceLine < sources[urlIndex].length));
+ Expect.isTrue(entry.sourceColumn == null ||
+ (entry.sourceColumn >= 0 &&
+ entry.sourceColumn < sources[urlIndex][entry.sourceLine].length));
+ Expect.isTrue(entry.sourceNameId == null ||
+ (entry.sourceNameId >= 0 &&
+ entry.sourceNameId < sourceMap.names.length));
+ }
+ });
+}
+
checkFileReferences(Uri targetUri, Uri mapUri, SingleMapping sourceMap) {
Expect.equals(targetUri, mapUri.resolve(sourceMap.targetUrl));
print('Checking sources');
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index 409e87d..bd7d93f 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -47,7 +47,7 @@
[ $compiler == dart2js && ($runtime == drt || $runtime == ff || $runtime == safari || $runtime == chrome || $runtime == chromeOnAndroid) ]
isolate2_test/01: Fail # Issue 14458.
-[ $jscl ]
+[ $runtime == jsshell ]
timer_test: Fail # Issue 7728.
[ $runtime == none ]
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 4b2c4fa..04cd3d3 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -42,10 +42,6 @@
string_from_environment3_test/03: Fail
string_from_environment3_test/04: Fail
-[ $compiler == dartanalyzer ]
-string_from_environment_test: Crash # http://dartbug.com/17234
-int_from_environment2_test: Crash # http://dartbug.com/17234
-
[ $compiler == none ]
unicode_test: Fail # Bug 6706
compare_to2_test: Fail # Bug 4018
@@ -109,10 +105,10 @@
[ $compiler == dart2js && ($runtime == firefox || $runtime == safari || $runtime == chrome || $runtime == drt) ]
-[ $compiler == dart2js && ($runtime == d8 || $runtime == chrome || $runtime == drt || $runtime == safari) ]
+[ $compiler == dart2js && ($runtime == chrome || $runtime == drt || $runtime == safari) ]
string_trimlr_test/none: Fail # Bug in v8. Fixed in v8 r19222, 2014-02-10.
-[ $compiler == dart2js && ($runtime == d8 || $runtime == drt) ]
+[ $compiler == dart2js && ( $runtime == drt) ]
string_case_test/02: Fail, OK # Bug in our version of V8.
[ $compiler == dart2js && ($runtime == ie9 || $runtime == ie10) ]
diff --git a/tests/html/custom/attribute_changed_callback_test.html b/tests/html/custom/attribute_changed_callback_test.html
index 8f54393..cf1042b 100644
--- a/tests/html/custom/attribute_changed_callback_test.html
+++ b/tests/html/custom/attribute_changed_callback_test.html
@@ -17,8 +17,6 @@
<h1> Running attribute_changed_callback_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/constructor_calls_created_synchronously_test.html b/tests/html/custom/constructor_calls_created_synchronously_test.html
index 531162e..3ba2ba8 100644
--- a/tests/html/custom/constructor_calls_created_synchronously_test.html
+++ b/tests/html/custom/constructor_calls_created_synchronously_test.html
@@ -17,8 +17,6 @@
<h1> Running entered_left_view_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/created_callback_test.html b/tests/html/custom/created_callback_test.html
index 424db1e..e68ef23 100644
--- a/tests/html/custom/created_callback_test.html
+++ b/tests/html/custom/created_callback_test.html
@@ -17,8 +17,6 @@
<h1> Running created_callback_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/document_register_basic_test.html b/tests/html/custom/document_register_basic_test.html
index f83837e..f87df4b 100644
--- a/tests/html/custom/document_register_basic_test.html
+++ b/tests/html/custom/document_register_basic_test.html
@@ -17,8 +17,6 @@
<h1> Running document_register_basic_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/document_register_type_extensions_test.html b/tests/html/custom/document_register_type_extensions_test.html
index 1a0fe01..92cdd27 100644
--- a/tests/html/custom/document_register_type_extensions_test.html
+++ b/tests/html/custom/document_register_type_extensions_test.html
@@ -17,8 +17,6 @@
<h1> Running document_register_type_extensions_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/element_upgrade_test.dart b/tests/html/custom/element_upgrade_test.dart
new file mode 100644
index 0000000..244f21b
--- /dev/null
+++ b/tests/html/custom/element_upgrade_test.dart
@@ -0,0 +1,132 @@
+// 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.
+
+library register_element_proxy_test;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:js' as js;
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import '../utils.dart';
+
+class FooElement extends HtmlElement {
+ static final tag = 'x-foo';
+
+ final int initializedField = 666;
+ js.JsObject _proxy;
+
+ factory FooElement() => new Element.tag(tag);
+ FooElement.created() : super.created() {
+ _proxy = new js.JsObject.fromBrowserObject(this);
+ }
+
+ String doSomething() => _proxy.callMethod('doSomething');
+
+ bool get fooCreated => _proxy['fooCreated'];
+}
+
+main() {
+ useHtmlConfiguration();
+
+ var registered = false;
+ var upgrader;
+ setUp(() => customElementsReady.then((_) {
+ if (!registered) {
+ registered = true;
+ upgrader = document.createElementUpgrader(FooElement);
+ js.context['upgradeListener'] = (e) {
+ upgrader.upgrade(e);
+ };
+
+ document.register('custom-element', CustomElement);
+ }
+ }));
+
+ test('created gets proxied', () {
+ var element = document.createElement(FooElement.tag);
+ expect(element is FooElement, isTrue);
+ expect(element.initializedField, 666);
+ expect(element.text, 'constructed');
+
+ js.context.callMethod('validateIsFoo', [element]);
+
+ expect(element.doSomething(), 'didSomething');
+ expect(element.fooCreated, true);
+ });
+
+ test('dart constructor works', () {
+ var element = new FooElement();
+ expect(element is FooElement, isTrue);
+ expect(element.text, 'constructed');
+
+ js.context.callMethod('validateIsFoo', [element]);
+
+ expect(element.doSomething(), 'didSomething');
+ });
+
+ test('cannot create upgrader for interfaces', () {
+ expect(() {
+ document.createElementUpgrader(HtmlElementInterface);
+ }, throws);
+ });
+
+ test('cannot upgrade interfaces', () {
+ expect(() {
+ upgrader.upgrade(new HtmlElementInterface());
+ }, throws);
+ });
+
+ test('cannot upgrade more than once', () {
+ var fooElement = new FooElement();
+ expect(() {
+ upgrader.upgrade(fooElement);
+ }, throws);
+ });
+
+ test('cannot upgrade non-matching elements', () {
+ expect(() {
+ upgrader.upgrade(new DivElement());
+ }, throws);
+ });
+
+ test('cannot upgrade custom elements', () {
+ var custom = new CustomElement();
+ expect(() {
+ upgrader.upgrade(custom);
+ }, throws);
+ });
+
+ test('can upgrade with extendsTag', () {
+ var upgrader =
+ document.createElementUpgrader(CustomDiv, extendsTag: 'div');
+ var div = new DivElement();
+ var customDiv = upgrader.upgrade(div);
+ expect(customDiv is CustomDiv, isTrue);
+
+ var htmlElement = document.createElement('not-registered');
+ expect(() {
+ upgrader.upgrade(htmlElement);
+ }, throws);
+ });
+
+ test('cannot create upgrader for built-in types', () {
+ expect(() {
+ document.createElementUpgrader(HtmlElement);
+ }, throws);
+ });
+}
+
+class HtmlElementInterface implements HtmlElement {
+ HtmlElementInterface.created();
+}
+
+class CustomDiv extends DivElement {
+ CustomDiv.created() : super.created();
+}
+
+class CustomElement extends HtmlElement {
+ factory CustomElement() => document.createElement('custom-element');
+ CustomElement.created() : super.created();
+}
diff --git a/tests/html/custom/element_upgrade_test.html b/tests/html/custom/element_upgrade_test.html
new file mode 100644
index 0000000..498a47b
--- /dev/null
+++ b/tests/html/custom/element_upgrade_test.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta name="dart.unittest" content="full-stack-traces">
+<title> element_upgrade_test </title>
+<style>
+ .unittest-table { font-family:monospace; border:1px; }
+ .unittest-pass { background: #6b3;}
+ .unittest-fail { background: #d55;}
+ .unittest-error { background: #a11;}
+</style>
+<script src="/packages/web_components/platform.concat.js"></script>
+<script src="/packages/web_components/dart_support.js"></script>
+
+<body>
+ <h1> Running element_upgrade_test </h1>
+
+<script>
+var Foo = function() {};
+Foo.prototype = Object.create(HTMLElement.prototype);
+Foo.prototype.createdCallback = function() {
+ this.fooCreated = true;
+ this.textContent = 'constructed';
+
+ // Tell the Dart side that this was created.
+ // For testing purposes, for real code this would use a different mechanism.
+ window.upgradeListener(this);
+};
+
+Foo.prototype.doSomething = function() {
+ this.textContent = 'didSomething';
+ return 'didSomething';
+};
+
+Foo = document.registerElement('x-foo', Foo);
+
+function validateIsFoo(element) {
+ if (!(element instanceof Foo)) {
+ throw Error('Element is not a Foo');
+ }
+
+ if (!element.fooCreated) {
+ throw Error('Expected fooCreated to be set');
+ }
+}
+</script>
+
+ <script type="text/javascript"
+ src="/root_dart/tools/testing/dart/test_controller.js"></script>
+ <script type="text/javascript"
+ src="/packages/browser/interop.js"></script>
+ %TEST_SCRIPTS%
+</body>
diff --git a/tests/html/custom/entered_left_view_test.html b/tests/html/custom/entered_left_view_test.html
index 531162e..3ba2ba8 100644
--- a/tests/html/custom/entered_left_view_test.html
+++ b/tests/html/custom/entered_left_view_test.html
@@ -17,8 +17,6 @@
<h1> Running entered_left_view_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/js_custom_test.html b/tests/html/custom/js_custom_test.html
index c477d92..92a36d1 100644
--- a/tests/html/custom/js_custom_test.html
+++ b/tests/html/custom/js_custom_test.html
@@ -17,8 +17,6 @@
<h1> Running js_custom_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/mirrors_test.html b/tests/html/custom/mirrors_test.html
index 0211aea..f28c4fa 100644
--- a/tests/html/custom/mirrors_test.html
+++ b/tests/html/custom/mirrors_test.html
@@ -17,8 +17,6 @@
<h1> Running mirrors_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript"
- src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/custom/template_wrappers_test.html b/tests/html/custom/template_wrappers_test.html
index 3af84bb..1b22431 100644
--- a/tests/html/custom/template_wrappers_test.html
+++ b/tests/html/custom/template_wrappers_test.html
@@ -12,7 +12,6 @@
</style>
<script src="/packages/web_components/platform.concat.js"></script>
<script src="/packages/web_components/dart_support.js"></script>
- <script src="/packages/browser/interop.js"></script>
</head>
<body>
<h1> Running template_wrappers_test </h1>
diff --git a/tests/html/custom_elements_test.html b/tests/html/custom_elements_test.html
index 8c2cd1e..7a3eb2c 100644
--- a/tests/html/custom_elements_test.html
+++ b/tests/html/custom_elements_test.html
@@ -17,7 +17,6 @@
<h1> Running custom_elements_test </h1>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
- <script type="text/javascript" src="/packages/browser/interop.js"></script>
%TEST_SCRIPTS%
</body>
</html>
diff --git a/tests/html/html.status b/tests/html/html.status
index 2f7368c..ab76af4 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -11,6 +11,7 @@
[ $compiler == dart2js && $csp ]
custom/js_custom_test: Fail # Issue 14643
+custom/element_upgrade_test: Fail # Issue 17298
[ $compiler == dart2js && $browser ]
custom/created_callback_test: Fail # Support for created constructor.
@@ -35,6 +36,7 @@
async_test: Fail # Background timers not implemented.
keyboard_event_test: Fail # Issue 13902
isolates_test: Fail # Issue 13921
+custom/element_upgrade_test: Skip # Issue 17298
[ $compiler == none && ($runtime == drt || $runtime == dartium || $runtime == ContentShellInAndroid) && $mode == debug ]
websocket_test/websocket: Skip # Issue 17666
@@ -194,6 +196,7 @@
[ $runtime == ie9 || $runtime == ie10 ]
custom/document_register_type_extensions_test/construction: Fail # Issue 13193
+custom/element_upgrade_test: Fail # Issue 18247
worker_api_test: Fail # IE does not support URL.createObjectURL in web workers.
[ $runtime == ie9 ]
@@ -440,6 +443,7 @@
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
audiocontext_test: StaticWarning
custom/document_register_basic_test: StaticWarning
+custom/element_upgrade_test: StaticWarning
datalistelement_test: StaticWarning
documentfragment_test: StaticWarning
element_add_test: StaticWarning
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index eb5f276..f1f41fa 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -22,9 +22,15 @@
ondone_test: Skip # Not implemented yet, hangs.
ping_test: Fail # Not implemented yet
ping_pause_test: Fail # Not implemented yet
+kill_test: Skip # Not implemented yet, hangs.
+kill2_test: Skip # Not implemented yet, hangs.
+kill3_test: Skip # Not implemented yet, hangs.
+kill_self_test: Skip # Not implemented yet, hangs.
[ $compiler == dart2js && $jscl ]
browser/*: SkipByDesign # Browser specific tests
+
+[ $compiler == dart2js && $runtime == jsshell ]
pause_test: Fail # non-zero timer not supported.
[ $compiler == dart2js ]
diff --git a/tests/isolate/kill2_test.dart b/tests/isolate/kill2_test.dart
new file mode 100644
index 0000000..911cd69
--- /dev/null
+++ b/tests/isolate/kill2_test.dart
@@ -0,0 +1,44 @@
+// 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.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+ RawReceivePort port = new RawReceivePort();
+ port.handler = (v) {
+ replyPort.send(v);
+ if (v == 0) port.close();
+ };
+ replyPort.send(port.sendPort);
+}
+
+void main() {
+ asyncStart();
+ var completer = new Completer(); // Completed by first reply from isolate.
+ RawReceivePort reply = new RawReceivePort(completer.complete);
+ Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+ List result = [];
+ completer.future.then((echoPort) {
+ reply.handler = (v) {
+ result.add(v);
+ if (v == 2) {
+ isolate.kill(Isolate.BEFORE_NEXT_EVENT);
+ }
+ echoPort.send(v - 1);
+ };
+ RawReceivePort exitSignal;
+ exitSignal = new RawReceivePort((_) {
+ Expect.listEquals([4, 3, 2], result);
+ exitSignal.close();
+ reply.close();
+ asyncEnd();
+ });
+ isolate.addOnExitListener(exitSignal.sendPort);
+ echoPort.send(4);
+ });
+ });
+}
diff --git a/tests/isolate/kill3_test.dart b/tests/isolate/kill3_test.dart
new file mode 100644
index 0000000..006f7ae
--- /dev/null
+++ b/tests/isolate/kill3_test.dart
@@ -0,0 +1,44 @@
+// 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.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+ RawReceivePort port = new RawReceivePort();
+ port.handler = (v) {
+ replyPort.send(v);
+ if (v == 0) port.close();
+ };
+ replyPort.send(port.sendPort);
+}
+
+void main() {
+ asyncStart();
+ var completer = new Completer(); // Completed by first reply from isolate.
+ RawReceivePort reply = new RawReceivePort(completer.complete);
+ Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+ List result = [];
+ completer.future.then((echoPort) {
+ reply.handler = (v) {
+ result.add(v);
+ echoPort.send(v - 1);
+ if (v == 2) {
+ isolate.kill(Isolate.AS_EVENT);
+ }
+ };
+ RawReceivePort exitSignal;
+ exitSignal = new RawReceivePort((_) {
+ Expect.listEquals([4, 3, 2, 1], result);
+ exitSignal.close();
+ reply.close();
+ asyncEnd();
+ });
+ isolate.addOnExitListener(exitSignal.sendPort);
+ echoPort.send(4);
+ });
+ });
+}
diff --git a/tests/isolate/kill_self_test.dart b/tests/isolate/kill_self_test.dart
new file mode 100644
index 0000000..932dca6
--- /dev/null
+++ b/tests/isolate/kill_self_test.dart
@@ -0,0 +1,43 @@
+// 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.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+ RawReceivePort port = new RawReceivePort();
+ bool firstEvent = true;
+ port.handler = (v) {
+ if (!firstEvent) {
+ throw "Survived suicide";
+ }
+ var controlPort = v[0];
+ var killCapability = v[1];
+ firstEvent = false;
+ var isolate = new Isolate(controlPort,
+ terminateCapability: killCapability);
+ isolate.kill(Isolate.IMMEDIATE);
+ };
+ replyPort.send(port.sendPort);
+}
+
+void main() {
+ asyncStart();
+ var completer = new Completer(); // Completed by first reply from isolate.
+ RawReceivePort reply = new RawReceivePort(completer.complete);
+ Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+ completer.future.then((isolatePort) {
+ RawReceivePort exitSignal;
+ exitSignal = new RawReceivePort((_) {
+ exitSignal.close();
+ asyncEnd();
+ });
+ isolate.addOnExitListener(exitSignal.sendPort);
+ isolatePort.send([isolate.controlPort, isolate.terminateCapability]);
+ reply.close();
+ });
+ });
+}
diff --git a/tests/isolate/kill_test.dart b/tests/isolate/kill_test.dart
new file mode 100644
index 0000000..d0da5ab
--- /dev/null
+++ b/tests/isolate/kill_test.dart
@@ -0,0 +1,44 @@
+// 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.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+ RawReceivePort port = new RawReceivePort();
+ port.handler = (v) {
+ replyPort.send(v);
+ if (v == 0) port.close();
+ };
+ replyPort.send(port.sendPort);
+}
+
+void main() {
+ asyncStart();
+ var completer = new Completer(); // Completed by first reply from isolate.
+ RawReceivePort reply = new RawReceivePort(completer.complete);
+ Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+ completer.future.then((SendPort echoPort) {
+ List result = [];
+ reply.handler = (v) {
+ result.add(v);
+ if (v == 2) {
+ isolate.kill(Isolate.IMMEDIATE);
+ }
+ echoPort.send(v - 1);
+ };
+ RawReceivePort exitSignal;
+ exitSignal = new RawReceivePort((_) {
+ Expect.listEquals([4, 3, 2], result);
+ exitSignal.close();
+ reply.close();
+ asyncEnd();
+ });
+ isolate.addOnExitListener(exitSignal.sendPort);
+ echoPort.send(4);
+ });
+ });
+}
diff --git a/tests/isolate/ping_pause_test.dart b/tests/isolate/ping_pause_test.dart
index 11fa951..ec47f7a 100644
--- a/tests/isolate/ping_pause_test.dart
+++ b/tests/isolate/ping_pause_test.dart
@@ -41,7 +41,7 @@
isolate.resume(resume);
pingPort.close();
};
- isolate.ping(pingPort.sendPort, Isolate.PING_CONTROL);
+ isolate.ping(pingPort.sendPort, Isolate.BEFORE_NEXT_EVENT);
echoPort.send(2);
echoPort.send(1);
});
diff --git a/tests/isolate/ping_test.dart b/tests/isolate/ping_test.dart
index e208189..48b1ec5 100644
--- a/tests/isolate/ping_test.dart
+++ b/tests/isolate/ping_test.dart
@@ -27,19 +27,22 @@
result.add(v);
if (v == 0) {
Expect.listEquals(["alive", "control", "event"],
- result.where((x) => x is String).toList());
+ result.where((x) => x is String).toList(),
+ "control events");
Expect.listEquals([4, 3, 2, 1, 0],
- result.where((x) => x is int).toList());
- Expect.isTrue(result.indexOf("alive") < result.indexOf(3));
- Expect.isTrue(result.indexOf("control") < result.indexOf(2));
+ result.where((x) => x is int).toList(),
+ "data events");
+ Expect.isTrue(result.indexOf("alive") < result.indexOf(3),
+ "alive index < 3");
+ Expect.isTrue(result.indexOf("control") < result.indexOf(2),
+ "control index < 2");
int eventIndex = result.indexOf("event");
- Expect.isTrue(eventIndex > result.indexOf(2));
- Expect.isTrue(eventIndex < result.indexOf(1));
+ Expect.isTrue(eventIndex > result.indexOf(2), "event index > 2");
+ Expect.isTrue(eventIndex < result.indexOf(1), "event index < 1");
reply.close();
asyncEnd();
}
};
- echoPort.send(4);
SendPort createPingPort(message) {
var pingPort = new RawReceivePort();
pingPort.handler = (_) {
@@ -48,11 +51,12 @@
};
return pingPort.sendPort;
}
- isolate.ping(createPingPort("alive"), Isolate.PING_ALIVE);
+ echoPort.send(4);
+ isolate.ping(createPingPort("alive"), Isolate.IMMEDIATE);
echoPort.send(3);
- isolate.ping(createPingPort("control"), Isolate.PING_CONTROL);
+ isolate.ping(createPingPort("control"), Isolate.BEFORE_NEXT_EVENT);
echoPort.send(2);
- isolate.ping(createPingPort("event"), Isolate.PING_EVENT);
+ isolate.ping(createPingPort("event"), Isolate.AS_EVENT);
echoPort.send(1);
echoPort.send(0);
});
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index 67d84da..1666a95 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -9,13 +9,6 @@
# Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask)
built_in_identifier_prefix_test: CompileTimeError # Issue 12694
-# Issue 15315
-class_literal_test/01: CompileTimeError
-class_literal_test/03: CompileTimeError
-class_literal_test/07: CompileTimeError
-constructor_call_as_function_test/01: fail
-call_type_literal_test/01: fail
-
# TBF: we should check conflicts not only for methods, but for accessors too
override_field_test/03: fail
method_override7_test/03: Fail # Issue 11496
@@ -29,8 +22,6 @@
call_closurization_test: StaticWarning # Issue 17476
-function_literals2_test: Fail # Issue 18137
-
# Please add new failing tests before this line.
# Section below is for invalid tests.
#
@@ -122,7 +113,6 @@
# test issue 12381, It is compile-time error to invoke not existing function
issue11724_test: fail # Issue 12381
-call_nonexistent_static_test/08: fail # Issue 12381
# test issue 12539, rules for finals were loosened, contradiction in spec was fixed
const_syntax_test/09: fail # Issue 12539
@@ -161,23 +151,6 @@
# test issue 14736, It is a static warning if a class C declares an instance method named n and has a setter named n=.
setter4_test: StaticWarning
-# test issue 15060
-least_upper_bound_test/23: StaticWarning # Issue 15060
-least_upper_bound_test/24: StaticWarning # Issue 15060
-least_upper_bound_test/25: MissingStaticWarning # Issue 15060
-least_upper_bound_test/26: MissingStaticWarning # Issue 15060
-least_upper_bound_test/29: StaticWarning # Issue 15060
-least_upper_bound_test/30: StaticWarning # Issue 15060
-least_upper_bound_test/31: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/02: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/04: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/05: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/06: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/08: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/10: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/11: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/12: MissingStaticWarning # Issue 15060
-
# test issue 15467
proxy_test/05: StaticWarning # Issue 15467
proxy_test/06: StaticWarning # Issue 15467
@@ -270,7 +243,7 @@
getter_declaration_negative_test: CompileTimeError
getter_no_setter2_test/01: StaticWarning
getter_no_setter_test/01: StaticWarning
-illegal_invocation_test/01: CompileTimeError
+illegal_invocation_test/01: StaticWarning
implicit_this_test/02: StaticWarning
implied_interface_test: StaticWarning
import_combinators_test: StaticWarning
@@ -302,9 +275,9 @@
label8_negative_test: CompileTimeError
label_test: StaticWarning
library_ambiguous_test/00: StaticWarning
-library_ambiguous_test/01: CompileTimeError
-library_ambiguous_test/02: CompileTimeError
-library_ambiguous_test/03: CompileTimeError
+library_ambiguous_test/01: StaticWarning
+library_ambiguous_test/02: StaticWarning
+library_ambiguous_test/03: StaticWarning
library_negative_test: CompileTimeError
list_literal2_negative_test: CompileTimeError
list_literal4_test: StaticWarning
@@ -410,7 +383,7 @@
script2_negative_test: CompileTimeError
setter_declaration2_negative_test: CompileTimeError
setter_declaration_negative_test: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
+setter_no_getter_call_test/01: StaticWarning
source_self_negative_test: CompileTimeError
static_initializer_type_error_test: StaticWarning
string_escape4_negative_test: CompileTimeError
@@ -459,7 +432,7 @@
unary_plus_negative_test: CompileTimeError
unbound_getter_test: StaticWarning
unhandled_exception_negative_test: CompileTimeError
-unresolved_top_level_method_negative_test: CompileTimeError
+unresolved_top_level_method_negative_test: StaticWarning
vm/type_cast_vm_test: StaticWarning
vm/type_vm_test: StaticWarning
void_type_test: StaticWarning
@@ -480,6 +453,6 @@
deferred_constraints_constants_old_syntax_test/*: Pass, Fail
deferred_shadow_load_library_test: Fail
deferred_closurize_load_library_test: Fail
-deferred_load_library_wrong_args_test/01: Fail
-deferred_load_library_wrong_args_test/none: Fail
-deferred_load_inval_code_test: Fail
+deferred_load_library_wrong_args_test/01: StaticWarning
+deferred_load_library_wrong_args_test/none: StaticWarning
+deferred_load_inval_code_test: StaticWarning
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index 6af40b1..f84c5b1 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -9,13 +9,6 @@
# Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask)
built_in_identifier_prefix_test: CompileTimeError # Issue 12694
-# Issue 15315
-class_literal_test/01: CompileTimeError
-class_literal_test/03: CompileTimeError
-class_literal_test/07: CompileTimeError
-constructor_call_as_function_test/01: fail
-call_type_literal_test/01: fail
-
# TBF: we should check conflicts not only for methods, but for accessors too
override_field_test/03: fail
method_override7_test/03: Fail # Issue 11496
@@ -120,7 +113,6 @@
# test issue 12381, It is compile-time error to invoke not existing function
issue11724_test: fail # Issue 12381
-call_nonexistent_static_test/08: fail # Issue 12381
# test issue 12539, rules for finals were loosened, contradiction in spec was fixed
const_syntax_test/09: fail # Issue 12539
@@ -159,23 +151,6 @@
# test issue 14736, It is a static warning if a class C declares an instance method named n and has a setter named n=.
setter4_test: StaticWarning
-# test issue 15060
-least_upper_bound_test/23: StaticWarning # Issue 15060
-least_upper_bound_test/24: StaticWarning # Issue 15060
-least_upper_bound_test/25: MissingStaticWarning # Issue 15060
-least_upper_bound_test/26: MissingStaticWarning # Issue 15060
-least_upper_bound_test/29: StaticWarning # Issue 15060
-least_upper_bound_test/30: StaticWarning # Issue 15060
-least_upper_bound_test/31: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/02: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/04: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/05: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/06: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/08: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/10: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/11: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/12: MissingStaticWarning # Issue 15060
-
# test issue 15467
proxy_test/05: StaticWarning # Issue 15467
proxy_test/06: StaticWarning # Issue 15467
@@ -268,7 +243,7 @@
getter_declaration_negative_test: CompileTimeError
getter_no_setter2_test/01: StaticWarning
getter_no_setter_test/01: StaticWarning
-illegal_invocation_test/01: CompileTimeError
+illegal_invocation_test/01: StaticWarning
implicit_this_test/02: StaticWarning
implied_interface_test: StaticWarning
import_combinators_test: StaticWarning
@@ -300,9 +275,9 @@
label8_negative_test: CompileTimeError
label_test: StaticWarning
library_ambiguous_test/00: StaticWarning
-library_ambiguous_test/01: CompileTimeError
-library_ambiguous_test/02: CompileTimeError
-library_ambiguous_test/03: CompileTimeError
+library_ambiguous_test/01: StaticWarning
+library_ambiguous_test/02: StaticWarning
+library_ambiguous_test/03: StaticWarning
library_negative_test: CompileTimeError
list_literal2_negative_test: CompileTimeError
list_literal4_test: StaticWarning
@@ -408,7 +383,7 @@
script2_negative_test: CompileTimeError
setter_declaration2_negative_test: CompileTimeError
setter_declaration_negative_test: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
+setter_no_getter_call_test/01: StaticWarning
source_self_negative_test: CompileTimeError
static_initializer_type_error_test: StaticWarning
string_escape4_negative_test: CompileTimeError
@@ -457,7 +432,7 @@
unary_plus_negative_test: CompileTimeError
unbound_getter_test: StaticWarning
unhandled_exception_negative_test: CompileTimeError
-unresolved_top_level_method_negative_test: CompileTimeError
+unresolved_top_level_method_negative_test: StaticWarning
vm/type_cast_vm_test: StaticWarning
vm/type_vm_test: StaticWarning
void_type_test: StaticWarning
@@ -478,6 +453,6 @@
deferred_constraints_constants_old_syntax_test/*: Pass, Fail
deferred_shadow_load_library_test: Fail
deferred_closurize_load_library_test: Fail
-deferred_load_library_wrong_args_test/01: Fail
-deferred_load_library_wrong_args_test/none: Fail
-deferred_load_inval_code_test: Fail
+deferred_load_library_wrong_args_test/01: StaticWarning
+deferred_load_library_wrong_args_test/none: StaticWarning
+deferred_load_inval_code_test: StaticWarning
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index c97ea0b..444de3c 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -3,7 +3,6 @@
# BSD-style license that can be found in the LICENSE file.
[ $compiler == dart2js || $compiler == dart2dart ]
-function_literals2_test: Fail # Issue 18102
override_inheritance_mixed_test/08: Fail # Issue 18124
override_inheritance_mixed_test/09: Fail # Issue 18124
bad_constructor_test/05: CompileTimeError # Issue 13669
diff --git a/tests/language/parse_closures_in_initializers_test.dart b/tests/language/parse_closures_in_initializers_test.dart
new file mode 100644
index 0000000..4da7e98
--- /dev/null
+++ b/tests/language/parse_closures_in_initializers_test.dart
@@ -0,0 +1,45 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+// Test that function literals are parsed correctly in initializers.
+
+class A {
+ final x;
+ static foo(f) => f();
+
+ A.parenthesized(y) : x = (() => y);
+ A.stringLiteral(y) : x = "**${() => y}--";
+ A.listLiteral(y) : x = [() => y];
+ A.mapLiteral(y) : x = { "fun": () => y };
+ A.arg(y) : x = foo(() => y);
+}
+
+main() {
+ var a, f;
+ a = new A.parenthesized(499);
+ f = a.x;
+ Expect.isTrue(f is Function);
+ Expect.equals(499, f());
+
+ // The toString of closures is not specified. Just make sure that there is no
+ // crash.
+ a = new A.stringLiteral(42);
+ Expect.isTrue(a.x.startsWith("**"));
+ Expect.isTrue(a.x.endsWith("--"));
+
+ a = new A.listLiteral(99);
+ f = a.x[0];
+ Expect.isTrue(f is Function);
+ Expect.equals(99, f());
+
+ a = new A.mapLiteral(314);
+ f = a.x["fun"];
+ Expect.isTrue(f is Function);
+ Expect.equals(314, f());
+
+ a = new A.arg(123);
+ Expect.equals(123, a.x);
+}
diff --git a/tests/lib/async/future_test.dart b/tests/lib/async/future_test.dart
index ad6830a..df33697 100644
--- a/tests/lib/async/future_test.dart
+++ b/tests/lib/async/future_test.dart
@@ -745,6 +745,8 @@
}
main() {
+ asyncStart();
+
testValue();
testSync();
testNeverComplete();
@@ -797,6 +799,8 @@
testChainedFutureError();
testSyncFuture_i13368();
+
+ asyncEnd();
}
/// A Future that isn't recognizable as a _Future.
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 83bc9ea..f5d65e2 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -136,6 +136,9 @@
math/math2_test: RuntimeError
[ $compiler == dart2js && $jscl ]
+mirrors/invoke_natives_fuzz_test: RuntimeError # Issue 15566
+
+[ $compiler == dart2js && $runtime == jsshell ]
async/future_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
async/slow_consumer2_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
async/slow_consumer3_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
@@ -188,9 +191,9 @@
mirrors/globalized_closures2_test/00: RuntimeError # Issue 17118. Please remove the multi-test comments when this test starts succeeding.
-[ $compiler == dart2js && $browser ]
+[ $compiler == dart2js && ( $browser || $runtime == d8 ) ]
async/timer_not_available_test: Fail, OK # only meant to test when there is no way to
- # implement timer (currently only in d8)
+ # implement timer (currently only in jsshell)
# 'js' tests import the dart:js library, so they only make sense in
# a browser environment.
@@ -244,6 +247,7 @@
[ $runtime == vm ]
async/timer_not_available_test: Fail, OK
+mirrors/invoke_natives_fuzz_test: RuntimeError # Issue 15274
mirrors/native_class_test: Fail, OK # This test is meant to run in a browser.
[ $compiler == none ]
@@ -307,7 +311,7 @@
# Issue 13921: spawnFunction is not allowed on Dartium's DOM thread.
async/deferred/deferred_in_isolate_test: Fail
-[ $compiler == dart2js ]
+[ $compiler == dart2js && $runtime != d8 ]
async/schedule_microtask_test: Fail # Issue 9002
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
@@ -316,8 +320,6 @@
mirrors/redirecting_factory_test/01: StaticWarning # test issue X, The return type 'Class<T2, T1>' of the redirected constructor is not assignable to 'Class<T1, T2>'
mirrors/redirecting_factory_test/none: StaticWarning # test issue X, The return type 'Class<T2, T1>' of the redirected constructor is not assignable to 'Class<T1, T2>
-mirrors/fake_function_without_call_test: StaticWarning, OK # Implements Function without defining call.
-mirrors/find_in_context_fake_function_test: StaticWarning, OK # Implements Function without defining call.
mirrors/immutable_collections_test: StaticWarning, OK # Expect failure for any type of Iterable.
mirrors/inference_and_no_such_method_test: StaticWarning, OK # Expect to trigger noSuchMethod.
mirrors/repeated_private_anon_mixin_app_test: StaticWarning, OK # Intentional library name conflict.
diff --git a/tests/lib/mirrors/invoke_natives_fuzz_test.dart b/tests/lib/mirrors/invoke_natives_fuzz_test.dart
new file mode 100644
index 0000000..f9967ee
--- /dev/null
+++ b/tests/lib/mirrors/invoke_natives_fuzz_test.dart
@@ -0,0 +1,135 @@
+// 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.
+
+// This test reflectively enumerates all the methods in the system and tries to
+// invoke them will all nulls. This may result in Dart exceptions or hangs, but
+// should never result in crashes or JavaScript exceptions.
+
+library test.invoke_natives;
+
+import 'dart:mirrors';
+import 'dart:async';
+import 'package:expect/expect.dart';
+
+// Methods to be skipped, by qualified name.
+var blacklist = [
+ // These prevent the test from exiting, typically by spawning another isolate.
+ 'dart.async._scheduleAsyncCallback',
+ 'dart.io._IOService.dispatch',
+ 'dart.isolate.RawReceivePort.RawReceivePort',
+ 'dart.isolate.ReceivePort.ReceivePort',
+ 'dart.isolate.ReceivePort.ReceivePort.fromRawReceivePort',
+ 'dart.isolate.ReceivePort.sendPort',
+ 'dart.isolate.ReceivePort.close',
+ 'dart.isolate.ReceivePort.listen',
+ 'dart.isolate.RawReceivePort.sendPort',
+ 'dart.isolate.RawReceivePort.close',
+ 'dart.isolate.RawReceivePort.handler=',
+
+ // These "crash" the VM (throw uncatchable API errors).
+ // TODO(15274): Fill in this list to make the test pass and provide coverage
+ // against addition of new natives.
+];
+
+class Task {
+ var name;
+ var action;
+}
+var queue = new List();
+
+checkMethod(MethodMirror m, ObjectMirror target, [origin]) {
+ if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return;
+
+ var task = new Task();
+ task.name = '${MirrorSystem.getName(m.qualifiedName)} from $origin';
+
+ if (m.isRegularMethod) {
+ task.action =
+ () => target.invoke(m.simpleName, new List(m.parameters.length));
+ } else if (m.isGetter) {
+ task.action =
+ () => target.getField(m.simpleName);
+ } else if (m.isSetter) {
+ task.action =
+ () => target.setField(m.simpleName, null);
+ } else if (m.isConstructor) {
+ return;
+ } else {
+ throw "Unexpected method kind";
+ }
+
+ queue.add(task);
+}
+
+checkInstance(instanceMirror, origin) {
+ instanceMirror.type.declarations.values
+ .where((d) => d is MethodMirror)
+ .forEach((m) => checkMethod(m, instanceMirror, origin));
+}
+
+checkClass(classMirror) {
+ classMirror.declarations.values
+ .where((d) => d is MethodMirror)
+ .forEach((m) => checkMethod(m, classMirror));
+
+ classMirror.declarations.values
+ .where((d) => d is MethodMirror)
+ .forEach((m) {
+ if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return;
+ if (!m.isConstructor) return;
+ var task = new Task();
+ task.name = MirrorSystem.getName(m.qualifiedName);
+
+ task.action = () {
+ var instance = classMirror.newInstance(m.constructorName,
+ new List(m.parameters.length));
+ checkInstance(instance, task.name);
+ };
+ queue.add(task);
+ });
+}
+
+checkLibrary(libraryMirror) {
+ // Don't recurse on this test.
+ if (libraryMirror.simpleName == #test.invoke_natives) return;
+
+ libraryMirror.declarations.values
+ .where((d) => d is ClassMirror)
+ .forEach(checkClass);
+
+ libraryMirror.declarations.values
+ .where((d) => d is MethodMirror)
+ .forEach((m) => checkMethod(m, libraryMirror));
+}
+
+var testZone;
+var debug = true;
+
+doOneTask() {
+ if (queue.length == 0) {
+ if (debug) print('Done');
+ return;
+ }
+
+ var task = queue.removeLast();
+ if (debug) print(task.name);
+ try {
+ task.action();
+ } catch(e) {}
+ // Register the next task in a timer callback so as to yield to async code
+ // scheduled in the current task. This isn't necessary for the test itself,
+ // but is helpful when trying to figure out which function is responsible for
+ // a crash.
+ testZone.createTimer(Duration.ZERO, doOneTask);
+}
+
+main() {
+ currentMirrorSystem().libraries.values.forEach(checkLibrary);
+
+ uncaughtErrorHandler(self, parent, zone, error, stack) {};
+ var zoneSpec =
+ new ZoneSpecification(handleUncaughtError: uncaughtErrorHandler);
+ testZone = Zone.current.fork(specification: zoneSpec);
+ testZone.createTimer(Duration.ZERO, doOneTask);
+}
diff --git a/tests/lib/mirrors/invoke_natives_malicious_test.dart b/tests/lib/mirrors/invoke_natives_malicious_test.dart
new file mode 100644
index 0000000..c353354
--- /dev/null
+++ b/tests/lib/mirrors/invoke_natives_malicious_test.dart
@@ -0,0 +1,28 @@
+// 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.
+
+library test.invoke_natives;
+
+import 'dart:mirrors';
+import 'package:expect/expect.dart';
+
+test(name, action) {
+ print(name);
+ Expect.throws(action, (e)=>true, name);
+ print("done");
+}
+
+main() {
+ LibraryMirror dartcore = reflectClass(Object).owner;
+
+ test('List_copyFromObjectArray', () {
+ var receiver = new List(3);
+ var selector = MirrorSystem.getSymbol('_copyFromObjectArray', dartcore);
+ var src = new List(3);
+ var srcStart = 10;
+ var dstStart = 10;
+ var count = 10;
+ reflect(receiver).invoke(selector, [src, srcStart, dstStart, count]);
+ });
+}
diff --git a/tests/lib/profiler/user_tags_test.dart b/tests/lib/profiler/user_tags_test.dart
new file mode 100644
index 0000000..4070d90
--- /dev/null
+++ b/tests/lib/profiler/user_tags_test.dart
@@ -0,0 +1,63 @@
+// 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.
+//
+
+import 'dart:profiler';
+import 'package:expect/expect.dart';
+
+// Test that the label property matches the constructor.
+void testLabel() {
+ var label = 'Hello World';
+ var tag = new UserTag(label);
+ Expect.equals(label, tag.label);
+}
+
+
+// Test that we canonicalize UserTag by name.
+void testCanonicalize(tag1) {
+ var label = 'Global Tag';
+ var tag = new UserTag(label);
+ Expect.isTrue(identical(tag, tag1));
+}
+
+
+// Test that we made the tag current.
+void testMakeCurrent(tag) {
+ tag.makeCurrent();
+ Expect.isTrue(identical(tag, getCurrentTag()));
+}
+
+
+// Test clearCurrentTag.
+void testClearCurrent(tag) {
+ tag.makeCurrent();
+ Expect.isTrue(identical(tag, getCurrentTag()));
+ var old_tag = clearCurrentTag();
+ Expect.isTrue(identical(tag, old_tag));
+ Expect.isNull(getCurrentTag());
+}
+
+
+// Test that we reach a limit of tags an exception is thrown.
+void testExhaust() {
+ var i = 0;
+ while (true) {
+ var label = i.toString();
+ var tag = new UserTag(label);
+ i++;
+ }
+}
+
+
+main() {
+ var label = 'Global Tag';
+ var tag = new UserTag(label);
+ testLabel();
+ testCanonicalize(tag);
+ for (var i = 0; i < 2000; i++) {
+ testMakeCurrent(tag);
+ }
+ testClearCurrent(tag);
+ Expect.throws(testExhaust);
+}
diff --git a/tests/standalone/io/file_input_stream_test.dart b/tests/standalone/io/file_input_stream_test.dart
index 923a44c..86cb355 100644
--- a/tests/standalone/io/file_input_stream_test.dart
+++ b/tests/standalone/io/file_input_stream_test.dart
@@ -209,15 +209,18 @@
var file = new File('${temp.path}/input_stream_bad_offset.txt');
var originalLength = writeLongFileSync(file);
var streamedBytes = 0;
+ bool error = false;
file.openRead(start, end).listen(
(d) {
streamedBytes += d.length;
},
onDone: () {
- temp.delete(recursive: true);
+ Expect.isTrue(error);
+ temp.deleteSync(recursive: true);
+ asyncEnd();
},
onError: (e) {
- asyncEnd();
+ error = true;
});
}
test(-1, null);
@@ -230,7 +233,7 @@
String fileName = getFilename("tests/standalone/io/$name");
// File contains 10 lines.
File file = new File(fileName);
- Expect.equals(length, file.openSync().lengthSync());
+ Expect.equals(length, file.lengthSync());
var lineStream = file.openRead()
.transform(UTF8.decoder)
.transform(new LineSplitter());
diff --git a/tests/standalone/io/file_invalid_arguments_test.dart b/tests/standalone/io/file_invalid_arguments_test.dart
index d66c458..58c6d7c 100644
--- a/tests/standalone/io/file_invalid_arguments_test.dart
+++ b/tests/standalone/io/file_invalid_arguments_test.dart
@@ -16,6 +16,7 @@
Expect.throws(() => file.read(arg),
(e) => e is ArgumentError);
+ file.closeSync();
}
void testReadIntoInvalidArgs(buffer, start, end) {
@@ -26,6 +27,7 @@
Expect.throws(() => file.readInto(buffer, start, end),
(e) => e is ArgumentError);
+ file.closeSync();
}
void testWriteByteInvalidArgs(value) {
@@ -36,6 +38,7 @@
Expect.throws(() => file.writeByte(value),
(e) => e is ArgumentError);
+ file.closeSync();
}
void testWriteFromInvalidArgs(buffer, start, end) {
@@ -46,6 +49,7 @@
Expect.throws(() => file.writeFrom(buffer, start, end),
(e) => e is ArgumentError);
+ file.closeSync();
}
void testWriteStringInvalidArgs(string, encoding) {
@@ -56,6 +60,7 @@
Expect.throws(() => file.writeString(string, encoding: encoding),
(e) => e is ArgumentError);
+ file.closeSync();
}
Future futureThrows(Future result) {
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index f8e93c9..0ce18ae 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -262,17 +262,24 @@
Expect.equals(103, buffer[9]); // represents 'g' in the file.
Expect.equals(104, buffer[10]); // represents 'h' in the file.
Expect.equals(116, buffer[11]); // represents 't' in the file.
+ raf.closeSync();
filename = getFilename("tests/vm/data/fixed_length_file");
File file = new File(filename);
int len = file.lengthSync();
- Expect.equals(0, file.openSync().readSync(0).length);
- Expect.equals(1, file.openSync().readSync(1).length);
- Expect.equals(len - 1, file.openSync().readSync(len - 1).length);
- Expect.equals(len, file.openSync().readSync(len).length);
- Expect.equals(len, file.openSync().readSync(len + 1).length);
- Expect.equals(len, file.openSync().readSync(len * 2).length);
- Expect.equals(len, file.openSync().readSync(len * 10).length);
+ int read(int length) {
+ var f = file.openSync();
+ int res = f.readSync(length).length;
+ f.closeSync();
+ return res;
+ }
+ Expect.equals(0, read(0));
+ Expect.equals(1, read(1));
+ Expect.equals(len - 1, read(len - 1));
+ Expect.equals(len, read(len));
+ Expect.equals(len, read(len + 1));
+ Expect.equals(len, read(len * 2));
+ Expect.equals(len, read(len * 10));
}
// Test for file read and write functionality.
diff --git a/tests/standalone/io/http_client_connect_test.dart b/tests/standalone/io/http_client_connect_test.dart
index b6b8e37..8e18325 100644
--- a/tests/standalone/io/http_client_connect_test.dart
+++ b/tests/standalone/io/http_client_connect_test.dart
@@ -148,19 +148,58 @@
});
}
-void testPostEmptyRequest() {
- HttpServer.bind("127.0.0.1", 0).then((server) {
- server.listen((request) {
- request.pipe(request.response);
- });
- var client = new HttpClient();
- client.post("127.0.0.1", server.port, "/")
- .then((request) => request.close())
- .then((response) {
- response.listen((data) {}, onDone: server.close);
+void testOpenEmptyRequest() {
+ var client = new HttpClient();
+ var methods = [
+ [client.get, 'GET'],
+ [client.post, 'POST'],
+ [client.put, 'PUT'],
+ [client.delete, 'DELETE'],
+ [client.patch, 'PATCH'],
+ [client.head, 'HEAD']];
+
+ for (var method in methods) {
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen((request) {
+ Expect.equals(method[1], request.method);
+ request.pipe(request.response);
});
- });
+
+ method[0]("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((data) {}, onDone: server.close);
+ });
+ });
+ }
+}
+
+
+void testOpenUrlEmptyRequest() {
+ var client = new HttpClient();
+ var methods = [
+ [client.getUrl, 'GET'],
+ [client.postUrl, 'POST'],
+ [client.putUrl, 'PUT'],
+ [client.deleteUrl, 'DELETE'],
+ [client.patchUrl, 'PATCH'],
+ [client.headUrl, 'HEAD']];
+
+ for (var method in methods) {
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen((request) {
+ Expect.equals(method[1], request.method);
+ request.pipe(request.response);
+ });
+
+ method[0](Uri.parse("http://127.0.0.1:${server.port}/"))
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((data) {}, onDone: server.close);
+ });
+ });
+ }
}
@@ -218,6 +257,7 @@
testGetServerCloseNoKeepAlive();
testGetServerForceClose();
testGetDataServerForceClose();
- testPostEmptyRequest();
+ testOpenEmptyRequest();
+ testOpenUrlEmptyRequest();
testNoBuffer();
}
diff --git a/tests/standalone/io/http_server_test.dart b/tests/standalone/io/http_server_test.dart
index 4eb8df5..f664501 100644
--- a/tests/standalone/io/http_server_test.dart
+++ b/tests/standalone/io/http_server_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import "dart:async";
+import "dart:typed_data";
import "dart:io";
import "package:async_helper/async_helper.dart";
@@ -110,8 +111,34 @@
}
+void testHttpServerClientClose() {
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ runZoned(() {
+ server.listen((request) {
+ request.response.bufferOutput = false;
+ request.response.add(new Uint8List(64 * 1024));
+ new Timer(const Duration(milliseconds: 100), () {
+ request.response.close().then((_) {
+ server.close();
+ });
+ });
+ });
+ }, onError: (e, s) {
+ Expect.fail("Unexpected error: $e(${e.hashCode})\n$s");
+ });
+ var client = new HttpClient();
+ client.get("127.0.0.1", server.port, "/")
+ .then((request) => request.close())
+ .then((response) {
+ response.listen((_) {}).cancel();
+ });
+ });
+}
+
+
void main() {
testListenOn();
testHttpServerZone();
testHttpServerZoneError();
+ testHttpServerClientClose();
}
diff --git a/tests/standalone/io/read_into_const_list_test.dart b/tests/standalone/io/read_into_const_list_test.dart
index 5bc1c6b..f010a83 100644
--- a/tests/standalone/io/read_into_const_list_test.dart
+++ b/tests/standalone/io/read_into_const_list_test.dart
@@ -27,5 +27,6 @@
}
Expect.equals(0, a[0]);
Expect.equals(0, b[0]);
+ input.closeSync();
});
}
diff --git a/tests/standalone/issue14236_test.dart b/tests/standalone/issue14236_test.dart
index 467e13a..74a8f29 100644
--- a/tests/standalone/issue14236_test.dart
+++ b/tests/standalone/issue14236_test.dart
Binary files differ
diff --git a/tools/VERSION b/tools/VERSION
index ed1e417..71f97d8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 1
MINOR 4
PATCH 0
-PRERELEASE 2
-PRERELEASE_PATCH 2
+PRERELEASE 3
+PRERELEASE_PATCH 0
diff --git a/tools/create_debian_packages.py b/tools/create_debian_packages.py
index d739dfd..0eba452 100755
--- a/tools/create_debian_packages.py
+++ b/tools/create_debian_packages.py
@@ -65,10 +65,6 @@
print "Building source package"
RunBuildPackage(['-S', '-us', '-uc'], join(temp_dir, tarroot));
- # Build 32-bit binary package.
- print "Building i386 package"
- RunBuildPackage(['-B', '-ai386', '-us', '-uc'], join(temp_dir, tarroot));
-
# Build 64-bit binary package.
print "Building amd64 package"
RunBuildPackage(['-B', '-aamd64', '-us', '-uc'], join(temp_dir, tarroot));
@@ -80,17 +76,12 @@
'%s.orig.tar.gz' % debbase,
'%s-1.debian.tar.gz' % debbase
]
- i386_package = [
- '%s-1_i386.deb' % debbase
- ]
amd64_package = [
'%s-1_amd64.deb' % debbase
]
for name in source_package:
copyfile(join(temp_dir, name), join(out_dir, name))
- for name in i386_package:
- copyfile(join(temp_dir, name), join(out_dir, name))
for name in amd64_package:
copyfile(join(temp_dir, name), join(out_dir, name))
diff --git a/tools/create_editor.py b/tools/create_editor.py
index ba4f4fa5..d53d19c 100644
--- a/tools/create_editor.py
+++ b/tools/create_editor.py
@@ -80,7 +80,7 @@
lines = f.readlines()
f.close()
lines[lines.index('-Xms40m\n')] = '-Xms256m\n'
- lines[lines.index('-Xmx1000m\n')] = '-Xmx2000m\n'
+ lines[lines.index('-Xmx1850m\n')] = '-Xmx2000m\n'
# Add -d64 to give better error messages to user in 64 bit mode.
lines[lines.index('-vmargs\n')] = '-vmargs\n-d64\n'
f = open(iniFilePath, 'w')
diff --git a/tools/create_sdk.py b/tools/create_sdk.py
index 62b9194..c8f79ed 100755
--- a/tools/create_sdk.py
+++ b/tools/create_sdk.py
@@ -203,7 +203,7 @@
join('html', 'dart2js'), join('html', 'dartium'),
join('html', 'html_common'),
join('indexed_db', 'dart2js'), join('indexed_db', 'dartium'),
- 'js', 'math', 'mirrors', 'typed_data',
+ 'js', 'math', 'mirrors', 'typed_data', 'profiler',
join('svg', 'dart2js'), join('svg', 'dartium'),
join('web_audio', 'dart2js'), join('web_audio', 'dartium'),
join('web_gl', 'dart2js'), join('web_gl', 'dartium'),
diff --git a/tools/create_timestamp_file.py b/tools/create_timestamp_file.py
new file mode 100644
index 0000000..7a1d4d3
--- /dev/null
+++ b/tools/create_timestamp_file.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import os
+
+def main(args):
+ for file_name in args[1:]:
+ dir_name = os.path.dirname(file_name)
+ if not os.path.exists(dir_name):
+ os.mkdir(dir_name)
+ open(file_name, 'w').close()
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index c6c9b57..62f093f 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -163,8 +163,6 @@
'WheelEvent': 'WheelEvent,MouseWheelEvent,MouseScrollEvent',
- 'XMLHttpRequestUpload': 'XMLHttpRequestUpload,XMLHttpRequestEventTarget',
-
}, dart2jsOnly=True)
def IsRegisteredType(type_name):
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 1b39357..f2cac7e 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -288,7 +288,6 @@
'MutationEvent.initMutationEvent',
'MutationObserver.observe',
'Node.attributes',
- 'Node.baseURI',
'Node.localName',
'Node.namespaceURI',
'Node.removeChild',
diff --git a/tools/dom/src/dart2js_CustomElementSupport.dart b/tools/dom/src/dart2js_CustomElementSupport.dart
index aa1c037..009f857 100644
--- a/tools/dom/src/dart2js_CustomElementSupport.dart
+++ b/tools/dom/src/dart2js_CustomElementSupport.dart
@@ -87,7 +87,7 @@
if (extendsTagName == null) {
if (baseClassName != 'HTMLElement') {
throw new UnsupportedError('Class must provide extendsTag if base '
- 'native class is not HTMLElement');
+ 'native class is not HtmlElement');
}
} else {
if (!JS('bool', '(#.createElement(#) instanceof window[#])',
@@ -128,3 +128,60 @@
void _initializeCustomElement(Element e) {
// TODO(blois): Add validation that this is only in response to an upgrade.
}
+
+/// Dart2JS implementation of ElementUpgrader
+class _JSElementUpgrader implements ElementUpgrader {
+ var _interceptor;
+ var _constructor;
+ var _nativeType;
+
+ _JSElementUpgrader(Document document, Type type, String extendsTag) {
+ var interceptorClass = findInterceptorConstructorForType(type);
+ if (interceptorClass == null) {
+ throw new ArgumentError(type);
+ }
+
+ _constructor = findConstructorForNativeSubclassType(type, 'created');
+ if (_constructor == null) {
+ throw new ArgumentError("$type has no constructor called 'created'");
+ }
+
+ // Workaround for 13190- use an article element to ensure that HTMLElement's
+ // interceptor is resolved correctly.
+ getNativeInterceptor(new Element.tag('article'));
+
+ var baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
+ if (baseClassName == null) {
+ throw new ArgumentError(type);
+ }
+
+ if (extendsTag == null) {
+ if (baseClassName != 'HTMLElement') {
+ throw new UnsupportedError('Class must provide extendsTag if base '
+ 'native class is not HtmlElement');
+ }
+ _nativeType = HtmlElement;
+ } else {
+ var element = document.createElement(extendsTag);
+ if (!JS('bool', '(# instanceof window[#])',
+ element, baseClassName)) {
+ throw new UnsupportedError(
+ 'extendsTag does not match base native class');
+ }
+ _nativeType = element.runtimeType;
+ }
+
+ _interceptor = JS('=Object', '#.prototype', interceptorClass);
+ }
+
+ Element upgrade(Element element) {
+ // Only exact type matches are supported- cannot be a subclass.
+ if (element.runtimeType != _nativeType) {
+ throw new ArgumentError('element is not subclass of $_nativeType');
+ }
+
+ setNativeSubclassDispatchRecord(element, _interceptor);
+ JS('', '#(#)', _constructor, element);
+ return element;
+ }
+}
diff --git a/tools/dom/src/dart2js_LocationWrapper.dart b/tools/dom/src/dart2js_LocationWrapper.dart
deleted file mode 100644
index c943226..0000000
--- a/tools/dom/src/dart2js_LocationWrapper.dart
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2011, 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.
-
-part of html;
-
-// On Firefox 11, the object obtained from 'window.location' is very strange.
-// It can't be monkey-patched and seems immune to putting methods on
-// Object.prototype. We are forced to wrap the object.
-
-class _LocationWrapper implements Location {
-
- final _ptr; // Opaque reference to real location.
-
- _LocationWrapper(this._ptr);
-
- // TODO(sra): Replace all the _set and _get calls with 'JS' forms.
-
- // final List<String> ancestorOrigins;
- List<String> get ancestorOrigins => _get(_ptr, 'ancestorOrigins');
-
- // String hash;
- String get hash => _get(_ptr, 'hash');
- void set hash(String value) {
- _set(_ptr, 'hash', value);
- }
-
- // String host;
- String get host => _get(_ptr, 'host');
- void set host(String value) {
- _set(_ptr, 'host', value);
- }
-
- // String hostname;
- String get hostname => _get(_ptr, 'hostname');
- void set hostname(String value) {
- _set(_ptr, 'hostname', value);
- }
-
- // String href;
- String get href => _get(_ptr, 'href');
- void set href(String value) {
- _set(_ptr, 'href', value);
- }
-
- // final String origin;
- String get origin {
- if (JS('bool', '("origin" in #)', _ptr)) {
- return JS('String', '#.origin', _ptr);
- }
- return '${this.protocol}//${this.host}';
- }
-
- // String pathname;
- String get pathname => _get(_ptr, 'pathname');
- void set pathname(String value) {
- _set(_ptr, 'pathname', value);
- }
-
- // String port;
- String get port => _get(_ptr, 'port');
- void set port(String value) {
- _set(_ptr, 'port', value);
- }
-
- // String protocol;
- String get protocol => _get(_ptr, 'protocol');
- void set protocol(String value) {
- _set(_ptr, 'protocol', value);
- }
-
- // String search;
- String get search => _get(_ptr, 'search');
- void set search(String value) {
- _set(_ptr, 'search', value);
- }
-
- void assign(String url) => JS('void', '#.assign(#)', _ptr, url);
-
- void reload() => JS('void', '#.reload()', _ptr);
-
- void replace(String url) => JS('void', '#.replace(#)', _ptr, url);
-
- String toString() => JS('String', '#.toString()', _ptr);
-
-
- static _get(p, m) => JS('var', '#[#]', p, m);
- static _set(p, m, v) => JS('void', '#[#] = #', p, m, v);
-}
diff --git a/tools/dom/src/dartium_CustomElementSupport.dart b/tools/dom/src/dartium_CustomElementSupport.dart
new file mode 100644
index 0000000..df29593
--- /dev/null
+++ b/tools/dom/src/dartium_CustomElementSupport.dart
@@ -0,0 +1,89 @@
+// 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.
+
+part of dart.dom.html;
+
+/// Dartium ElementUpgrader implementation.
+class _VMElementUpgrader implements ElementUpgrader {
+ final Type _type;
+ final Type _nativeType;
+
+ _VMElementUpgrader(Document document, Type type, String extendsTag) :
+ _type = type,
+ _nativeType = _validateCustomType(type).reflectedType {
+
+ if (extendsTag == null) {
+ if (_nativeType != HtmlElement) {
+ throw new UnsupportedError('Class must provide extendsTag if base '
+ 'native class is not HtmlElement');
+ }
+ } else {
+ if (document.createElement(extendsTag).runtimeType != _nativeType) {
+ throw new UnsupportedError(
+ 'extendsTag does not match base native class');
+ }
+ }
+ }
+
+ Element upgrade(Element element) {
+ if (element.runtimeType != _nativeType) {
+ throw new UnsupportedError('Element is incorrect type');
+ }
+ return _Utils.changeElementWrapper(element, _type);
+ return null;
+ }
+}
+
+/// Validates that the custom type is properly formed-
+///
+/// * Is a user-defined class.
+/// * Has a created constructor with zero args.
+/// * Derives from an Element subclass.
+///
+/// Then returns the native base class.
+ClassMirror _validateCustomType(Type type) {
+ ClassMirror cls = reflectClass(type);
+ if (_isBuiltinType(cls)) {
+ throw new UnsupportedError('Invalid custom element from '
+ '${(cls.owner as LibraryMirror).uri}.');
+ }
+
+ var className = MirrorSystem.getName(cls.simpleName);
+ var createdConstructor = cls.declarations[new Symbol('$className.created')];
+ if (createdConstructor == null ||
+ createdConstructor is! MethodMirror ||
+ !createdConstructor.isConstructor) {
+ throw new UnsupportedError(
+ 'Class is missing constructor $className.created');
+ }
+
+ if (createdConstructor.parameters.length > 0) {
+ throw new UnsupportedError(
+ 'Constructor $className.created must take zero arguments');
+ }
+
+ Symbol objectName = reflectClass(Object).qualifiedName;
+ bool isRoot(ClassMirror cls) =>
+ cls == null || cls.qualifiedName == objectName;
+ Symbol elementName = reflectClass(HtmlElement).qualifiedName;
+ bool isElement(ClassMirror cls) =>
+ cls != null && cls.qualifiedName == elementName;
+ ClassMirror superClass = cls.superclass;
+ ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
+ while(!isRoot(superClass) && !isElement(superClass)) {
+ superClass = superClass.superclass;
+ if (nativeClass == null && _isBuiltinType(superClass)) {
+ nativeClass = superClass;
+ }
+ }
+ return nativeClass;
+}
+
+
+bool _isBuiltinType(ClassMirror cls) {
+ // TODO(vsm): Find a less hackish way to do this.
+ LibraryMirror lib = cls.owner;
+ String libName = lib.uri.toString();
+ return libName.startsWith('dart:');
+}
diff --git a/tools/dom/src/native_DOMImplementation.dart b/tools/dom/src/native_DOMImplementation.dart
index 8387a0f..d8c4edc 100644
--- a/tools/dom/src/native_DOMImplementation.dart
+++ b/tools/dom/src/native_DOMImplementation.dart
@@ -396,48 +396,10 @@
static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
- static bool _isBuiltinType(ClassMirror cls) {
- // TODO(vsm): Find a less hackish way to do this.
- LibraryMirror lib = cls.owner;
- String libName = lib.uri.toString();
- return libName.startsWith('dart:');
- }
-
static void register(Document document, String tag, Type type,
String extendsTagName) {
- // TODO(vsm): Move these checks into native code.
- ClassMirror cls = reflectClass(type);
- if (_isBuiltinType(cls)) {
- throw new UnsupportedError("Invalid custom element from ${(cls.owner as LibraryMirror).uri}.");
- }
- var className = MirrorSystem.getName(cls.simpleName);
- var createdConstructor = cls.declarations[new Symbol('$className.created')];
- if (createdConstructor == null ||
- createdConstructor is! MethodMirror ||
- !createdConstructor.isConstructor) {
- throw new UnsupportedError(
- 'Class is missing constructor $className.created');
- }
+ var nativeClass = _validateCustomType(type);
- if (createdConstructor.parameters.length > 0) {
- throw new UnsupportedError(
- 'Constructor $className.created must take zero arguments');
- }
-
- Symbol objectName = reflectClass(Object).qualifiedName;
- bool isRoot(ClassMirror cls) =>
- cls == null || cls.qualifiedName == objectName;
- Symbol elementName = reflectClass(HtmlElement).qualifiedName;
- bool isElement(ClassMirror cls) =>
- cls != null && cls.qualifiedName == elementName;
- ClassMirror superClass = cls.superclass;
- ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
- while(!isRoot(superClass) && !isElement(superClass)) {
- superClass = superClass.superclass;
- if (nativeClass == null && _isBuiltinType(superClass)) {
- nativeClass = superClass;
- }
- }
if (extendsTagName == null) {
if (nativeClass.reflectedType != HtmlElement) {
throw new UnsupportedError('Class must provide extendsTag if base '
@@ -454,6 +416,8 @@
static Element createElement(Document document, String tagName) native "Utils_createElement";
static void initializeCustomElement(HtmlElement element) native "Utils_initializeCustomElement";
+
+ static void changeElementWrapper(HtmlElement element, Type type) native "Utils_changeElementWrapper";
}
class _DOMWindowCrossFrame extends NativeFieldWrapperClass2 implements
diff --git a/tools/dom/src/shared_html.dart b/tools/dom/src/shared_html.dart
index 7c4f696..dd67c48 100644
--- a/tools/dom/src/shared_html.dart
+++ b/tools/dom/src/shared_html.dart
@@ -68,3 +68,12 @@
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+ /// Upgrade the specified element to be of the Dart type this was created for.
+ ///
+ /// After upgrading the element passed in is invalid and the returned value
+ /// should be used instead.
+ Element upgrade(Element element);
+}
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 91d162f..1695b66 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -89,7 +89,6 @@
part '$AUXILIARY_DIR/dart2js_CustomElementSupport.dart';
part '$AUXILIARY_DIR/dart2js_DOMImplementation.dart';
part '$AUXILIARY_DIR/dart2js_KeyEvent.dart';
-part '$AUXILIARY_DIR/dart2js_LocationWrapper.dart';
part '$AUXILIARY_DIR/dart2js_Platform.dart';
part '$AUXILIARY_DIR/shared_html.dart';
part '$AUXILIARY_DIR/Validators.dart';
diff --git a/tools/dom/templates/html/dartium/html_dartium.darttemplate b/tools/dom/templates/html/dartium/html_dartium.darttemplate
index 8c22b56..3e10354 100644
--- a/tools/dom/templates/html/dartium/html_dartium.darttemplate
+++ b/tools/dom/templates/html/dartium/html_dartium.darttemplate
@@ -78,6 +78,7 @@
part '$AUXILIARY_DIR/WrappedList.dart';
part '$AUXILIARY_DIR/_HttpRequestUtils.dart';
part '$AUXILIARY_DIR/_ListIterators.dart';
+part '$AUXILIARY_DIR/dartium_CustomElementSupport.dart';
part '$AUXILIARY_DIR/dartium_KeyEvent.dart';
part '$AUXILIARY_DIR/dartium_Platform.dart';
part '$AUXILIARY_DIR/shared_html.dart';
@@ -130,4 +131,4 @@
Future<Isolate> spawnDomUri(Uri uri, List<String> args, message) {
// TODO(17738): Plumb arguments and return value through.
return _Utils.spawnDomUri(uri.toString());
-}
\ No newline at end of file
+}
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 7fd5abc..4155c8d 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -1223,7 +1223,7 @@
// Workaround for Chrome bug 229142- URIs are not resolved in new doc.
var base = _parseDocument.createElement('base');
- base.href = document._baseUri;
+ base.href = document.baseUri;
_parseDocument.head.append(base);
}
var contextElement;
diff --git a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
index 973c9d4..70662bd 100644
--- a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
@@ -244,32 +244,6 @@
* This custom element can also be instantiated via HTML using the syntax
* `<input is="x-bar"></input>`
*
- * The [nativeTagName] parameter is needed by platforms without native support
- * when subclassing a native type other than:
- *
- * * HtmlElement
- * * SvgElement
- * * AnchorElement
- * * AudioElement
- * * ButtonElement
- * * CanvasElement
- * * DivElement
- * * ImageElement
- * * InputElement
- * * LIElement
- * * LabelElement
- * * MenuElement
- * * MeterElement
- * * OListElement
- * * OptionElement
- * * OutputElement
- * * ParagraphElement
- * * PreElement
- * * ProgressElement
- * * SelectElement
- * * SpanElement
- * * UListElement
- * * VideoElement
*/
$if DART2JS
void register(String tag, Type customElementClass, {String extendsTag}) {
@@ -321,4 +295,21 @@
@Experimental()
Stream<Event> get onVisibilityChange =>
visibilityChangeEvent.forTarget(this);
+
+ /// Creates an element upgrader which can be used to change the Dart wrapper
+ /// type for elements.
+ ///
+ /// The type specified must be a subclass of HtmlElement, when an element is
+ /// upgraded then the created constructor will be invoked on that element.
+ ///
+ /// If the type is not a direct subclass of HtmlElement then the extendsTag
+ /// parameter must be provided.
+ @Experimental()
+ ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+$if DART2JS
+ return new _JSElementUpgrader(this, type, extendsTag);
+$else
+ return new _VMElementUpgrader(this, type, extendsTag);
+$endif
+ }
}
diff --git a/tools/dom/templates/html/impl/impl_Window.darttemplate b/tools/dom/templates/html/impl/impl_Window.darttemplate
index cdf44bf..94baa46 100644
--- a/tools/dom/templates/html/impl/impl_Window.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Window.darttemplate
@@ -70,59 +70,29 @@
}
// API level getter and setter for Location.
- // TODO: The cross domain safe wrapper can be inserted here or folded into
- // _LocationWrapper.
+ // TODO: The cross domain safe wrapper can be inserted here.
/**
* The current location of this window.
*
* Location currentLocation = window.location;
* print(currentLocation.href); // 'http://www.example.com:80/'
*/
- Location get location {
- // Firefox work-around for Location. The Firefox location object cannot be
- // made to behave like a Dart object so must be wrapped.
- var result = _location;
- if (_isDartLocation(result)) return result; // e.g. on Chrome.
- if (null == _location_wrapper) {
- _location_wrapper = new _LocationWrapper(result);
- }
- return _location_wrapper;
- }
+ Location get location => _location;
// TODO: consider forcing users to do: window.location.assign('string').
/**
* Sets the window's location, which causes the browser to navigate to the new
- * location. [value] may be a Location object or a string.
+ * location. [value] may be a Location object or a String.
*/
void set location(value) {
- if (value is _LocationWrapper) {
- _location = value._ptr;
- } else {
- _location = value;
- }
+ _location = value;
}
- _LocationWrapper _location_wrapper; // Cached wrapped Location object.
-
// Native getter and setter to access raw Location object.
- dynamic get _location => JS('Location|=Object', '#.location', this);
+ dynamic get _location => JS('Location|Null', '#.location', this);
void set _location(value) {
JS('void', '#.location = #', this, value);
}
- // Prevent compiled from thinking 'location' property is available for a Dart
- // member.
- @JSName('location')
- _protect_location() native;
-
- static _isDartLocation(thing) {
- // On Firefox the code that implements 'is Location' fails to find the patch
- // stub on Object.prototype and throws an exception.
- try {
- return thing is Location;
- } catch (e) {
- return false;
- }
- }
/**
* Called to draw an animation frame and then request the window to repaint
diff --git a/tools/gyp/configurations_make.gypi b/tools/gyp/configurations_make.gypi
index ec010c9..dbc7cb5 100644
--- a/tools/gyp/configurations_make.gypi
+++ b/tools/gyp/configurations_make.gypi
@@ -30,7 +30,7 @@
'Dart_Linux_ia32_Base': {
'abstract': 1,
- 'cflags': [ '-m32', '-msse2' ],
+ 'cflags': [ '-m32', '-msse2', '-mfpmath=sse' ],
'ldflags': [ '-m32', ],
},
diff --git a/tools/testing/dart/browser_controller.dart b/tools/testing/dart/browser_controller.dart
index 92166e4..41a74ab 100644
--- a/tools/testing/dart/browser_controller.dart
+++ b/tools/testing/dart/browser_controller.dart
@@ -83,7 +83,7 @@
const ['safari', 'ff', 'firefox', 'chrome', 'ie9', 'ie10',
'ie11', 'dartium'];
- static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const [];
+ static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const ['ie11'];
// TODO(kustermann): add standard support for chrome on android
static bool supportedBrowser(String name) {
@@ -1376,11 +1376,10 @@
if (use_iframe) {
embedded_iframe.src = url;
} else {
- if (testing_window == undefined) {
- testing_window = window.open(url);
- } else {
- testing_window.location = url;
+ if (typeof testing_window != 'undefined') {
+ testing_window.close();
}
+ testing_window = window.open(url);
}
}
diff --git a/tools/testing/dart/browser_test.dart b/tools/testing/dart/browser_test.dart
index 42aa821..5e6be19 100644
--- a/tools/testing/dart/browser_test.dart
+++ b/tools/testing/dart/browser_test.dart
@@ -32,8 +32,6 @@
</script>
<script type="text/javascript"
src="/root_dart/pkg/browser/lib/dart.js"></script>
- <script type="text/javascript"
- src="/root_dart/pkg/browser/lib/interop.js"></script>
</body>
</html>""";
}
diff --git a/tools/testing/dart/multitest.dart b/tools/testing/dart/multitest.dart
index cc4445f..51468b2 100644
--- a/tools/testing/dart/multitest.dart
+++ b/tools/testing/dart/multitest.dart
@@ -272,7 +272,6 @@
isNegativeIfChecked: isNegativeIfChecked,
hasCompileErrorIfChecked: hasCompileErrorIfChecked,
hasStaticWarning: hasStaticWarning,
- multitestOutcome: outcome,
multitestKey: key,
originTestPath: filePath);
}
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
index a3d59b9..6dbc8af 100644
--- a/tools/testing/dart/test_runner.dart
+++ b/tools/testing/dart/test_runner.dart
@@ -576,10 +576,16 @@
class CommandBuilder {
static final CommandBuilder instance = new CommandBuilder._();
+ bool _cleared = false;
final _cachedCommands = new Map<Command, Command>();
CommandBuilder._();
+ void clearCommandCache() {
+ _cachedCommands.clear();
+ _cleared = true;
+ }
+
ContentShellCommand getContentShellCommand(String executable,
String htmlFile,
List<String> options,
@@ -674,6 +680,10 @@
// We check if this command has already been built.
// If so, we return the cached one. Otherwise we
// store the one given as [command] argument.
+ if (_cleared) {
+ throw new Exception(
+ "CommandBuilder.get[type]Command called after cache cleared");
+ }
var cachedCommand = _cachedCommands[command];
if (cachedCommand != null) {
return cachedCommand;
@@ -824,7 +834,7 @@
return testOutput.contains("unittest-suite-wait-for-done");
}
- bool _isAsyncTestSuccessfull(String testOutput) {
+ bool _isAsyncTestSuccessful(String testOutput) {
return testOutput.contains("unittest-suite-success");
}
@@ -835,7 +845,7 @@
// TODO: maybe we should introduce a AsyncIncomplete marker or so
if (outcome == Expectation.PASS) {
if (_isAsyncTest(testOutput) &&
- !_isAsyncTestSuccessfull(testOutput)) {
+ !_isAsyncTestSuccessful(testOutput)) {
return Expectation.FAIL;
}
}
@@ -1464,7 +1474,7 @@
if (line.length == 0) continue;
List<String> fields = splitMachineError(line);
// We only consider errors/warnings for files of interest.
- if (fields.length > FILENAME) {
+ if (fields.length > FORMATTED_ERROR) {
if (fields[ERROR_LEVEL] == 'ERROR') {
outErrors.add(fields[FORMATTED_ERROR]);
} else if (fields[ERROR_LEVEL] == 'WARNING') {
@@ -2278,7 +2288,7 @@
* [CommandQueue] will listen for nodes entering the NodeState.ENQUEUING state,
* queue them up and run them. While nodes are processed they will be in the
* NodeState.PROCESSING state. After running a command, the node will change
- * to a state of NodeState.Successfull or NodeState.Failed.
+ * to a state of NodeState.Successful or NodeState.Failed.
*
* It provides a synchronous stream [completedCommands] which provides the
* [CommandOutputs] for the finished commands.
@@ -2322,7 +2332,7 @@
}
});
// We're finished if the graph is sealed and all nodes are in a finished
- // state (Successfull, Failed or UnableToRun).
+ // state (Successful, Failed or UnableToRun).
// So we're calling '_checkDone()' to check whether that condition is met
// and we can cleanup.
graph.events.listen((dgraph.GraphEvent event) {
@@ -2651,7 +2661,7 @@
/*
* [TestCaseCompleter] will listen for
- * NodeState.Processing -> NodeState.{Successfull,Failed} state changes and
+ * NodeState.Processing -> NodeState.{Successful,Failed} state changes and
* will complete a TestCase if it is finished.
*
* It provides a stream [finishedTestCases], which will stream all TestCases
@@ -2684,7 +2694,7 @@
_checkDone();
});
- // Listen for NodeState.Processing -> NodeState.{Successfull,Failed}
+ // Listen for NodeState.Processing -> NodeState.{Successful,Failed}
// changes.
eventCondition((event) => event is dgraph.StateChangedEvent)
.listen((dgraph.StateChangedEvent event) {
@@ -2755,8 +2765,6 @@
class ProcessQueue {
Map _globalConfiguration;
- bool _allTestsWereEnqueued = false;
-
bool _listTests;
Function _allDone;
final dgraph.Graph _graph = new dgraph.Graph();
@@ -2916,6 +2924,10 @@
testCaseEnqueuer.enqueueTestSuites(testSuites);
}
+ void freeEnqueueingStructures() {
+ CommandBuilder.instance.clearCommandCache();
+ }
+
void eventFinishedTestCase(TestCase testCase) {
for (var listener in _eventListener) {
listener.done(testCase);
@@ -2929,6 +2941,7 @@
}
void eventAllTestsKnown() {
+ freeEnqueueingStructures();
for (var listener in _eventListener) {
listener.allTestsKnown();
}
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart
index 4a12b7a..b3d3ed7 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/tools/testing/dart/test_suite.dart
@@ -50,7 +50,6 @@
{bool isNegativeIfChecked,
bool hasCompileErrorIfChecked,
bool hasStaticWarning,
- Set<String> multitestOutcome,
String multitestKey,
Path originTestPath});
@@ -121,6 +120,9 @@
abstract class TestSuite {
final Map configuration;
final String suiteName;
+ // This function is set by subclasses before enqueueing starts.
+ Function doTest;
+
TestSuite(this.configuration, this.suiteName);
@@ -245,10 +247,6 @@
*/
void forEachTest(TestCaseEvent onTest, Map testCache, [VoidFunction onDone]);
-
- // This function is set by subclasses before enqueueing starts.
- Function doTest;
-
// This function will be called for every TestCase of this test suite.
// It will
// - handle sharding
@@ -575,14 +573,12 @@
bool isNegativeIfChecked;
bool hasCompileErrorIfChecked;
bool hasStaticWarning;
- Set<String> multitestOutcome;
String multitestKey;
TestInformation(this.filePath, this.optionsFromFile,
this.hasCompileError, this.hasRuntimeError,
this.isNegativeIfChecked, this.hasCompileErrorIfChecked,
this.hasStaticWarning,
- this.multitestOutcome,
{this.multitestKey, this.originTestPath}) {
assert(filePath.isAbsolute);
if (originTestPath == null) originTestPath = filePath;
@@ -705,6 +701,9 @@
});
}
}).then((_) {
+ testExpectations = null;
+ cachedTests = null;
+ doTest = null;
if (onDone != null) onDone();
});
}
@@ -1025,7 +1024,6 @@
{bool isNegativeIfChecked: false,
bool hasCompileErrorIfChecked: false,
bool hasStaticWarning: false,
- Set<String> multitestOutcome: null,
String multitestKey,
Path originTestPath}) {
// Cache the test information for each test case.
@@ -1036,7 +1034,6 @@
isNegativeIfChecked,
hasCompileErrorIfChecked,
hasStaticWarning,
- multitestOutcome,
multitestKey: multitestKey,
originTestPath: originTestPath);
cachedTests.add(info);
diff --git a/utils/apidoc/docgen.gyp b/utils/apidoc/docgen.gyp
index 49af512..a6a9142 100644
--- a/utils/apidoc/docgen.gyp
+++ b/utils/apidoc/docgen.gyp
@@ -78,14 +78,13 @@
'--package-root=<(PRODUCT_DIR)/packages',
'../../pkg/docgen/bin/docgen.dart',
'--out=<(PRODUCT_DIR)/api_docs/docgen',
- '--json',
'--include-sdk',
'--no-include-dependent-packages',
'--package-root=<(PRODUCT_DIR)/packages',
'--exclude-lib=async_helper',
'--exclude-lib=expect',
'--exclude-lib=docgen',
- '../../pkg',
+ '../../pkg'
],
'message': 'Running docgen: <(_action)',
},
diff --git a/utils/compiler/compiler.gyp b/utils/compiler/compiler.gyp
index fc4b1cf..9be9644 100644
--- a/utils/compiler/compiler.gyp
+++ b/utils/compiler/compiler.gyp
@@ -13,6 +13,7 @@
'dependencies': [
'../../runtime/dart-runtime.gyp:dart',
'../../pkg/pkg.gyp:pkg_packages',
+ 'dart2js_files_stamp',
],
'actions': [
{
@@ -20,8 +21,9 @@
'inputs': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
'../../sdk/lib/_internal/libraries.dart',
- '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/compiler", "../../runtime/lib", "../../sdk/lib/_internal/dartdoc"])',
+ '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../runtime/lib", "../../sdk/lib/_internal/dartdoc"])',
'create_snapshot.dart',
+ '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
'<(SHARED_INTERMEDIATE_DIR)/packages.stamp',
'../../tools/VERSION',
],
@@ -40,5 +42,30 @@
},
],
},
+ # Other targets depend on dart2js files, but have to many inputs,
+ # which causes issues on some platforms.
+ # This target lists all the files in sdk/lib/_internal/compiler,
+ # and creates a single dart2js_files.stamp
+ {
+ 'target_name': 'dart2js_files_stamp',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'make_dart2js_files_stamp',
+ 'inputs': [
+ '../../tools/create_timestamp_file.py',
+ '<!@(["python", "../../tools/list_files.py", "\\.dart$",'
+ ' "../../sdk/lib/_internal/compiler"])',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
+ ],
+ 'action': [
+ 'python', '../../tools/create_timestamp_file.py',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }
],
}
diff --git a/utils/pub/pub.gyp b/utils/pub/pub.gyp
index fd5e147..9bc9ad2 100644
--- a/utils/pub/pub.gyp
+++ b/utils/pub/pub.gyp
@@ -10,16 +10,19 @@
'dependencies': [
'../../runtime/dart-runtime.gyp:dart',
'../../pkg/pkg.gyp:pkg_packages',
+ '../../pkg/pkg_files.gyp:pkg_files_stamp',
+ '../../utils/compiler/compiler.gyp:dart2js_files_stamp',
+ 'pub_files_stamp'
],
'actions': [
{
'action_name': 'generate_pub_snapshot',
'inputs': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
- '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/pub"])',
'../../sdk/lib/_internal/libraries.dart',
- '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/compiler"])',
- '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../pkg"])',
+ '<(SHARED_INTERMEDIATE_DIR)/pub_files.stamp',
+ '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
+ '<(SHARED_INTERMEDIATE_DIR)/pkg_files.stamp',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/pub.dart.snapshot',
@@ -33,5 +36,32 @@
},
],
},
+ # Other targets depend on pub files, but have to many inputs, which causes
+ # issues on some platforms.
+ # This target lists all the files in sdk/lib/_internal/pub,
+ # and creates a single pub_files.stamp
+ {
+ 'target_name': 'pub_files_stamp',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'make_pub_files_stamp',
+ 'inputs': [
+ '../../tools/create_timestamp_file.py',
+ '<!@(["python", "../../tools/list_files.py", "\\.dart$",'
+ ' "../../sdk/lib/_internal/pub"])',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/pub_files.stamp',
+ ],
+ 'action': [
+ 'python', '../../tools/create_timestamp_file.py',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }
+
+
],
}